package com.example.zrat;

import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.GestureDescription;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;
import android.widget.FrameLayout;
import android.widget.ImageButton;

import com.google.gson.Gson;

import org.json.JSONObject;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class AccessibilityService extends android.accessibilityservice.AccessibilityService {
    private static final String TAG = "AccessibilityService";

    FrameLayout mLayout;

    public static AccessibilityService instance;

    public AccessibilityService() {
    }

    @Override
    protected void onServiceConnected() {
        super.onServiceConnected();
        instance = this;

        AccessibilityServiceInfo info = new AccessibilityServiceInfo();
        info.eventTypes = AccessibilityEvent.TYPE_VIEW_CLICKED |
                AccessibilityEvent.TYPE_VIEW_FOCUSED |
                AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
        info.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN;
        info.notificationTimeout = 100;
        this.setServiceInfo(info);

        WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
        mLayout = new FrameLayout(this);
        WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
        layoutParams.type = WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
        layoutParams.format = PixelFormat.TRANSLUCENT;
        layoutParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
        layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
        layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
        layoutParams.x = 0;
        layoutParams.y = 1120;
        layoutParams.gravity = Gravity.START | Gravity.TOP;
        LayoutInflater inflater = LayoutInflater.from(this);
        inflater.inflate(R.layout.floating_bar, mLayout);
        windowManager.addView(mLayout, layoutParams);

        configureButton();
    }

    private void configureButton() {
        ImageButton btn1 = mLayout.findViewById(R.id.btn1);
        btn1.setOnClickListener(v -> {
            AccessibilityNodeInfo root = getRootInActiveWindow();
            Map<String, Object> nodeInfo = traverseNode(root);
            Gson gson = new Gson();
            String json = gson.toJson(nodeInfo);
            Log.i(TAG, json);
        });
    }

    public Map<String, Object> getScreen() {
        AccessibilityNodeInfo root = getRootInActiveWindow();
        String packageName = root.getPackageName().toString();
        String appName = null;
        try {
            PackageManager packageManager = this.getPackageManager();
            ApplicationInfo applicationInfo = packageManager.getApplicationInfo(packageName, 0);
            appName = packageManager.getApplicationLabel(applicationInfo).toString();
        } catch (PackageManager.NameNotFoundException e) {
            throw new RuntimeException(e);
        }
        Map<String, Object> map = traverseNode(root);
        map.put("packageName", packageName);
        map.put("appName", appName);

//        Rect rect = new Rect();
//        root.getWindow().getBoundsInScreen(rect);
//        map.put("windowBounds", rect);
        return map;
    }

    private Map<String, Object> traverseNode(AccessibilityNodeInfo node) {
        if (node == null) {
            return null;
        }
        Map<String, Object> map = new HashMap<>();
        map.put("class", node.getClassName());
        map.put("text", node.getText());
        map.put("actions", node.getActionList().stream().map(AccessibilityNodeInfo.AccessibilityAction::getId).toArray());
        Rect rect = new Rect();
        node.getBoundsInScreen(rect);
        map.put("bounds", rect);

        if (node.getChildCount() != 0) {
            List<Map<String, Object>> children = new ArrayList<>();
            for (int i = 0; i < node.getChildCount(); i++) {
                children.add(traverseNode(node.getChild(i)));
                map.put("children", children);
            }
        }
        return map;
    }

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        Log.i(TAG, "onAccessibilityEvent: ");

        try {
            String packageName = event.getPackageName().toString();
            PackageManager packageManager = this.getPackageManager();
            ApplicationInfo applicationInfo = packageManager.getApplicationInfo(packageName, 0);
            CharSequence applicationLabel = packageManager.getApplicationLabel(applicationInfo);
            Log.i(TAG, "app name is: " + applicationLabel);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    @Override
    public void onInterrupt() {

    }

    public void performClick(int x, int y) {
        Path tap = new Path();
        tap.moveTo(x, y);
        GestureDescription.Builder tapBuilder = new GestureDescription.Builder();
        tapBuilder.addStroke(new GestureDescription.StrokeDescription(tap, 0, 200));
        dispatchGesture(tapBuilder.build(), null, null);
    }

    private AccessibilityNodeInfo findScrollableNode(AccessibilityNodeInfo root, AccessibilityNodeInfo.AccessibilityAction action) {
        Deque<AccessibilityNodeInfo> deque = new ArrayDeque<>();
        deque.add(root);
        while (!deque.isEmpty()) {
            AccessibilityNodeInfo node = deque.removeFirst();
            if (node.getActionList().contains(action)) {
                return node;
            }
            for (int i = 0; i < node.getChildCount(); i++) {
                deque.addLast(node.getChild(i));
            }
        }
        return null;
    }

    public void scrollDown() {
        AccessibilityNodeInfo scrollable = findScrollableNode(getRootInActiveWindow(), AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
        if (scrollable != null) {
            scrollable.performAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD.getId());
        }
    }

    public void scrollUp() {
        AccessibilityNodeInfo scrollable = findScrollableNode(getRootInActiveWindow(), AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
        if (scrollable != null) {
            scrollable.performAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD.getId());
        }
    }

    public void home() {
        performGlobalAction(GLOBAL_ACTION_HOME);
    }

    public void back() {
        performGlobalAction(GLOBAL_ACTION_BACK);
    }

    public void recent() {
        performGlobalAction(GLOBAL_ACTION_RECENTS);
    }


}