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

import com.alibaba.fastjson.JSONObject;
import net.northking.cctp.upperComputer.constants.ResponseCmd;
import net.northking.cctp.upperComputer.deviceManager.IOSDeviceManager;
import net.northking.cctp.upperComputer.deviceManager.thread.IosDeviceInitThread;
import net.northking.cctp.upperComputer.driver.ios.NKAgent;
import net.northking.cctp.upperComputer.driver.usbmuxd.AppleDevice;
import net.northking.cctp.upperComputer.driver.usbmuxd.UsbMuxd;
import net.northking.cctp.upperComputer.driver.usbmuxd.UsbMuxdConnectFailedException;
import net.northking.cctp.upperComputer.driver.usbmuxd.payload.usbmuxd.entity.DeviceConnectionDetail;
import net.northking.cctp.upperComputer.entity.PhoneEntity;
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.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author : yineng.huang
 * @date : 2023/10/17 16:41
 */
public class IosScreenResponseThread extends Thread {

    private Logger logger = LoggerFactory.getLogger(IosScreenResponseThread.class);

    private static final int WEB_DRIVER_AGENT_SCREEN_PORT = 9100;
    /**
     * 每个数据块的固定开头
     */
    private static final String BOUNDARY_STRING = "--BoundaryString--Content-type: image/jpg--=";

    /**
     * 最后有效图片头部块
     */
    private final byte[] lastChunkHeader = new byte[BOUNDARY_STRING.length() + 2];

    public static float compressQuality = 0.75f;
    /**
     * 输出header，请求方连接后首先发送，每次连接只发一次
     */
    private String header = null;

    private PhoneEntity phone;

    private Session webSession;

    private long num = 1;

    private CmdRequest screenOnRequest;  //只是第一次连接需要，发送给前端代表连接成功

    private UsbMuxd.ConnectOperator connectOperator;

    private IosScreenRecordThread screenRecordThread;

    private String recordingType = "0";  //录屏类型0-未录，1-真机录屏，2-自动化执行任务录屏

    private boolean sendDeviceStatus = true;

//    private IosScreenCompressHandleThread screenCompressHandle;

