/*
 * Copyright (c) 2020. BoostTag E.I.R.L. Romell D.Z.
 * All rights reserved
 * porfile.romellfudi.com
 */

package com.romellfudi.ussdlibrary;

import android.accessibilityservice.AccessibilityService;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Parcel;
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;

import androidx.annotation.RequiresApi;

import com.elvishew.xlog.XLog;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;

/**
 * AccessibilityService for ussd windows on Android mobile Telcom
 *
 * @author Romell Dominguez
 * @version 1.1.c 27/09/2018
 * @since 1.0.a
 */
public class USSDService extends AccessibilityService {

    private static final String TAG = "USSDServiceUSSD";

    public static AccessibilityEvent event;

    @RequiresApi(api = Build.VERSION_CODES.P)
    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            USSDService.event = new AccessibilityEvent(event);
        } else {
            Parcel parcel = Parcel.obtain();
            event.writeToParcel(parcel, 0);
            parcel.setDataPosition(0);
            AccessibilityEvent newEvent = AccessibilityEvent.CREATOR.createFromParcel(parcel);
            parcel.recycle();
            USSDService.event = newEvent;
        }
        XLog.i(String.format("USSDService onAccessibilityEvent: [type] %s [class] %s [package] %s [time] %s [text] %s [ContentChangeTypes] %s [WindowChanges] %s",
                event.getEventType(),
                event.getClassName(),
                event.getPackageName(),
                event.getEventTime(),
                event.getText(),
                event.getContentChangeTypes(),
                event.getWindowChanges()
        ));
        new Handler(Looper.getMainLooper()).postDelayed(() -> {
            Log.d("hzshkj", String.format("USSDService onAccessibilityEvent: [type] %s [class] %s [package] %s [time] %s [text] %s [ContentChangeTypes] %s [WindowChanges] %s",
                    event.getEventType(),
                    event.getClassName(),
                    event.getPackageName(),
                    event.getEventTime(),
                    event.getText(),
                    event.getContentChangeTypes(),
                    event.getWindowChanges()
            ));
            XLog.i(String.format("USSDService onAccessibilityEvent:2秒后 [type] %s [class] %s [package] %s [time] %s [text] %s [ContentChangeTypes] %s [WindowChanges] %s",
                    event.getEventType(),
                    event.getClassName(),
                    event.getPackageName(),
                    event.getEventTime(),
                    event.getText(),
                    event.getContentChangeTypes(),
                    event.getWindowChanges()
            ));

            if (USSDController.instance == null || !USSDController.instance.isRunning) {
                XLog.e("USSDService Error : USSDController == " + (USSDController.instance == null));
                XLog.e("USSDService Error : USSDController.isRunning == " + (USSDController.instance.isRunning));
//            return;
            }

            String response = event.getText().toString();
            XLog.i("USSDService response = " + response);
            if (response.toLowerCase(Locale.US).contains("ussd code running")) {
                XLog.e("中断执行,监测到是 ---> " + response);
                return;
            }
            if (LoginView(event) && notInputText(event)) {
                XLog.i("USSDService: 1");
                clickOnButton(event, 0);
                USSDController.instance.isRunning = false;
                if (USSDController.instance.send)
                    USSDController.instance.callbackMessage.over(response);
                else
                    USSDController.instance.callbackInvoke.over(response);
            } else if (problemView(event) || LoginView(event)) {
                XLog.i("USSDService: 2");
                clickOnButton(event, 1);
                if (USSDController.instance.send)
                    USSDController.instance.callbackMessage.over(response);
                else
                    USSDController.instance.callbackInvoke.over(response);

            } else if (isUSSDWidget(event)) {
                XLog.i("USSDService: 3");
                if (notInputText(event)) {
                    XLog.i("USSDService: 4");
//                    clickOnButton(event, 0);
                    USSDController.instance.isRunning = false;
                    if (USSDController.instance.send)
                        USSDController.instance.callbackMessage.over(response);
                    else
                        USSDController.instance.callbackInvoke.over(response);
                } else {
                    XLog.i("USSDService: 5");
                    if (USSDController.instance.send)
                        USSDController.instance.callbackMessage.responseMessage(response);
                    else
                        USSDController.instance.callbackInvoke.responseInvoke(response);
                }
            } else {
                XLog.e("USSDService Error : 未走进任何逻辑循环" + event.getText().toString());
            }
        }, 2000); // 延迟 1 秒

    }

    /**
     * Send whatever you want via USSD
     *
     * @param text any string
     */
    public static void send(String text) {
        setTextIntoField(event, text);
        clickOnButton(event, 1);
    }

    /**
     * Cancel USSD
     */
    public static void cancel() {
        clickOnButton(event, 0);
    }


    /**
     * set text into input text at USSD widget
     *
     * @param event AccessibilityEvent
     * @param data  Any String
     */
    private static void setTextIntoField(AccessibilityEvent event, String data) {
        USSDController ussdController = USSDController.instance;
        Bundle arguments = new Bundle();
        arguments.putCharSequence(
                AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, data);

        for (AccessibilityNodeInfo leaf : getLeaves(event)) {

            if (leaf.getClassName().equals("android.widget.EditText")
                    && !leaf.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments)) {
                ClipboardManager clipboardManager = ((ClipboardManager) ussdController.context
                        .getSystemService(Context.CLIPBOARD_SERVICE));
                if (clipboardManager != null) {
                    clipboardManager.setPrimaryClip(ClipData.newPlainText("text", data));
                }

                leaf.performAction(AccessibilityNodeInfo.ACTION_PASTE);
            }
        }
    }

    /**
     * Method evaluate if USSD widget has input text
     *
     * @param event AccessibilityEvent
     * @return boolean has or not input text
     */
    protected static boolean notInputText(AccessibilityEvent event) {
        boolean flag = true;
        XLog.i("USSDService Event Json event.source = : " + event.getSource());
        for (AccessibilityNodeInfo leaf : getLeaves(event)) {
            if (leaf.getClassName().equals("android.widget.EditText")) {
                flag = false;
            }
        }
        return flag;
    }

    /**
     * The AccessibilityEvent is instance of USSD Widget class
     *
     * @param event AccessibilityEvent
     * @return boolean AccessibilityEvent is USSD
     */
    private boolean isUSSDWidget(AccessibilityEvent event) {
        if (event == null || event.getClassName() == null) {
            return false;
        }
        String className = event.getClassName().toString();
        return (className.equals("amigo.app.AmigoAlertDialog")
                || className.equals("android.app.AlertDialog")
                || className.equals("com.android.phone.oppo.settings.LocalAlertDialog")
                || className.equals("com.zte.mifavor.widget.AlertDialog")
                || className.equals("color.support.v7.app.AlertDialog")
                || className.equals("com.transsion.widgetslib.dialog.PromptDialog")
                || className.equals("miuix.appcompat.app.AlertDialog")
                || className.equals("androidx.appcompat.app.AlertDialog"));
    }


    /**
     * The View has a login message into USSD Widget
     *
     * @param event AccessibilityEvent
     * @return boolean USSD Widget has login message
     */
    private boolean LoginView(AccessibilityEvent event) {
        HashSet<String> hashSet = new HashSet<>(Arrays.asList("espere", "waiting", "loading", "esperando"));
        return isUSSDWidget(event) && hashSet.contains(event.getText().get(0).toString());
    }

    /**
     * The View has a problem message into USSD Widget
     *
     * @param event AccessibilityEvent
     * @return boolean USSD Widget has problem message
     */
    protected boolean problemView(AccessibilityEvent event) {
        HashSet<String> hashSet = new HashSet<>(Arrays.asList("problema", "problem", "error", "null"));
        return isUSSDWidget(event) && hashSet.contains(event.getText().get(0).toString());
    }

    /**
     * click a button using the index
     *
     * @param event AccessibilityEvent
     * @param index button's index
     */
    protected static void clickOnButton(AccessibilityEvent event, int index) {
        int count = -1;
        for (AccessibilityNodeInfo leaf : getLeaves(event)) {
            if (leaf.getClassName().toString().toLowerCase().contains("button")) {
                count++;
                if (count == index) {
                    leaf.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                }
            }
        }
    }

    private static List<AccessibilityNodeInfo> getLeaves(AccessibilityEvent event) {
        List<AccessibilityNodeInfo> leaves = new ArrayList<>();
        try {
            if (event.getSource() != null) {
                getLeaves(leaves, event.getSource());
            }
        } catch (Exception e) {
            XLog.e("UssdService-", e);
        }
        return leaves;
    }

    private static void getLeaves(List<AccessibilityNodeInfo> leaves, AccessibilityNodeInfo node) {
        try {
            if (node.getChildCount() == 0) {
                leaves.add(node);
                return;
            }

            for (int i = 0; i < node.getChildCount(); i++) {
                getLeaves(leaves, node.getChild(i));
            }
        } catch (Exception e) {
            XLog.e("UssdService-", e);
        }

    }

    /**
     * Active when SO interrupt the application
     */
    @Override
    public void onInterrupt() {
        Log.d(TAG, "onInterrupt");
    }

    /**
     * Configure accessibility server from Android Operative System
     */
    @Override
    protected void onServiceConnected() {
        super.onServiceConnected();
        Log.d(TAG, "onServiceConnected");
    }

}
