/*
 * 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.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 {
    public static AccessibilityEvent event;

    @RequiresApi(api = Build.VERSION_CODES.P)
    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        if (USSDService.event != null) {
            USSDService.event.recycle();
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            try {
                USSDService.event = new AccessibilityEvent(event);
            } catch (Exception e) {
                XLog.e("USSDService Error : 复制Ussd event 失败", e);
            }

        } else {
            Parcel parcel = Parcel.obtain();
            event.writeToParcel(parcel, 0);
            parcel.setDataPosition(0);
            AccessibilityEvent newEvent = AccessibilityEvent.CREATOR.createFromParcel(parcel);
            parcel.recycle();
            USSDService.event = newEvent;
        }

        if (USSDController.instance == null || !USSDController.instance.isRunning) {
            XLog.e("USSDService Error : USSDController.instance 是否为空? -> " + (USSDController.instance == null));
            XLog.e("USSDService Error : USSDController.instance.isRunning? -> " + (USSDController.instance.isRunning));
        }

        String data = String.format("Ussd服务基本参数 -> " +
                        "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(data);
        Log.d("hzshkj", "[USSDService] onAccessibilityEvent: " + data);

        String response = event.getText().toString();
        XLog.i("USSDService response的内容为 -> " + response);
        if (response.toLowerCase(Locale.US).contains("ussd code running")) {
            XLog.e("停止执行,检测到窗口为 -> ussd code running, response内容为: -> " + response);
            return;
        }
        if (LoginView(event) && notInputText(event)) {
            XLog.i("USSDService: LoginView && notInputText");
            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: problemView && LoginView");
            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: isUSSDWidget");
            if (notInputText(event)) {
                XLog.i("USSDService: notInputText");
                USSDController.instance.isRunning = false;
                if (USSDController.instance.send)
                    USSDController.instance.callbackMessage.over(response);
                else
                    USSDController.instance.callbackInvoke.over(response);
            } else {
                XLog.i("USSDService: 不是notInputText");
                if (USSDController.instance.send)
                    USSDController.instance.callbackMessage.responseMessage(response);
                else
                    USSDController.instance.callbackInvoke.responseInvoke(response);
            }
        } else {
            XLog.e("USSDService Error : 未走进任何逻辑循环" + event.getText().toString());
        }

    }

    /**
     * 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("androidx.appcompat.app.e")
                || 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")
                || className.equals("com.mediatek.phone.UssdAlertActivity"));
    }


    /**
     * 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() {
        XLog.i("onInterrupt");
    }

    /**
     * Configure accessibility server from Android Operative System
     */
    @Override
    protected void onServiceConnected() {
        super.onServiceConnected();
        XLog.i("onServiceConnected");
    }

}
