package net.northking.cctp.upperComputer.deviceManager.thread;


import net.northking.cctp.upperComputer.deviceManager.HarmonyDeviceManager;
import net.northking.cctp.upperComputer.deviceManager.thread.util.ThreadJob;
import net.northking.cctp.upperComputer.deviceManager.thread.util.ThreadScope;
import net.northking.cctp.upperComputer.driver.harmony.HarmonyDevice;
import net.northking.cctp.upperComputer.driver.harmony.hdc.HDCConnectStatus;
import net.northking.cctp.upperComputer.driver.harmony.hyppium.HyppiumAgent;
import net.northking.cctp.upperComputer.driver.harmony.hyppium.data.DisplaySize;
import net.northking.cctp.upperComputer.driver.harmony.ui.UiComponent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;

/**
 * 鸿蒙设备调用提供器
 * <br>
 * 如果使用此对象进行设备操作，则所有操作均通过此对象进行（对象有提供的），否则会发生状态管理冲突。
 * <br>
 * 当设备发生不可用时，对外隐藏恢复过程。
 */

public class HarmonyProvider implements Closeable,InitDevice {

    private static final Logger log = LoggerFactory.getLogger(HarmonyProvider.class);

    private static final ThreadScope GLOBAL_PROVIDER_THREAD_SCOPE = new ThreadScope("HarmonyProvider");

    /**
     * 失败重试等待时间。单位：毫秒
     */
    private static final long RETRY_DELAY = 1000L;


    private static final long REQUEST_TIMEOUT = 2000L;

    /**
     * 鸿蒙设备
     */
    private final HarmonyDevice harmonyDevice;

    /**
     * Agent操作对象
     */
    private final HyppiumAgent agent;

    /**
     * 当前Agent通信会话
     */
    private HyppiumAgent.Session session;

    private final ThreadScope requestScope;

    private final ArrayList<ThreadJob> requestJobList = new ArrayList<>();

    private StatusListener statusListener = null;

    /**
     * 当前屏幕图片流数据捕捉回调
     * <br>
     * 不为null时，标志着当前处于需要开启屏幕流的状态，当设备从不可用恢复时，需要自动重新开始屏幕流
     */
    private HyppiumAgent.OnCaptureData screenCaptureDataCallback = null;

    /**
     * 屏幕图片流缩放率
     */
    private float screenCaptureScale = 1.0f;

    /**
     * 当前UI事件数据捕捉回调
     * <br>
     * 不为null时，标志着当前处于需要开启UI事件捕捉的状态，当设备从不可用恢复时，需要自动重新开始UI事件数据捕捉
     */
    private HyppiumAgent.OnCaptureData uiActionCaptureDataCallback = null;

    private LinkedHashMap<String, Object> deviceInfo;

    /**
     * @param harmonyDevice 鸿蒙设备，应对次对象进行及时状态更新
     */
    public HarmonyProvider(HarmonyDevice harmonyDevice) throws IOException {
        this.harmonyDevice = harmonyDevice;
        this.requestScope = GLOBAL_PROVIDER_THREAD_SCOPE.createChildScope("hm-" + harmonyDevice.getHdcDevice().getConnectKey());
        try {
            agent = new HyppiumAgent(harmonyDevice.getHdcDevice()); //初始化一次Agent
        } catch (IOException e) {
            log.error("初始化设备{}的HyppiumAgent失败", harmonyDevice.getHdcDevice().getConnectKey(), e);
            throw e;
        }
        if (!createSession()) {
            GLOBAL_PROVIDER_THREAD_SCOPE.launch((job) -> {
                init();
            }, RETRY_DELAY);
        }
        deviceInfo = getDeviceInfo();
        HarmonyDeviceManager.getInstance().registerDeviceToDeviceManager(deviceInfo);  //注册设备到设备管理
    }

    private void init() {
        if (!reinitAgent()) {
            GLOBAL_PROVIDER_THREAD_SCOPE.launch((job) -> {
                init();
            }, RETRY_DELAY);
            return;
        }
        if (!createSession()) {
            GLOBAL_PROVIDER_THREAD_SCOPE.launch((job) -> {
                init();
            }, RETRY_DELAY);
            return;
        }
        if (screenCaptureDataCallback != null) {
            startCaptureScreenImageStream(screenCaptureScale, screenCaptureDataCallback);
        }
    }

