package com.imdroid.vermgr.service;

import com.imdroid.secapi.client.BeidouClient;
import com.imdroid.secapi.client.HttpResp;
import com.imdroid.vermgr.entity.DeviceApp;
import com.imdroid.vermgr.utils.CRC16;
import io.netty.channel.Channel;
import io.netty.util.AttributeKey;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;

/******************************************************
 * 批量升级终端版本
 * 单例，要求线程安全
 * 状态机：Idle---收到UpgradeDeviceList--->Upgrading
 *         ^                                 |
 *         |                                 |
 *         +------Timerout or Complete <-----+
 *
 * 流程：
 * 1）收到待升级的设备列表UpgradeDeviceList。如果当前处于升级完毕状态，则更新待升级列表。回应答：开始升级/升级未结束
 * 2）TCP通道激活 ---> 获取版本信息 ---> 关联TCP通道和待升级的设备
 * 3）启动升级会话 ---> 实时更新会话
 * 数据结构：deviceId,cur_ver,upgrade_firmware,TCP_channel,upgrade_session
 * ****************************************************/
@Slf4j
@RestController
public class UpgradeManager {
    // 版本管理协议
    public static final byte GET_APP_INFO = (byte) 0xA0;
    public static final byte APP_INFO_IND = (byte) 0xA1;
    public static final byte UPGRADE_IND  = (byte) 0xA2;
    public static final byte NOT_UPGRADE_IND = (byte) 0xA3;
    public static final byte UPGRADE_ACK  = (byte) 0xA4;

    @Autowired
    static public UpgradeManager INSTANCE;
    @Autowired
    BeidouClient beidouClient;

    Map<Long, DeviceApp> upgradeDeviceList = new ConcurrentHashMap<>();
    Map<Long, UpgradeSession> upgradeSessionMap = new ConcurrentHashMap<>();
    boolean isIdleState = true;
    TimerTask operationTimerTask = null;
    TimerTask txProgressTimerTask = null;
    Timer timer;   //30s定时器

    @PostMapping("/upgrade_cmd")
    HttpResp upgrade(@RequestParam(name = "deviceList") String deviceList, @RequestParam(name = "verFile") String verFile){
        HttpResp resp = new HttpResp();
        if(onRxUpgradeDeviceList(deviceList.split(";"),verFile)){
            resp.setCode(HttpResp.HTTP_RSP_OK);
        }
        else{
            resp.setCode(HttpResp.HTTP_RSP_FAILED);
            resp.setResponseMessage("正在升级中");
        }
        return resp;
    }

    boolean onRxUpgradeDeviceList(String[] deviceList, String firmware){
        if(isIdleState){
            isIdleState = false;
            //清空状态信息和升级会话
            upgradeDeviceList.clear();
            upgradeSessionMap.clear();
            //创建新的状态信息
            for(String deviceId:deviceList){
                Long deviceSn = Long.valueOf(deviceId);
                if(deviceSn!=null) {
                    DeviceApp deviceApp = new DeviceApp();
                    deviceApp.setDevice_sn(deviceSn);
                    deviceApp.setFirmware(firmware);
                    upgradeDeviceList.put(deviceSn, deviceApp);
                }
            }
            //启动20s定时器，如果这期间没有收到任何数据，则超时失败
            startTimer();
            startTxProgressTimerTask();
            return true;
        }
        else{
            return false;
        }
    }
    public void onChannelActive(Channel channel){
        // get app info
        Long deviceId = (Long) channel.attr(AttributeKey.valueOf("deviceId")).get();
        if(deviceId == null){
            getVerInfo(channel);
        }
    }
    void onChannelInactive(Channel channel){
        // 更新状态为”未连接“
        long sn = (long) channel.attr(AttributeKey.valueOf("sn")).get();
        if(sn > 0){
            DeviceApp deviceApp = upgradeDeviceList.get(sn);
            if (deviceApp != null) {
                deviceApp.setUpgrade_state(DeviceApp.STATE_DISCONNECTED);
            }
            upgradeSessionMap.remove(sn);
        }
    }
    void onRxData(Channel channel, byte[] rx_data){
        if(isIdleState){
            // 临时，空闲状态下，如果连续3次收到C，则认为有终端主动请求升级
            if(rx_data[0]==UpgradeSession.CC && rx_data.length<=4){
                //onUpgradeRequest(channel);
            }
        }
        else {
            if (rx_data[0] == APP_INFO_IND && rx_data.length > 2) {
                onVerInfoReceived(channel, rx_data);
                restartTimer();
            }
            else {
                Long sn = (Long) channel.attr(AttributeKey.valueOf("deviceId")).get();
                if(sn==null) return;
                UpgradeSession session = upgradeSessionMap.get(sn);
                if (session == null) return;

                boolean session_done = false;

                if (rx_data[0] == UPGRADE_ACK) {
                    log.info("upgrade ack");
                    //outputHex(rx_data);
                    if (rx_data[5] != 0) {
                        //终端拒绝升级，原因是产品序列号不对
                        session.reject();
                        session_done = true;
                    }
                } else {
                    session_done = session.procYmodem(channel, rx_data);
                }

                if (session_done) {
                    if (isAllUpgraded()) {
                        //全部发完，等待新版本启动回应答
                        stopTimer();
                        onUpgradeComplete();
                    }
                }
            }
        }

    }

