package com.zd.laboratory.socket.service;

import com.alibaba.fastjson.JSONObject;
import com.zd.common.core.annotation.FunctionMapper;
import com.zd.common.core.constant.CacheConstants;
import com.zd.common.core.constant.CacheDevice;
import com.zd.common.core.enums.HardwareOperate;
import com.zd.common.core.enums.SenseStateEnum;
import com.zd.common.core.utils.SpringUtils;
import com.zd.common.core.web.domain.AjaxResult;
import com.zd.common.redis.service.RedisService;
import com.zd.laboratory.domain.LabRelay;
import com.zd.laboratory.domain.SensorConfig;
import com.zd.laboratory.event.SensorStatusEvent;
import com.zd.laboratory.event.SubFunctionEvent;
import com.zd.laboratory.mapper.LabRelayMapper;
import com.zd.laboratory.mqtt.entiy.EquipmentStatus;
import com.zd.laboratory.mqtt.entiy.SensorStatus;
import com.zd.laboratory.mqtt.service.impl.CommonSend;
import com.zd.laboratory.mqtt.service.impl.SubMessageSendManager;
import com.zd.laboratory.service.ISensorConfigService;
import com.zd.laboratory.socket.command.Control;
import com.zd.laboratory.socket.command.Symbol;
import com.zd.laboratory.socket.runner.TCPServer;
import com.zd.laboratory.socket.vo.TransmissionVo;
import com.zd.laboratory.utils.CRCCHECK;
import com.zd.laboratory.utils.FunctionMapperUtil;
import com.zd.laboratory.utils.HexUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.validation.constraints.NotNull;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

@Service(value = "socketService")
@EnableAsync
public class SocketService {

    @Value("${mqtt.relayPrefix}")
    private String relayPrefix;

    @Value("${mqtt.steerSubscriptPrefix}")
    private String steerSubscriptPrefix;

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

    @Autowired
    private RedisService redisService;
    @Autowired
    private SubMessageSendManager subMessageSendManager;
    @Autowired
    private FunctionMapperUtil functionMapperUtil;
    @Autowired
    private LabRelayMapper labRelayMapper;
    @Autowired
    private ISensorConfigService sensorConfigService;

    private List<LabRelay> relays;

    private List<SensorConfig> sensorConfigs;


    @Autowired
    private CommonSend commonSend;

    /**
     * 获取当前网关与传感器功能配置
     * @return
     */
    public List<SensorConfig> getSensorConfigs() {
        return sensorConfigs;
    }

    @PostConstruct
    private void queryRelays(){
        relays = labRelayMapper.selectLabRelayList(new LabRelay());
        sensorConfigs = sensorConfigService.selectSensorConfigList(new SensorConfig());
    }

    /**
     * 实时监测传感器数据 并放入缓存
     * @param data 解析后的数据
     */
    @Async("labExecutor")
    public void setSensorData(String data)  {

        //try {
            TransmissionVo transmissionVo = new TransmissionVo(data);

            log.info("传感器解析结果：" + transmissionVo.toString());

            log.info("传感器设备ID：" + transmissionVo.getA3());
//
//            SensorConfig sensorConfig = saveRelayInfo(transmissionVo.getA3(), Integer.parseInt(transmissionVo.getA6(), Character.FORMAT)/4, 2);

            /*if(sensorConfig != null){
                confiSensorData(transmissionVo, sensorConfig);
            }*/

            String redisKey = CacheDevice.SENSOR_KEY.getRedisKey() + transmissionVo.getA3();
            if(!redisService.hasKey(redisKey)){
                SensorStatus sensorStatus = new SensorStatus();
                sensorStatus.setNum(transmissionVo.getA3());
                sensorStatus.setSenseState(SenseStateEnum.TURN_ON);
                SensorStatusEvent sensorStatusEvent = new SensorStatusEvent(sensorStatus);
                SpringUtils.getApplicationContext().publishEvent(sensorStatusEvent);
            }
            //缓存数据
            redisService.setCacheObject(redisKey, transmissionVo.toString(), 5L, TimeUnit.MINUTES);
            redisService.setCacheObject( CacheDevice.SENSOR_LIFE.getRedisKey() + transmissionVo.getA3(), transmissionVo.getA3(),
                    CacheDevice.SENSOR_LIFE.getRedisTime() ,TimeUnit.SECONDS);

            //抛出传感器解析事件
            SubFunctionEvent subFunctionEvent = new SubFunctionEvent(transmissionVo.getA3());
            SpringUtils.getApplicationContext().publishEvent(subFunctionEvent);
        /*}catch (Exception e){
            e.printStackTrace();
            log.error("传感器推送数据异常：" + e.getMessage());
        }*/
    }