    private boolean reinitAgent() {
        if (harmonyDevice.getHdcDevice().getConnectStatus() != HDCConnectStatus.CONNECTED) {
            return false;
        }
        try {
            agent.initAgent();
        } catch (IOException e) {
            log.error("重新初始化设备{}的HyppiumAgent失败", harmonyDevice.getHdcDevice().getConnectKey(), e);
            return false;
        }
        return true;
    }

    private boolean createSession() {
        if (harmonyDevice.getHdcDevice().getConnectStatus() != HDCConnectStatus.CONNECTED) {
            return false;
        }
        if (session != null) {
            session.close();
            session = null;
        }
        session = agent.createSession();
        session.setOnCloseListener(this::onSessionClosed);
        return session != null;
    }

    private void onSessionClosed() {
        if (statusListener != null) {
            try {
                statusListener.onStatusChange(harmonyDevice, Status.UNAVAILABLE);
            } catch (Exception e) {
                log.error("执行鸿蒙设备{}状态回调时发生异常", harmonyDevice.getHdcDevice().getConnectKey(), e);
            }
        }

        GLOBAL_PROVIDER_THREAD_SCOPE.launch((job) -> {
            init();
        });
    }

    @Override
    public void close() {
        requestScope.depose();
    }

    /**
     * 设置状态监听器
     * <br>
     * 用于知道当前是否可用
     *
     * @param statusListener 状态监听器
     */
    public void setStatusListener(StatusListener statusListener) {
        this.statusListener = statusListener;
    }

    /**
     * 按下一次返回键
     *
     * @return 是否操作成功
     */
    public boolean pressBack() {
        return scopeRequest(() -> session.pressBack(REQUEST_TIMEOUT), Boolean.class);
    }

    /**
     * 按下一次主屏键
     *
     * @return 是否操作成功
     */
    public boolean pressHome() {
        return scopeRequest(() -> session.pressHome(REQUEST_TIMEOUT), Boolean.class);
    }

    /**
     * 按下一次最近按钮（当前实现为滑屏模拟）
     *
     * @return 是否操作成功
     */
    public boolean pressRecentApp() {
        return scopeRequest(() -> session.pressRecentApp(REQUEST_TIMEOUT), Boolean.class);
    }

    /**
     * 按下一次电源键
     *
     * @return 是否操作成功
     */
    public boolean pressPowerKey() {
        return scopeRequest(() -> session.pressPowerKey(REQUEST_TIMEOUT), Boolean.class);
    }

    /**
     * 按下一次音量上键
     *
     * @return 是否操作成功
     */
    public boolean pressUpVolume() {
        return scopeRequest(() -> session.pressUpVolume(REQUEST_TIMEOUT), Boolean.class);
    }

    /**
     * 按下一次音量下键
     *
     * @return 是否操作成功
     */
    public boolean pressDownVolume() {
        return scopeRequest(() -> session.pressDownVolume(REQUEST_TIMEOUT), Boolean.class);
    }

    /**
     * 执行双指捏屏操作
     *
     * @param scale 缩放系数
     * @return 是否操作成功
     */
    public boolean pinch(float scale) {
        return scopeRequest(() -> session.pinch(scale, REQUEST_TIMEOUT), Boolean.class);
    }

    /**
     * 执行触摸单指按下操作
     *
     * @param x 坐标
     * @param y 坐标
     * @return 是否操作成功
     */
    public boolean touchDown(int x, int y) {
        return scopeRequest(() -> session.touchDown(x, y, REQUEST_TIMEOUT), Boolean.class);
    }

    /**
     * 执行触摸单指移动操作
     *
     * @param x 坐标
     * @param y 坐标
     * @return 是否操作成功
     */
    public boolean touchMove(int x, int y) {
        return scopeRequest(() -> session.touchMove(x, y, REQUEST_TIMEOUT), Boolean.class);
    }