    void onUpgradeComplete(){
        isIdleState = true;
        stopTxProgressTimerTask();
        refreshTxProgress();
        beidouClient.onUpgradeComplete();
    }
    /***************************************************
     * 定时器处理
     ****************************************************/
    void startTimer(){
        if(operationTimerTask == null) {
            operationTimerTask = new TimerTask() {
                @Override
                public void run() {
                    onTimerout();
                }
            };
        }
        timer.schedule(operationTimerTask,15000);
    }
    void restartTimer(){
        stopTimer();
        startTimer();
    }
    void stopTimer(){
        if(operationTimerTask != null){
            operationTimerTask.cancel();
            operationTimerTask = null;
        }
    }

    void onTimerout(){
        // 超时处理
        for(UpgradeSession session:upgradeSessionMap.values()){
            if(session.getState() == DeviceApp.STATE_UPGRADING) {
                session.onTimeout();
            }
        }
        //
        onUpgradeComplete();
    }

    void startTxProgressTimerTask(){
        if(txProgressTimerTask==null){
            txProgressTimerTask = new TimerTask() {
                @Override
                public void run() {
                    refreshTxProgress();
                }
            };
        }
        timer.schedule(txProgressTimerTask,1000,1000);
    }
    void stopTxProgressTimerTask(){
        if(txProgressTimerTask != null){
            txProgressTimerTask.cancel();
            txProgressTimerTask = null;
            log.info("stopTxProgressTimerTask");
        }
    }
    /***************************************************
     * 获取终端信息
     ****************************************************/
    public void getVerInfo(Channel channel){
        // 向所有连接终端发读取版本请求
        byte get_app_info[] = {GET_APP_INFO,0};
        byte tailer_buff[] = {0,0};
        int crc = CRC16.calculate(get_app_info);
        tailer_buff[0] = (byte) (crc>>8);
        tailer_buff[1] = (byte) crc;
        channel.write(get_app_info);
        channel.writeAndFlush(tailer_buff);
    }

    void onVerInfoReceived(Channel channel, byte[] rx_data){
        log.info(channel.remoteAddress()+": received app info");
        // 记录获取到的版本信息
        if(rx_data.length<9) return;
        int crc = CRC16.calculate(rx_data, 7);
        int crc0 =  (((short)rx_data[7]&0xFF)<<8) | ((short)rx_data[8]&0xFF);
        if(crc != crc0) {
            log.info(channel.remoteAddress()+": crc error");
            return;
        }
        int i=1;
        long sn = 0;
        for(i=1;i<=4;i++){
            sn = (sn<<8) | ((short)rx_data[i]&0xFF);
        }
        String cur_ver = ((short)rx_data[i++]&0xFF) + "." + ((short)rx_data[i++]&0xFF);

        // 更新APP记录并检查此终端是否需要升级
        DeviceApp deviceApp = upgradeDeviceList.get(sn);

        if (deviceApp == null) return;

        deviceApp.setVersion(cur_ver);
        //记录这个channel是有应答的
        channel.attr(AttributeKey.valueOf("deviceId")).set(sn);
        // 如果原来是未连接状态，则改为连接；如果是其他状态则保持不变
        if(deviceApp.getUpgrade_state() == DeviceApp.STATE_DISCONNECTED) {
            deviceApp.setUpgrade_state(DeviceApp.STATE_CONNECTED);
        }
        //启动升级
        UpgradeSession session = new UpgradeSession(deviceApp);
        upgradeSessionMap.put(sn, session);
        session.start(channel);
    }

    /***************************************************
     * 升级
     ****************************************************/
    synchronized public boolean isAllUpgraded(){
        for(DeviceApp deviceApp:upgradeDeviceList.values()){
            if(deviceApp.getUpgrade_state() != DeviceApp.STATE_TX_COMPLETED &&
                    deviceApp.getUpgrade_state() != DeviceApp.STATE_TX_FAILED) {
                return false;
            }
        }
        return true;
    }

    void refreshTxProgress(){
        for(UpgradeSession session:upgradeSessionMap.values()){
            if(session.getState() == DeviceApp.STATE_UPGRADING) {
                beidouClient.onUpgradeProgress(Long.toString(session.getSN()), session.getTxPercentage() + "%");
            }
        }
        for(DeviceApp deviceApp:upgradeDeviceList.values()){
            if(deviceApp.getUpgrade_state()!=DeviceApp.STATE_UPGRADING){
                beidouClient.onUpgradeProgress(Long.toString(deviceApp.getDevice_sn()),deviceApp.getStrState());
            }
        }
    }
}