    private void confiSensorData(TransmissionVo transmissionVo, SensorConfig sensorConfig) {

        try {
            // 煤尘
            if(StringUtils.isNotBlank(sensorConfig.getFenchen())){
                doConfig(transmissionVo, sensorConfig.getFenchen(), "fenchen");
            }

            // 温度
            if(StringUtils.isNotBlank(sensorConfig.getWendu())){
                doConfig(transmissionVo, sensorConfig.getWendu(), "wendu");
            }

            // 湿度
            if(StringUtils.isNotBlank(sensorConfig.getShidu())){
                doConfig(transmissionVo, sensorConfig.getShidu(), "shidu");
            }

            // 一氧化碳
            if(StringUtils.isNotBlank(sensorConfig.getCo())){
                doConfig(transmissionVo, sensorConfig.getCo(), "co");
            }

            // 大气压
            if(StringUtils.isNotBlank(sensorConfig.getDaqiya())){
                doConfig(transmissionVo, sensorConfig.getDaqiya(), "daqiya");
            }

            // 氧气浓度
            if(StringUtils.isNotBlank(sensorConfig.getO2())){
                doConfig(transmissionVo, sensorConfig.getO2(), "o2");
            }

            // 烟雾
            if(StringUtils.isNotBlank(sensorConfig.getYanwu())){
                doConfig(transmissionVo, sensorConfig.getYanwu(), "yanwu");
            }

            // 甲烷
            if(StringUtils.isNotBlank(sensorConfig.getJiawan())){
                doConfig(transmissionVo, sensorConfig.getJiawan(), "jiawan");
            }

            // voc
            if(StringUtils.isNotBlank(sensorConfig.getVoc())){
                doConfig(transmissionVo, sensorConfig.getVoc(), "voc");
            }

            // 二氧化硫
            if(StringUtils.isNotBlank(sensorConfig.getSo2())){
                doConfig(transmissionVo, sensorConfig.getSo2(), "so2");
            }

            // 一氧化氮
            if(StringUtils.isNotBlank(sensorConfig.getNo())){
                doConfig(transmissionVo, sensorConfig.getNo(), "no");
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    private void doConfig(TransmissionVo transmissionVo, String data, String type) throws NoSuchFieldException, IllegalAccessException {
        String[] nums = data.split(",");
        for (String num : nums) {
            String field = "A7_" + num;

            Field a = transmissionVo.getClass().getDeclaredField(field);
            if(a == null){continue;};
            String name = a.getName();

            if(name.equals(field)){
                //获取注解
                FunctionMapper foo = a.getAnnotation(FunctionMapper.class);
                if(foo == null){
                    continue;
                }
                InvocationHandler h = Proxy.getInvocationHandler(foo);
                Field hField ;

                String value = foo.funNum();

                hField = h.getClass().getDeclaredField("memberValues");
                // 因为这个字段事 private final 修饰，所以要打开权限
                hField.setAccessible(true);
                Map memberValues = (Map) hField.get(h);

                // 修改 value 属性值
                memberValues.put("funNum", type);
                // 获取 foo 的 value 属性值
                value = foo.funNum();
                a.setAccessible(true);

                System.out.println("传感器数据转换--" + field + ": " + value + ", " + a.get(transmissionVo));
            }
        }
    }

    /**
     * 通过传感器ID 获取传输数据
     * @param sensorId 传感器id
     * @return 缓存数据
     */
    public TransmissionVo getSensorData(String sensorId){
        TransmissionVo transmissionVo = null;
        if(StringUtils.isNotBlank(sensorId) && redisService.hasKey(CacheDevice.SENSOR_KEY.getRedisKey() + sensorId)){

            String str = redisService.getCacheObject(CacheDevice.SENSOR_KEY.getRedisKey() + sensorId);
            if(StringUtils.isNotBlank(str)){
                transmissionVo = JSONObject.parseObject(str, TransmissionVo.class);
            }
        }
        return transmissionVo;
    }

    /**
     * 通过传感器ID 获取传输数据
     * @return 缓存数据
     */
//    @Async
    public SensorConfig saveRelayInfo(String deviceNumber, int count, int type) {
        try {

            boolean has = false;
            if(CollectionUtils.isNotEmpty(relays)){
                has = relays.stream().anyMatch(a -> a.getCode().equals(deviceNumber) && a.getRouteNum() == count);
            }
            if(!has){
                LabRelay relay = new LabRelay(deviceNumber, type, count);
                labRelayMapper.insertLabRelay(relay);
                relays.add(relay);
            }

            // 如果是继电器，则返回继电器 configId
            if(type == 2){
                LabRelay relay = relays.stream().filter(a -> a.getCode().equals(deviceNumber)).findFirst().orElse(null);
                if(relay != null){
                    Long configId = relay.getConfigId();
                    if(configId != null){
                        SensorConfig sensorConfig = sensorConfigs.stream().filter(s -> s.getId().equals(configId)).findFirst().orElse(null);
                        return sensorConfig;
                    }else {
                        LabRelay newRelay = labRelayMapper.selectLabRelayById(relay.getId());
                        if(newRelay != null && newRelay.getConfigId() != null){
                            relays.remove(relay);
                            relays.add(newRelay);
                            SensorConfig sensorConfig = sensorConfigs.stream().filter(s -> s.getId().equals(newRelay.getConfigId())).findFirst().orElse(null);
                            return sensorConfig;
                        }
                    }
                }
            }
        }catch (Exception e){

        }
        return null;
    }

    /**
     *  继电器下发指令
     * @param order 操作指令
     * @param deviceNumber 继电器编码
     * @param command 控制指令 开启/关闭
     * @param lines 继电器线路数量 从1 开始，不能大于该继电器的总数  最大16
     */
    public AjaxResult sendCommand(@NotNull Symbol.order order, @NotNull String deviceNumber, @NotNull Symbol.command command, int lines) {

        String str = Control.sendCommand(order, deviceNumber, command, lines);
        log.info("returnStr:"+str);
        if("下发成功".equals(str)){
            return AjaxResult.success();
        }
        return AjaxResult.error(str);
    }

    /**
     *  继电器下发指令
     * @param hardwareId 继电器id
     * @param deviceNumber 继电器编码
     * @param command 控制指令 开启/关闭
     * @param lines 继电器线路数量 从1 开始，不能大于该继电器的总数  最大16
     */
    public AjaxResult sendMqttCommand(@NotNull Long hardwareId, @NotNull String deviceNumber, @NotNull Symbol.command command, int lines,Long subjectId) {
        List<EquipmentStatus> equipmentStatusList=new ArrayList<>();
        //开关闭合 打开
       String CLOSE_CHAR= "\"close#\":1";
        //开关打开 关闭
       String OPEN_CHAR=" \"open#\":0";
       int status=Integer.parseInt(command.getType());
       String topic=relayPrefix+"/"+deviceNumber;
       topic=topic.replace("//","/");
        //关闭
       if(status==0){
           //发送状态控制
           EquipmentStatus equipmentStatus=new EquipmentStatus();
           equipmentStatus.setNum(hardwareId+"");
           equipmentStatus.setHardwareOperate(HardwareOperate.CLOSE);
           equipmentStatusList.add(equipmentStatus);
           subMessageSendManager.sendNewHardwareUpdate(equipmentStatusList);
           subMessageSendManager.sendMachineNewHardwareUpdate(equipmentStatusList,subjectId);
           OPEN_CHAR=OPEN_CHAR.replace("#",lines+"");
           commonSend.send(topic,OPEN_CHAR);
           //存入redis中
           redisService.setCacheObject(CacheConstants.RELAY_STATUS_KEY+hardwareId,HardwareOperate.CLOSE.getCode(),15L,TimeUnit.SECONDS);
       }else{
           //发送状态控制
           EquipmentStatus equipmentStatus=new EquipmentStatus();
           equipmentStatus.setNum(hardwareId+"");
           equipmentStatus.setHardwareOperate(HardwareOperate.OPEN);
           equipmentStatusList.add(equipmentStatus);
           subMessageSendManager.sendNewHardwareUpdate(equipmentStatusList);
           subMessageSendManager.sendMachineNewHardwareUpdate(equipmentStatusList,subjectId);
           CLOSE_CHAR=CLOSE_CHAR.replace("#",lines+"");
           commonSend.send(topic,CLOSE_CHAR);
           //存入redis中
           redisService.setCacheObject(CacheConstants.RELAY_STATUS_KEY+hardwareId,HardwareOperate.OPEN.getCode(),15L,TimeUnit.SECONDS);
       }
       return AjaxResult.success();
    }

    /**
     * 继电器下发指令
     */
    public void sendMqttCommand(@NotNull String deviceNumber, @NotNull Symbol.command command, String bit) {
        //开关闭合 打开
        String CLOSE_CHAR= "\"close#\":1";
        //开关打开 关闭
        String OPEN_CHAR=" \"open#\":0";
        int status=Integer.parseInt(command.getType());
        String topic=relayPrefix+"/"+deviceNumber;
        topic=topic.replace("//","/");
        //关闭
        if(status==0){
            OPEN_CHAR=OPEN_CHAR.replace("#",bit);
            commonSend.send(topic,OPEN_CHAR);
        }else{
            CLOSE_CHAR=CLOSE_CHAR.replace("#",bit);
            commonSend.send(topic,CLOSE_CHAR);
        }
    }


    /**
     * 门锁坨机下发写指令
     * @param id ID号  0-253
     * @param position 目标位置 0-4095 开锁 940  关锁 2340
     * @param time 时间
     * @param speed 目标速度 0-3400
     */
    public void sendWriteHelmCommand(@NotNull String id,@NotNull String position,@NotNull String time,@NotNull String speed) {
        byte[] command=HexUtils.generateCommand(id,position,time,speed);
        String topic=steerSubscriptPrefix + "/285E3E7AE93D";
        System.out.println(topic);
        commonSend.send(steerSubscriptPrefix + "/285E3E7AE93D",command);
    }

    /**
     * 门锁坨机下发读指令
     * @param id 0-253
     */
    public void sendReadHelmCommand(@NotNull String id) {
        byte[] command=HexUtils.readCommandById(id);
        String topic=steerSubscriptPrefix + "/285E3E7AE93D";
        commonSend.send(topic,command);
    }


    /**
     * 通过继电器ID 获取传输数据
     * @param relayCode 继电器编码
     * @return 缓存数据
     */
    public Map<String, Object> getRelayData(String relayCode){
        Map<String, Object> redisData = null;
        if(StringUtils.isNotBlank(relayCode) && redisService.hasKey(CacheDevice.RELAY_KEY.getRedisKey() + relayCode)){
            redisData = redisService.getCacheObject(CacheDevice.RELAY_KEY.getRedisKey() + relayCode);
        }
        return redisData;
    }

    /**
     * 获取继电器 位状态 不报错
     * @param relayCode
     * @return
     */
    public  Map<Integer, com.zd.system.api.laboratory.domain.Control.SwitchEnums> getSwitchEnums(String relayCode)
    {
        try {
            Map<String, Object> relayData = getRelayData(relayCode);
            if(relayData==null) return null;
            String reverseBit = (String)relayData.get("reverseBit");
            if(reverseBit==null) return null;
            char[] chars = reverseBit.toCharArray();
            char a= '0';
            Map<Integer, com.zd.system.api.laboratory.domain.Control.SwitchEnums> map = new HashMap<>(chars.length);
            for (int i = 0; i < chars.length; i++) {
                map.put(i+1,chars[i]==a? com.zd.system.api.laboratory.domain.Control.SwitchEnums.OFF: com.zd.system.api.laboratory.domain.Control.SwitchEnums.ON);
            }
            return map;
        } catch (Exception e) {
            return null;
        }
    }

    public void setCabinetLockData(Socket socket, String data) {

        if(data.replace(" ", "").length() != 12){
            log.info("柜锁回调指令非状态指令！" + data);
            return;
        }

        AtomicReference<String> relayCode = new AtomicReference<>("");
        TCPServer.cacheMap.entrySet().forEach(a -> {
            try {
                if(a.getValue() == socket.getOutputStream()){
                    relayCode.set(a.getKey());
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        });

        // 柜锁bit 位
        long bit = CRCCHECK.getBitByCommand(data);
        // 1开锁状态 0关锁状态
        int status = CRCCHECK.getLockStatus(data);

        log.info("柜锁回调：" + relayCode.get() + ":" + bit + "，回调结果" + (status == 1 ? "开启": "关闭") + "，指令：" + data);
        redisService.setCacheObject(relayCode.get() + ":" + bit, status, 3 * 60L, TimeUnit.SECONDS);
    }
}