    /**
     * 执行触摸单指抬起操作
     *
     * @param x 坐标
     * @param y 坐标
     * @return 是否操作成功
     */
    public boolean touchUp(int x, int y) {
        return scopeRequest(() -> session.touchUp(x, y, REQUEST_TIMEOUT), Boolean.class);
    }

    /**
     * 开始捕获屏幕图片流
     *
     * @param scale         缩放比例
     * @param onCaptureData 屏幕流图片数据回调
     * @return 是否开启成功
     */
    public synchronized boolean startCaptureScreenImageStream(float scale, HyppiumAgent.OnCaptureData onCaptureData) {
        if (onCaptureData == null) throw new IllegalArgumentException("onCaptureData不能为空");
        screenCaptureDataCallback = onCaptureData;
        screenCaptureScale = scale;
        return scopeRequest(() -> {
            if (screenCaptureDataCallback == null) {
                return false;
            }
            try {
                return session.startCaptureScreenImageStream(scale, onCaptureData, REQUEST_TIMEOUT);
            } catch (HyppiumAgent.CaptureAlreadyRunningException e) {
                log.error("鸿蒙设备{}报告已经开启了屏幕图片流", harmonyDevice.getHdcDevice().getConnectKey());
                if (screenCaptureDataCallback == null) return false;
                if (session.stopCaptureScreenImageStream(screenCaptureDataCallback, REQUEST_TIMEOUT)) {
                    if (screenCaptureDataCallback == null) return false;
                    try {
                        return session.startCaptureScreenImageStream(scale, onCaptureData, REQUEST_TIMEOUT);
                    } catch (Exception ignored) {
                        log.error("鸿蒙设备{}报告已经开启了屏幕图片流且尝试自动启动", harmonyDevice.getHdcDevice().getConnectKey());
                    }
                } else {
                    log.error("鸿蒙设备{}报告已经开启了屏幕图片流，尝试自动停止时失败", harmonyDevice.getHdcDevice().getConnectKey());
                }
                return false;
            }
        }, Boolean.class);
    }

    /**
     * 停止捕获屏幕图片流
     *
     * @return 是否操作成功
     */
    public boolean stopCaptureScreenImageStream() {
        if (screenCaptureDataCallback == null) return false;
        boolean result = scopeRequest(() -> session.stopCaptureScreenImageStream(screenCaptureDataCallback, REQUEST_TIMEOUT), Boolean.class);
        if (result) {
            screenCaptureDataCallback = null;
        }
        return result;
    }

    /**
     * 开启捕捉UI动作
     * <br>
     * 会得到动作事件数据，以及一些事件中的UI层级树
     *
     * @param onCaptureData UI动作事件数据回调
     * @return 是否操作成功
     */
    public synchronized boolean startCaptureUiAction(HyppiumAgent.OnCaptureData onCaptureData) {
        if (onCaptureData == null) throw new IllegalArgumentException("onCaptureData不能为空");
        uiActionCaptureDataCallback = onCaptureData;
        return scopeRequest(() -> {
            if (uiActionCaptureDataCallback == null) {
                return false;
            }
            try {
                return session.startCaptureUiAction(onCaptureData, REQUEST_TIMEOUT);
            } catch (HyppiumAgent.CaptureAlreadyRunningException e) {
                log.error("鸿蒙设备{}报告已经开启了UI事件捕捉", harmonyDevice.getHdcDevice().getConnectKey());
                if (uiActionCaptureDataCallback == null) return false;
                if (session.stopCaptureUiAction(uiActionCaptureDataCallback, REQUEST_TIMEOUT)) {
                    if (uiActionCaptureDataCallback == null) return false;
                    try {
                        return session.startCaptureUiAction(onCaptureData, REQUEST_TIMEOUT);
                    } catch (Exception ignored) {
                        log.error("鸿蒙设备{}报告已经开启了UI事件捕捉", harmonyDevice.getHdcDevice().getConnectKey());
                    }
                } else {
                    log.error("鸿蒙设备{}报告已经开启了UI事件捕捉，尝试自动停止时失败", harmonyDevice.getHdcDevice().getConnectKey());
                }
            }
            return false;
        }, Boolean.class);
    }

