package net.northking.cctp.upperComputer.deviceManager;

import com.alibaba.fastjson.JSON;
import net.northking.cctp.upperComputer.deviceManager.screen.AndroidScreenResponseThread;
import net.northking.cctp.upperComputer.deviceManager.thread.AndroidDeviceInitThread;
import net.northking.cctp.upperComputer.driver.adb.Adb;
import net.northking.cctp.upperComputer.driver.adb.AdbDevice;
import net.northking.cctp.upperComputer.driver.adb.AndroidDeviceListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.websocket.Session;
import java.io.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class AndroidDeviceManager extends AbstractDeviceManager {

    private static AndroidDeviceManager androidDeviceManager;

    public static AndroidDeviceManager getInstance() {
        if (androidDeviceManager == null) {
            androidDeviceManager = new AndroidDeviceManager();
            try {
                androidDeviceManager.adb = new Adb();
            } catch (IOException e) {
                logger.warn("adb对象创建失败", e);
            }
        }
        return androidDeviceManager;
    }

    private Adb adb;

    /**
     * 初始的端口
     */
    private int initPort = 8200;

    private Properties phonePortProperties = new Properties();

    private final String portFileName = "androidPhonePort.properties";

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

    /**
     * android设备初始化线程
     */
    private ConcurrentHashMap<String, AndroidDeviceInitThread> onlineDeviceInitMap = new ConcurrentHashMap<>();

    /**
     * android设备初始化线程
     */
    private ConcurrentHashMap<String, Boolean> deviceSystemIsReady = new ConcurrentHashMap<>();


    /**
     * android推送屏幕
     */
    public ConcurrentHashMap<String, AndroidScreenResponseThread> screenMap = new ConcurrentHashMap<>();

    public void saveDeviceScreenThread(String deviceId, AndroidScreenResponseThread screenResponseThread){
        this.screenMap.put(deviceId, screenResponseThread);
    }

    public AndroidScreenResponseThread getScreenThread(String deviceId){
        return this.screenMap.get(deviceId);
    }

    public AndroidScreenResponseThread removeScreenThread(String deviceId){
        return this.screenMap.remove(deviceId);
    }


    /**
     * 设置设备系统状态，true是准备好，false未准备好
     * @param serial
     * @param status
     */
    @Override
    public void setDeviceSystemStatus(String serial, Boolean status) {
        deviceSystemIsReady.put(serial, status);
    }


    @Override
    public void shutdown() {
        clearAndShutdownDevices();
    }


    public AdbDevice getCurrentDevice(String deviceId) {
        AndroidDeviceInitThread initThread = onlineDeviceInitMap.get(deviceId);
        if (null != initThread && initThread.isAlive() && !initThread.isInterrupted()) {
            if (initThread.isAgentStatus()) {
                return initThread.getAdbDevice();
            }
        } else {
            logger.warn("当前设备不在线：{}",deviceId);
        }
        return null;
    }

    public AndroidDeviceInitThread getAndroidInitThread(String serial){
        return onlineDeviceInitMap.get(serial);
    }


    public Adb getAdb() {
        return adb;
    }

    @Override
    public void run() {
        readAndroidForwardPortProperty();
        while (!isInterrupted()) {
            startAdbServer();
            try {
                adb.watchDevice(new AndroidDeviceListener());
            } catch (Exception e) {
                logger.warn("Android设备监听失败",e);
            }
        }
        logger.warn("android设备监听退出。。。。。。。。。。。。");
    }

    private void startAdbServer() {
        String[] cmd = {"adb", "devices"};
        ProcessBuilder builder = new ProcessBuilder(Arrays.asList(cmd)).redirectErrorStream(true);
        try {
            Process process = builder.start();
            process.waitFor();
        } catch (IOException e) {
            logger.warn("adb服务异常",e);
        } catch (InterruptedException e) {
            interrupt();
            return;
        }
    }

    public void handleAndroidDevice(ArrayList<AdbDevice> currentDevices) {
        List<AdbDevice> onlineDevices = new ArrayList<>();
        List<String> offlineDevices = new ArrayList<>();
        checkAndroidDevice(currentDevices,offlineDevices,onlineDevices);
        if (!CollectionUtils.isEmpty(onlineDevices)) {  //设备上线
            for (AdbDevice adbDevice : onlineDevices) {
                logger.info("新上来设备：{}", JSON.toJSONString(adbDevice));
                if (AdbDevice.State.Device == adbDevice.state) {
                    Integer forwardPort = saveDeviceForwardPort(adbDevice.getSerial());
                    //注册并启动agent
                    AndroidDeviceInitThread androidDeviceInitThread = new AndroidDeviceInitThread(adb,adbDevice,forwardPort);
                    androidDeviceInitThread.start();
                    onlineDeviceInitMap.put(adbDevice.getSerial(), androidDeviceInitThread);
                    publishDeviceOnlineToWebConnection(adbDevice.getSerial());
                }
            }
        }
        //下线
        if (!CollectionUtils.isEmpty(offlineDevices)) {
            for (String deviceId : offlineDevices) {
                logger.warn("设备【{}】下线", deviceId);
                AndroidDeviceInitThread deviceInitThread = onlineDeviceInitMap.remove(deviceId);
                if (deviceInitThread != null && deviceInitThread.isAlive() && !deviceInitThread.isInterrupted()) {
                    deviceInitThread.exit();
                }
                offlineDeviceFromDeviceManager(deviceId);
                //设备下线，停止获取屏幕线程
                publishDeviceOfflineToWebConnection(deviceId);
            }
        }
    }

    private Integer saveDeviceForwardPort(String serial) {
        String value = this.phonePortProperties.getProperty(serial);
        if (StringUtils.hasText(value)) {
            logger.info("设备【】已经上过线，分配过adb转发端口，端口为：{}.............", serial, value);
        } else {
            doPropertiesWrite(serial);
            value = this.phonePortProperties.getProperty(serial);
            logger.info("设备【】第一次来，分配adb转发端口，端口为：{}.............", serial,value);
        }
        return Integer.parseInt(value);
    }

    /**
     * 检查哪些设备上线，下线，持有状态不变
     * @param currentDevices  adb检测到变化的设备
     * @param offlineDevices  下线的设备
     * @param onlineDevices   上线的设备
     */
    private void checkAndroidDevice(ArrayList<AdbDevice> currentDevices, List<String> offlineDevices, List<AdbDevice> onlineDevices) {
        Map<String, AdbDevice> currentDeviceMap = new HashMap<>();
        //检查上线
        for (AdbDevice currentDevice : currentDevices) {
            currentDeviceMap.put(currentDevice.getSerial(), currentDevice);
            if (onlineDeviceInitMap.containsKey(currentDevice.getSerial())) {  //原来有
                logger.debug("adb检测到设备：{},该设备已经上线。。。。。。。", currentDevice.getSerial());
            } else {
                onlineDevices.add(currentDevice);
            }
        }
        //检查下线
        ConcurrentHashMap.KeySetView<String, AndroidDeviceInitThread> keySet = onlineDeviceInitMap.keySet();
        for (String deviceId : keySet) {
            if (!currentDeviceMap.containsKey(deviceId)) {
                offlineDevices.add(deviceId);
            }
        }
    }


    public boolean checkMobileIsOnline(String serial) {
        AndroidDeviceInitThread initThread = onlineDeviceInitMap.get(serial);
        if (null != initThread && !initThread.isInterrupted() && initThread.isAlive()) {
            return true;
        }
        return false;
    }

    private void clearAndShutdownDevices(){
        for (Map.Entry<String, AndroidDeviceInitThread> initThreadEntry : onlineDeviceInitMap.entrySet()) {
            AndroidDeviceInitThread initThread = initThreadEntry.getValue();
            initThread.exit();
        }
        onlineDeviceInitMap.clear();
        //停止屏幕获取线程
        for (AndroidScreenResponseThread screenResponseThread : screenMap.values()) {
            logger.debug("上位机重启，中断残余获取屏幕线程");
            if (null != screenResponseThread && screenResponseThread.isAlive() && !screenResponseThread.isInterrupted()) {
                screenResponseThread.stopFetchPic();
            }
        }
        screenMap.clear();
        interrupt();
    }

    @Override
    public List<String> getOnlineDevice() {
        List<String> list = new ArrayList<>();
        Collection<AndroidDeviceInitThread> deviceInitThreads = onlineDeviceInitMap.values();
        for (AndroidDeviceInitThread initThread : deviceInitThreads) {
            if (initThread.isAlive() && !initThread.isInterrupted()) {
                if (initThread.isAgentStatus()) {
                    list.add(initThread.getAdbDevice().getSerial());
                }
            }
        }
        return list;
    }


    @Override
    public void stopWebScreen(String udid, Session session) {
        AndroidScreenResponseThread androidScreenResponseThread = screenMap.get(udid);
        if (null != androidScreenResponseThread) {
            androidScreenResponseThread.stopWebSessionTransform(session);
        }
    }

    private void readAndroidForwardPortProperty() {
        String property = System.getProperty("user.dir");
        File file = new File(property + File.separatorChar + portFileName);
        logger.info("开始读取已经存在的android手机对应的端口，读取文件：{}", file.getAbsolutePath());
        if (!file.exists()) {
            logger.warn("android手机端口映射文件不存在，新建《{}》文件",portFileName);
            try {
                boolean newFile = file.createNewFile();
                if (!newFile) {
                    logger.warn("android端口映射文件【{}】创建失败", file.getAbsolutePath());
                }
            } catch (IOException e) {
                logger.warn("新建android手机端口映射文件失败", e);
            }
        } else {
            FileInputStream inputStream = null;
            try {
                inputStream = new FileInputStream(file);
                phonePortProperties.load(inputStream);
            } catch (FileNotFoundException e) {
                logger.warn("android手机端口映射文件不存在");
            } catch (IOException e) {
                logger.warn("读取android手机端口映射配置文件失败");
            } finally {
                if (null != inputStream) {
                    try {
                        inputStream.close();
                    } catch (IOException e) {
                        logger.warn("关闭流出错", e);
                    }
                }
            }
            logger.info("android手机端口映射文件加载完成，加载内容：");
            for (Object key : phonePortProperties.keySet()) {
                String port = phonePortProperties.getProperty(key + "");
                int forwardPort = Integer.parseInt(port);
                logger.info("手机:{},adb映射的端口为:{}", key, port);
                if (initPort < forwardPort) {
                    initPort = forwardPort;
                }
            }
        }
    }

    private synchronized int getForwardPort() {
        this.initPort++;
        return this.initPort;
    }

    private synchronized void doPropertiesWrite(String deviceId) {
        this.phonePortProperties.setProperty(deviceId, getForwardPort()+"");  //存好分配的端口
        String property = System.getProperty("user.dir");
        File file = new File(property + File.separatorChar + portFileName);
        FileOutputStream fileOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream(file);
            this.phonePortProperties.store(fileOutputStream, "android设备adb转发端口的映射关系");
        } catch (IOException e) {
            logger.warn("写入端口映射文件失败");
        }finally {
            if (null != fileOutputStream) {
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}
