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

import net.northking.cctp.upperComputer.constants.ResponseCmd;
import net.northking.cctp.upperComputer.deviceManager.AndroidDeviceManager;
import net.northking.cctp.upperComputer.driver.adb.AdbDevice;
import net.northking.cctp.upperComputer.driver.agent.AndroidAgentSession;
import net.northking.cctp.upperComputer.driver.agent.command.ScreenStreamCommand;
import net.northking.cctp.upperComputer.driver.agent.command.WatchScreenStreamCommand;
import net.northking.cctp.upperComputer.utils.SessionUtils;
import net.northking.cctp.upperComputer.webSocket.entity.CatchParam;
import net.northking.cctp.upperComputer.webSocket.entity.CmdRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.websocket.Session;
import java.io.DataInput;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class AndroidScreenResponseThread extends Thread {

    private AndroidAgentSession asyncAgentSession;

    private Session wsSession;

    private AdbDevice adbDevice;

    private CmdRequest screenOnRequest;

    private CatchParam catchParam;

    private byte[] lastData;

    private boolean sendDeviceStatus = true;

    private final Logger logger = LoggerFactory.getLogger(AndroidScreenResponseThread.class);

    public AndroidScreenResponseThread(Session wsSession, AdbDevice adbDevice, CatchParam catchParam) {
        this.wsSession = wsSession;
        this.adbDevice = adbDevice;
        this.catchParam = catchParam;
        setName("[" + adbDevice.getSerial() + "-response]");
    }

    public void setScreenOnRequest(CmdRequest screenOnRequest) {
        this.screenOnRequest = screenOnRequest;
    }

    @Override
    public void run() {
        while (!isInterrupted()) {
            //通知前端开始连接
            Map<String, Object> messageMap = new HashMap<>();
            messageMap.put(ResponseCmd.DeviceStatus.STATUS, ResponseCmd.DeviceStatus.CONNECTING);
            SessionUtils.sendMessageInitiative(wsSession, ResponseCmd.DEVICE_STATUS, adbDevice.getSerial(), messageMap, "设备掉线了");
            AdbDevice currentDevice = AndroidDeviceManager.getInstance().getCurrentDevice(adbDevice.getSerial());
            if (null == currentDevice) {
                logger.warn("设备【{}】掉线了，等待设备准备好再打开图片流........", adbDevice.getSerial());
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    break;
                }
                continue;
            }
            logger.info("开启设备【{}】的屏幕监听。。。。。。。。。。", adbDevice.getSerial());
            this.asyncAgentSession = new AndroidAgentSession(adbDevice, true);
            try {
                this.asyncAgentSession.start();
                this.asyncAgentSession.send(WatchScreenStreamCommand.getInstance());
                logger.debug("开启屏幕=====>width：{} height:{} quality:{} frameRate:{},rotation:{}", catchParam.getWidth(), catchParam.getHeight(), catchParam.getQuality(), catchParam.getFrameRate(), catchParam.getRotation());
                ScreenStreamCommand screenStreamCommand = new ScreenStreamCommand(true, catchParam.getRotation(), catchParam.getWidth(), catchParam.getHeight(), catchParam.getFrameRate(), catchParam.getQuality());
                this.asyncAgentSession.send(screenStreamCommand);
            } catch (IOException e) {
                logger.error("创建设备【{}】agentSession出错。。。。。。", adbDevice.getSerial(), e);
                Thread.currentThread().interrupt();
                Map<String, Object> result = new HashMap<>();
                result.put(ResponseCmd.DeviceStatus.STATUS, ResponseCmd.DeviceStatus.DISCONNECT);
                SessionUtils.sendMessageInitiative(wsSession, ResponseCmd.DEVICE_STATUS, adbDevice.getSerial(), result, "设备连接失败");
                this.asyncAgentSession.closeSilence();
                break;
            }
            sendConnectInfo2Web();
            DataInput dataInput = this.asyncAgentSession.getDataInput();
            while (!isInterrupted()) {
                try {
                    int commandId = dataInput.readInt();
//                    logger.debug("屏幕读取线程commandId:{}",commandId);
                    if (commandId == WatchScreenStreamCommand.getInstance().getCommandId()) {//屏幕流
                        byte[] screenPicContent = new byte[0];
                        try {
                            screenPicContent = WatchScreenStreamCommand.getInstance().buildReceive(dataInput);
                            //将获取的最新帧进行缓存
                            if (null != screenPicContent && screenPicContent.length > 0) {
                                this.lastData = screenPicContent;
                            } else {
                                logger.warn("设备【{}】command读取最新帧为空!",adbDevice.getSerial());
                                continue;
                            }
                        } catch (IOException e) {
                            logger.error("读取数据出错了", e);
                            break;
                        }
                        //有后端会话则给后端推送屏幕图片
                        if (wsSession != null && wsSession.isOpen()) {
                            SessionUtils.sendBinary(wsSession, screenPicContent);
                            if (sendDeviceStatus) {
                                Map<String, Object> result = new HashMap<>();
                                result.put(ResponseCmd.DeviceStatus.STATUS, ResponseCmd.DeviceStatus.CONNECTED);
                                SessionUtils.sendMessageInitiative(wsSession, ResponseCmd.DEVICE_STATUS, adbDevice.getSerial(), result, "设备连接失败");
                                sendDeviceStatus = false;
                            }
                        }
                    }
                } catch (IOException e) {
                    logger.warn("读取设备【{}】agentSession输出出错", adbDevice.getSerial(), e);
                    break;
                }
            }
            this.asyncAgentSession.closeSilence();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                break;
            }
            logger.warn("重新建立设备【{}】图片传输的连接...........................", adbDevice.getSerial());
        }
        logger.warn("设备【{}】响应到上位机的屏幕线程退出..........................", adbDevice.getSerial());
    }

    public void sendConnectInfo2Web() {
        //新连接需要先发送状态
        if (null != screenOnRequest) {  //只发第一次screen_on指令
            //向前端推送之前缓存的最新帧
            if (null != adbDevice) {
                if (null != lastData && lastData.length > 0) {
                    SessionUtils.sendBinary(wsSession, lastData);
                    logger.debug("上位机发送缓存帧成功，大小：{}", lastData.length);
                    Map<String, Object> result = new HashMap<>();
                    result.put(ResponseCmd.DeviceStatus.STATUS, ResponseCmd.DeviceStatus.CONNECTED);
                    SessionUtils.sendMessageInitiative(wsSession, ResponseCmd.DEVICE_STATUS, adbDevice.getSerial(), result, "设备连接失败");
                    sendDeviceStatus = false;
                } else {
                    logger.warn("设备【{}】缓存帧获取为空...", adbDevice.getSerial());
                }
            }
            //向前端推送之前缓存的最新帧后发screenOn回复
            SessionUtils.sendSuccessData(wsSession, screenOnRequest, null, "开启屏幕成功");
            setScreenOnRequest(null);
        }
    }

    public void stopAndExit() {
        logger.warn("设备【{}】退出屏幕获取线程...", adbDevice.getSerial());
        asyncAgentSession.closeSilence();
        this.lastData = null;
        AndroidDeviceManager.getInstance().removeScreenThread(adbDevice.getSerial());
        interrupt();
    }

    /**
     * 更新关联的后端会话和屏幕尺寸
     *
     * @param session
     * @param catchParam
     */
    public void startSendScreenToWeb(Session session, CatchParam catchParam) {
        this.wsSession = session;
        this.catchParam = catchParam;
//        this.isNewConnect = true;
        sendConnectInfo2Web();
    }

    /**
     * 断开后端会话连接，保留接收最新帧数据
     */
    public void stopSendPic2Web() {
        this.wsSession = null;
//        logger.debug("后端会话关联已置空！屏幕获取线程是否被中断："+isInterrupted());
    }

    public void setSendDeviceStatus(boolean sendDeviceStatus) {
        this.sendDeviceStatus = sendDeviceStatus;
    }
}