    /**
     * 停止捕捉UI动作
     *
     * @return 是否操作成功
     */
    public boolean stopCaptureUiAction() {
        if (uiActionCaptureDataCallback == null) return false;
        boolean result = scopeRequest(() -> session.stopCaptureUiAction(uiActionCaptureDataCallback, REQUEST_TIMEOUT), Boolean.class);
        if (result) {
            uiActionCaptureDataCallback = null;
        }
        return result;
    }

    /**
     * 捕获UI层级树
     *
     * @return 获取到的UI层级树数据，null为获取失败
     */
    public UiComponent captureLayout() {
        return scopeRequest(() -> session.captureLayout(REQUEST_TIMEOUT), UiComponent.class);
    }

    /**
     * 设置屏幕方向
     *
     * @param direction 屏幕方向值，逆时针：0为0°，1为90°，2为180°，3为270°
     * @return 是否操作成功
     */
    public boolean setDisplayRotation(int direction) {
        if (direction < 0 || direction > 3) {
            throw new IllegalArgumentException("屏幕方向参数错误，应该在0 - 3 之间");
        }
        return scopeRequest(() -> session.setDisplayRotation(direction, REQUEST_TIMEOUT), Boolean.class);
    }

    /**
     * 获得屏幕方向
     *
     * @return 屏幕方向值，-1为获取失败，逆时针：0为0°，1为90°，2为180°，3为270°
     */
    public int getDisplayRotation() {
        return scopeRequest(() -> session.getDisplayRotation(REQUEST_TIMEOUT), Integer.class);
    }

    /**
     * 获得屏幕大小
     *
     * @return 屏幕大小，null为获取失败
     */
    public DisplaySize getScreenSize() {
        return scopeRequest(() -> session.getScreenSize(REQUEST_TIMEOUT), DisplaySize.class);
    }

    private <T> T scopeRequest(DoSessionRequestBlock<T> block, Class<T> resultClass) {
        ThreadJob threadJob = requestScope.launch((job) -> {
            T result = block.request();
            while (job.isValid() && (session == null || session.isClosed())) {
                try {
                    Thread.sleep(500L);
                } catch (InterruptedException e) {
                    break;
                }
                if (session != null && !session.isClosed()) {
                    result = block.request();
                }
            }
            job.setResult(result);
        });

        synchronized (requestJobList) {
            requestJobList.add(threadJob);
        }
        try {
            threadJob.join();
        } catch (InterruptedException e) {
            threadJob.setResult(null);
        }
        synchronized (requestJobList) {
            requestJobList.remove(threadJob);
        }
        return threadJob.getResult(resultClass);
    }

    @Override
    public LinkedHashMap<String, Object> getDeviceInfo() throws IOException {
        LinkedHashMap<String, Object> deviceInfo = new LinkedHashMap<>();
        //region 设备号
        deviceInfo.put("serial", harmonyDevice.getHdcDevice().getConnectKey());
        //region 品牌
        deviceInfo.put("manufacturer", "HUAWEI");
        //region 型号 todo:后面得改
        deviceInfo.put("model", "mate60");
        deviceInfo.put("product", "mate60");
        //region 平台
        deviceInfo.put("platform", "harmony");
        int rotation = getDisplayRotation();
        deviceInfo.put("rotation", rotation);
        DisplaySize screenSize = getScreenSize();
        deviceInfo.put("width", screenSize.width);
        deviceInfo.put("height", screenSize.height);
        deviceInfo.put("isHarmony", true);
        deviceInfo.put("scale", 1.0);
        return deviceInfo;
    }

    @Override
    public void exitDeviceInit() {

    }

    interface DoSessionRequestBlock<T> {
        T request();
    }

    /**
     * 放弃当前进行中的请求
     * <br>
     * 请求方法会立即返回结果，实际请求将会等待至请求超时后结束（放弃后下一次可能因此不会马上开始）
     */
    public synchronized void giveUp() {
        synchronized (requestJobList) {
            for (ThreadJob job : requestJobList) {
                job.cancel();
            }
            requestJobList.clear();
        }
    }

    public enum Status {
        AVAILABLE, UNAVAILABLE;
    }

    public interface StatusListener {
        void onStatusChange(HarmonyDevice device, Status status);
    }

}