    @Override
    public void run() {
        logger.info("即将开始拉取设备【{}】图片................................", phone.getUdid());
        while (!isInterrupted()) {
            //通知前端开始连接
            Map<String, Object> messageMap = new HashMap<>();
            messageMap.put(ResponseCmd.DeviceStatus.STATUS, ResponseCmd.DeviceStatus.CONNECTING);
            SessionUtils.sendMessageInitiative(webSession, ResponseCmd.DEVICE_STATUS, phone.getUdid(), messageMap, "设备掉线了");
            UsbMuxd usbmuxd = new UsbMuxd();
            connectOperator = getDeviceConnectOperator(usbmuxd);
            if (connectOperator == null) {
                logger.warn("未能获取与设备[" + phone.getUdid() + "]的通信操作对象，稍后重试");
                try {
                    sleep(500);
                } catch (InterruptedException ignored) {
                    Thread.currentThread().interrupt();
                }
                continue;
            }
            try {
                connectOperator.outputStream.write("\n\n".getBytes(StandardCharsets.UTF_8));
            } catch (IOException e) {
                logger.warn("传递指令到设备[" + phone.getUdid() + "]时发生输入输出错误，稍后重试");
                try {
                    sleep(500);
                } catch (InterruptedException ignored) {
                    Thread.currentThread().interrupt();
                }
                continue;
            }

            byte[] readCache = new byte[4096];
            ByteArrayOutputStream readBuffer = new ByteArrayOutputStream();
            ByteArrayOutputStream chunkBuffer = new ByteArrayOutputStream();
            byte[] chunkHeaderByteArray = BOUNDARY_STRING.getBytes();
            int chunkHeaderPosition = 0;
            int readBufferChunkEndIndex = 0;
            boolean readFirstChunk = false;
            int lastRawChunkSize = 0;
            boolean confirmSameFrame = false;

            int foundChunkHeaderAndNeedMoreBytes = 0;

            try {
                while (!isInterrupted()) {
                    int count = connectOperator.inputStream.read(readCache);
                    if (count == -1) break;
                    if (count == 0) continue;
                    readBuffer.write(readCache, 0, count);

                    int i = 0;

                    while (i < count) {
                        if (foundChunkHeaderAndNeedMoreBytes > 0) {
                            foundChunkHeaderAndNeedMoreBytes--;
                            readBufferChunkEndIndex++;
                            if (foundChunkHeaderAndNeedMoreBytes == 0) {

                                byte[] readArray = readBuffer.toByteArray();

                                chunkBuffer.write(readArray, 0, readBufferChunkEndIndex - 2 - chunkHeaderByteArray.length);

                                readBuffer.reset();
                                int k = readBufferChunkEndIndex - 2 - chunkHeaderByteArray.length;
                                for (int j = 0; j < BOUNDARY_STRING.length() + 2; j++, k++) {
                                    lastChunkHeader[j] = readArray[k];
                                }
                                readBuffer.write(readArray, readBufferChunkEndIndex, readArray.length - readBufferChunkEndIndex);

                                readBufferChunkEndIndex = 0;
                                if (!readFirstChunk) {
                                    readFirstChunk = true;
                                    header = chunkBuffer.toString();
                                    chunkBuffer.reset();
//                                    notifyChunkReady();
                                    i++;
                                    continue;
                                }
                                byte[] rawImageFileByteArray = chunkBuffer.toByteArray();
                                //这里是录屏
                                if (null != screenRecordThread && screenRecordThread.isAlive()) {
                                    screenRecordThread.addImgToVideo(rawImageFileByteArray);
                                }
//                                if (compressQuality != 1f) {
//
////                                    chunkHandleThread.putChunk(rawImageFileByteArray);
//                                } else {
////                                    lastChunk = rawImageFileByteArray;
////                                    notifyChunkReady();
//                                }
//                                if (lastRawChunkSize == rawImageFileByteArray.length) {
//                                if (Math.abs(lastRawChunkSize - rawImageFileByteArray.length)<=1000) {
//                                    if (confirmSameFrame) {
//                                        chunkBuffer.reset();
//                                        i++;
//                                        continue;
//                                    }
//                                    confirmSameFrame = true;
//                                } else {
//                                    confirmSameFrame = false;
//                                    lastRawChunkSize = rawImageFileByteArray.length;
//                                }

                                if (null != this.webSession && this.webSession.isOpen()) {
                                    SessionUtils.sendBinary(this.webSession, rawImageFileByteArray);
                                    if (sendDeviceStatus) {
                                        Map<String, Object> result = new HashMap<>();
                                        result.put(ResponseCmd.DeviceStatus.STATUS, ResponseCmd.DeviceStatus.CONNECTED);
                                        SessionUtils.sendMessageInitiative(webSession, ResponseCmd.DEVICE_STATUS, phone.getUdid(), result, "设备连接失败");
                                        sendDeviceStatus = false;
                                    }
                                }


//                                if (null != screenCompressHandle && screenCompressHandle.isAlive() && !screenCompressHandle.isInterrupted()) {
//                                    screenCompressHandle.addData(rawImageFileByteArray);
//                                }
                                chunkBuffer.reset();
                            }
                            i++;
                            continue;
                        }

                        if (chunkHeaderByteArray[chunkHeaderPosition] == readCache[i]) {
                            chunkHeaderPosition++;
                            if (chunkHeaderPosition == chunkHeaderByteArray.length) {
                                foundChunkHeaderAndNeedMoreBytes = 2;
                                readBufferChunkEndIndex++;
                                chunkHeaderPosition = 0;
                                i++;
                                continue;
                            }
                        } else {
                            chunkHeaderPosition = 0;
                        }
                        readBufferChunkEndIndex++;
                        i++;
                    }
                }
            } catch (IOException e) {
                logger.error("从设备读取数据失败，稍后重试", e);
                try {
                    sleep(500);
                } catch (InterruptedException ignored) {
                    interrupt();
                    break;
                }
            }

        }
        logger.warn("设备【{}】抓取屏幕线程退出。。。。。。。。。。。。", phone.getUdid());
    }

    /**
     * @param phone
     */
    public IosScreenResponseThread(PhoneEntity phone) {
//        screenCompressHandle = new IosScreenCompressHandleThread(phone.getUdid());
//        screenCompressHandle.start();
        this.phone = phone;
        setName(phone.getUdid() + "拉取屏幕");
    }

    private UsbMuxd.ConnectOperator getDeviceConnectOperator(UsbMuxd usbMuxd) {
        try {
            List<DeviceConnectionDetail> connectionDetailList = usbMuxd.listDevices();
            for (DeviceConnectionDetail detail : connectionDetailList) {
                if (detail.serialNumber.equalsIgnoreCase(phone.getUdid()) && detail.connectStatus == DeviceConnectionDetail.ConnectStatus.ATTACHED) {
                    AppleDevice appleDevice = new AppleDevice(detail);
                    try {
                        return usbMuxd.connectDeviceLocalPort(appleDevice, WEB_DRIVER_AGENT_SCREEN_PORT);
                    } catch (IOException e) {
                        logger.warn("设备已连接，但无法连接WebDriverAgent端口:" + WEB_DRIVER_AGENT_SCREEN_PORT);
                    }
                }
            }
        } catch (UsbMuxdConnectFailedException e) {
            logger.error("查找设备时无法连接到usbmuxd，是否至少连接了一个苹果设备？", e);
        } catch (IOException e) {
            logger.error("查找设备时与usbmuxd通信发生错误", e);
        }
        return null;
    }

    public void setScreenOnRequest(CmdRequest screenOnRequest) {
        if (!isInterrupted() && null != this.webSession && this.webSession.isOpen()) {  //只发第一次screen_on指令
            SessionUtils.sendSuccessData(this.webSession, screenOnRequest, null, "开启屏幕成功");
        }
    }

    public void changeSize(int width, int height) {
//        if (this.screenCompressHandle != null && this.screenCompressHandle.isAlive() && !this.screenCompressHandle.isInterrupted()) {
//            this.screenCompressHandle.setScreenSize(width,height);
//        } else {
//            logger.warn("手机【】的图像传输线程未准备好。。。。。。。。。。。。",phone.getUdid());
//        }
    }

    public void changeQuality(float quality,int frameRate) {
        IosDeviceInitThread iosInitThread = IOSDeviceManager.getInstance().getIosInitThread(phone.getUdid());
        if (null != iosInitThread && !iosInitThread.isInterrupted() && iosInitThread.isAlive()) {
            logger.warn("设置手机【{}】推送图片的帧率：{}",phone.getUdid(),frameRate);
            NKAgent nkAgent = iosInitThread.getNkAgent();
            nkAgent.setMJPEGServerRate(frameRate);
        } else {
            logger.warn("设备【{}】已经不在线了............................");
        }
    }

    /**
     * 1.判断是否处于自动化任务录屏中，若是，保留该线程，等待自动化录屏完成之后再退出
     * 2.若不处于自动化任务录屏中，判断是否真机录屏中，是则先退出真机的录屏，将录屏地址发送到服务端，若没有真机录屏，直接关闭
     */
    public void stopFetchPic() {
        if (recordingType.equals("2")) {  //直播退出
            logger.warn("该设备【{}】处于自动化的录屏阶段，稍后自动化录屏完成之后再退出手机屏幕的获取。。。。。。", phone.getUdid());
            this.webSession = null;
            return;
        }
        logger.warn("该设备【{}】未处于自动化的录屏阶段，现在退出屏幕抓取。。。。。。", phone.getUdid());
        String videoUrl = stopRecord();
        if (null != videoUrl) {
            Map<String, Object> result = new HashMap<>();
            result.put("videoInfo", videoUrl);
            SessionUtils.sendMessageInitiative(webSession, ResponseCmd.RECORD_FINISH, this.phone.getUdid(), result, "结束录屏成功");
        }
        if (null != connectOperator) {
            try {
                connectOperator.close();
            } catch (Exception e) {
                logger.warn("设备【{}】关闭图像抓取失败。。。。。", phone.getUdid(), e);
            }
        }
        IOSDeviceManager.getInstance().removeScreenThread(phone.getUdid());
        this.webSession = null;
        interrupt();
    }

    public void startRecordScreen(String tenantId, int width, int height, String recordType) {
        this.recordingType = recordType;
        if (null != screenRecordThread && screenRecordThread.isAlive()) {
            screenRecordThread.killRecord();
        }
        SimpleDateFormat format = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss");
        String dateFormat = format.format(new Date());
        String videoName = this.phone.getUdid() + "_" + dateFormat + ".mp4";
        screenRecordThread = new IosScreenRecordThread(this.phone.getUdid(), videoName, tenantId, width, height);
        screenRecordThread.start();
    }

    //真机结束录屏
    public String stopRecord() {
        this.recordingType = "0";
        if (null == screenRecordThread || screenRecordThread.isInterrupted() || !screenRecordThread.isAlive()) {
            return null;
        }
        String result = screenRecordThread.endRecord();
        screenRecordThread = null;
        return result;
    }

    //自动化结束录屏
    public String stopRecord(String tenantId) {
        this.recordingType = "0";
        if (null == screenRecordThread || screenRecordThread.isInterrupted() || !screenRecordThread.isAlive()) {
            return null;
        }
        String result = screenRecordThread.endRecord(tenantId);
        JSONObject object = JSONObject.parseObject(result, JSONObject.class);
        screenRecordThread = null;
        String videoUrl = object.getString("videoUrl");
        logger.debug("上传录屏的地址：{}", videoUrl);
        if (this.webSession == null) {
            logger.info("设备【{}】自动化录屏退出，同时退出设备的屏幕获取", phone.getUdid());
            stopFetchPic();
        } else {
            logger.info("设备【{}】正链接前端的屏幕，稍后退出", phone.getUdid());
        }
        return videoUrl;
    }

    public void startSendScreenToWeb(Session session, CatchParam catchParam) {
        this.webSession = session;
        //通知前端开始连接
        Map<String, Object> messageMap = new HashMap<>();
        messageMap.put(ResponseCmd.DeviceStatus.STATUS, ResponseCmd.DeviceStatus.CONNECTING);
        SessionUtils.sendMessageInitiative(webSession, ResponseCmd.DEVICE_STATUS, phone.getUdid(), messageMap, "设备掉线了");
        this.sendDeviceStatus = true;
    }

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