package com.zd.netty.sdk;

import com.gg.reader.api.dal.GClient;
import com.gg.reader.api.protocol.gx.*;
import com.zd.common.core.enums.HardwareOperate;
import com.zd.common.core.exception.ServiceException;
import com.zd.common.core.utils.SpringUtils;
import com.zd.netty.service.ISendService;
import com.zd.netty.service.IService;
import com.zd.system.api.domain.InventoryTag;
import com.zd.system.api.laboratory.RemoteLaboratoryService;
import com.zd.system.api.laboratory.domain.RemoteLabHardware;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @author Administrator
 */
@Slf4j
@Service
public class DeJuRFIDServerImpl implements IService {

    private static final ISendService sendService = SpringUtils.getBean("sendService");

    private final Map<String, GClient> clientMap = new ConcurrentHashMap<>();

    private final Map<String, Boolean> alarmMap = new ConcurrentHashMap<>();

    private final Map<String, Integer> stopMap = new ConcurrentHashMap<>();
    private static final Integer TIME_OUT = 2000;

    private static final Integer MAX_RETRY = 3;

    private static final ScheduledExecutorService scheduledExecutorService = SpringUtils.getBean("scheduledExecutorService");

    @Resource
    private RedisTemplate<String, RemoteLabHardware> redisTemplate;

    @Resource
    private RemoteLaboratoryService remoteLaboratoryService;

    @Override
    public void start(RemoteLabHardware hardware) {
        open(hardware);
    }

    private void open(RemoteLabHardware hardware) {
        GClient client;
        String ipAddress = hardware.getIpAddress();
        if (clientMap.containsKey(ipAddress)) {
            client = clientMap.get(ipAddress);
        } else {
            client = new GClient();
            clientMap.put(ipAddress, client);
        }
        String[] ip = ipAddress.split(":");
        int port=8160;
        if (ip.length==1){
            ipAddress+=":"+port;
        }
        if (client.openTcp(ipAddress, TIME_OUT)) {
            stopMsg(client);
            boolean change = setGpi(client);
            if (!change){
                log.info("GPI epc error.");
                close(hardware);
                open(hardware);
                return;
            }
            MsgBaseSetBaseband msgBaseSetBaseband = setSession(hardware, client);
            if (0 != msgBaseSetBaseband.getRtCode()) {
                log.info("Session epc error.");
                close(hardware);
                open(hardware);
                return;
            }
            // 功率配置, 将4个天线功率都设置为30dBm.
            MsgBaseSetPower msgBaseSetPower = setPower(hardware, client);
            if (0 != msgBaseSetPower.getRtCode()) {
                log.info("Power configuration error.");
                close(hardware);
                open(hardware);
                return;
            }
            //天线读卡, 读取EPC数据区以及TID数据区
            MsgBaseInventoryEpc msgBaseInventoryEpc = setInventory(hardware, client);
            if (0 != msgBaseInventoryEpc.getRtCode()) {
                log.info("Inventory epc error.");
                close(hardware);
                open(hardware);
                return;
            }

            // 订阅标签上报事件
            subscribeHandler(client, hardware);
            subscribeHandlerTagEpcOver(client,hardware);
            subscribeTcpHandler(client);
            String serialNumber = client.getSerialNumber();
            if (StringUtils.hasLength(serialNumber)){
                redisTemplate.opsForValue().set(serialNumber, hardware);
            }
        } else {
            throw new ServiceException("Connect failure.");
        }
    }

    /**
     * 订阅TCP断开连接上报
     *
     * @param client   客户端对象
     */
    public void subscribeTcpHandler(GClient client) {
        RemoteLabHardware labHardware = redisTemplate.opsForValue().get(client.getSerialNumber());
        client.onDisconnected = s -> {
            log.info("连接" + s + "已断开");
            if (labHardware!=null){
                client.close();
                remoteLaboratoryService.update(HardwareOperate.CLOSE, labHardware.getIpAddress());
                log.info("连接" + s + "重连中。。。");
                start(labHardware);
            }
        };
    }

    public static MsgBaseInventoryEpc setInventory(RemoteLabHardware hardware, GClient client) {
        MsgBaseInventoryEpc msgBaseInventoryEpc = new MsgBaseInventoryEpc();
        switch (hardware.getChannels()) {
            case 4:
                msgBaseInventoryEpc.setAntennaEnable(EnumG.AntennaNo_1 | EnumG.AntennaNo_2 | EnumG.AntennaNo_3 | EnumG.AntennaNo_4);
                break;
            case 8:
                msgBaseInventoryEpc.setAntennaEnable(EnumG.AntennaNo_1 | EnumG.AntennaNo_2 | EnumG.AntennaNo_3 | EnumG.AntennaNo_4 | EnumG.AntennaNo_5 | EnumG.AntennaNo_6 | EnumG.AntennaNo_7 | EnumG.AntennaNo_8);
                break;
            case 16:
                msgBaseInventoryEpc.setAntennaEnable(EnumG.AntennaNo_1 | EnumG.AntennaNo_2 | EnumG.AntennaNo_3 | EnumG.AntennaNo_4 | EnumG.AntennaNo_5 | EnumG.AntennaNo_6 | EnumG.AntennaNo_7 | EnumG.AntennaNo_8 | EnumG.AntennaNo_9 | EnumG.AntennaNo_10 | EnumG.AntennaNo_11 | EnumG.AntennaNo_12 | EnumG.AntennaNo_13 | EnumG.AntennaNo_14 | EnumG.AntennaNo_15 | EnumG.AntennaNo_16);
                break;
            case 1:
            default:
                msgBaseInventoryEpc.setAntennaEnable(EnumG.AntennaNo_1);
        }
        msgBaseInventoryEpc.setInventoryMode(EnumG.InventoryMode_Inventory);

        client.sendSynMsg(msgBaseInventoryEpc);
        if (0 == msgBaseInventoryEpc.getRtCode()) {
            log.info("Inventory epc success.");
        }
        return msgBaseInventoryEpc;
    }

    public static MsgBaseSetPower setPower(RemoteLabHardware hardware, GClient client) {
        String uniformPower = hardware.getUniformPower();
        MsgBaseGetPower msgBaseGetPower = new MsgBaseGetPower();
        client.sendSynMsg(msgBaseGetPower);
        MsgBaseSetPower msgBaseSetPower = new MsgBaseSetPower();
        if (0 == msgBaseGetPower.getRtCode()) {
            sendPower(hardware, client, uniformPower, msgBaseGetPower, msgBaseSetPower);
        } else {
            log.info("==============获取输出功率失败==============");
        }
        return msgBaseSetPower;
    }

    private static void sendPower(RemoteLabHardware hardware, GClient client, String uniformPower, MsgBaseGetPower msgBaseGetPower, MsgBaseSetPower msgBaseSetPower) {
        if (StringUtils.hasLength(uniformPower)) {
            editAndSendPower(hardware, client, uniformPower, msgBaseSetPower, msgBaseGetPower);
        } else {
            msgBaseSetPower.setRtCode((byte) 0);
        }
    }

    private static void editAndSendPower(RemoteLabHardware hardware, GClient client, String uniformPower, MsgBaseSetPower msgBaseSetPower, MsgBaseGetPower msgBaseGetPower) {
        Hashtable<Integer, Integer> dicPower = msgBaseGetPower.getDicPower();
        Integer channels = hardware.getChannels();
        String[] uniformPowers = uniformPower.split(",");
        for (int i = 1; i <= channels; i++) {
            Integer power = dicPower.get(i);
            String tempUniformPower = uniformPowers[i - 1];
            String[] tempUniformPowers = tempUniformPower.split(":");
            String temp = tempUniformPowers[0];
            if (power == null || !Objects.equals(power, Integer.valueOf(temp))) {
                dicPower.put(i, Integer.valueOf(temp));
            }
        }
        msgBaseSetPower.setDicPower(dicPower);
        client.sendSynMsg(msgBaseSetPower);
        if (0 == msgBaseSetPower.getRtCode()) {
            log.info("Power epc success.");
        } else {
            log.info("==============设置输出功率失败==============");
        }
    }

    public static MsgBaseSetBaseband setSession(RemoteLabHardware hardware, GClient client) {
        MsgBaseGetBaseband msgBaseGetBaseband = new MsgBaseGetBaseband();
        MsgBaseSetBaseband msgBaseSetBaseband = new MsgBaseSetBaseband();
        client.sendSynMsg(msgBaseGetBaseband);
        if (0 == msgBaseGetBaseband.getRtCode()) {
            int session = msgBaseGetBaseband.getSession();
            log.info("Session index is：==============》{}", session);
            if (session != hardware.getSessionIndex()) {
                msgBaseSetBaseband.setSession(hardware.getSessionIndex());
                client.sendSynMsg(msgBaseSetBaseband);
                if (0 != msgBaseSetBaseband.getRtCode()) {
                    log.info("Session epc error.");
                } else {
                    log.info("Session epc success.");
                }
            } else {
                msgBaseSetBaseband.setRtCode((byte) 0);
            }
        }
        return msgBaseSetBaseband;
    }

    public static boolean setGpi(GClient client) {
        MsgAppGetGpiState gpiState=new MsgAppGetGpiState();
        client.sendSynMsg(gpiState);
        HashMap<Integer, Integer> hashMap = gpiState.getHpGpiState();
        boolean change=true;
        for (Map.Entry<Integer,Integer> entry:hashMap.entrySet()) {
            Integer k = entry.getKey();
            MsgAppGetGpiTrigger getGpiTrigger = new MsgAppGetGpiTrigger();
            getGpiTrigger.setGpiPort(k-1);
            client.sendSynMsg(getGpiTrigger);
            if (getGpiTrigger.getRtCode()==0){
                int triggerOver = getGpiTrigger.getTriggerOver();
                int triggerStart = getGpiTrigger.getTriggerStart();
                if (triggerStart!=0|| triggerOver!=0){
                    MsgAppSetGpiTrigger gpiTrigger = new MsgAppSetGpiTrigger();
                    gpiTrigger.setGpiPort(getGpiTrigger.getGpiPort());
                    gpiTrigger.setTriggerOver(0);
                    gpiTrigger.setTriggerStart(0);
                    client.sendSynMsg(gpiTrigger);
                    if (0 != gpiTrigger.getRtCode()) {
                        log.info("GPI epc error.");
                        change=false;
                    } else {
                        log.info("GPI epc success.");
                    }
                }
            }
        }
        return change;
    }

    @Override
    public void disconnect(RemoteLabHardware hardware) {
        close(hardware);
    }

    @Override
    public boolean alarm(RemoteLabHardware hardware) {
        //灯带设置
        String ipAddress = hardware.getIpAddress();
        if (clientMap.containsKey(ipAddress)) {
            GClient client = clientMap.get(ipAddress);
            boolean alarm = false;
            synchronized (this.alarmMap){
                if (alarmMap.containsKey(ipAddress)) {
                    alarm = alarmMap.get(ipAddress);
                }
                log.info("报警状态：==============》{},设备IP：{}", alarm, ipAddress);
                if (!alarm) {
                    alarmMap.putIfAbsent(ipAddress,true);
                    return changeGpo(ipAddress, 1, client, 10);
                }
            }
        }
        return false;
    }

    public void close(RemoteLabHardware hardware) {
        String ipAddress = hardware.getIpAddress();
        if (clientMap.containsKey(ipAddress)) {
            GClient client = clientMap.get(ipAddress);
            changeGpo(ipAddress, 0, client, 0);
            stopMsg(client);
            log.error("client.close()");
            client.close();
            clientMap.remove(ipAddress);
        }
    }

    public static void stopMsg(GClient client) {
        MsgBaseStop msg = new MsgBaseStop();
        // 停止读卡，空闲态
        client.sendSynMsg(msg);
        String result = msg.getRtCode() == 0 ? "成功" : "失败";
        log.info("客户端停止读卡{}", result);
    }

    private boolean changeGpo(String ipAddress, int state, GClient client, int delayTime) {
        MsgAppSetGpo msgAppSetGpo = new MsgAppSetGpo();
        msgAppSetGpo.setGpo1(state);
        msgAppSetGpo.setGpo2(state);
        msgAppSetGpo.setGpo3(state);
        msgAppSetGpo.setGpo4(state);
        client.sendSynMsg(msgAppSetGpo);
        String status = state == 1 ? "start" : "stop";
        if (0 == msgAppSetGpo.getRtCode()) {
            log.info("============》灯带{}成功.", status);
            if (state == 1) {
                stopGpo(ipAddress, client, delayTime);
            } else {
                alarmMap.remove(ipAddress);
                stopMap.remove(ipAddress);
            }
            return true;
        } else {
            log.info("Gpo epc {} error.", status);
            if (stopMap.containsKey(ipAddress)){
                stopMap.put(ipAddress,stopMap.get(ipAddress)+1);
            }else {
                stopMap.put(ipAddress,1);
            }
            if (stopMap.get(ipAddress) <= MAX_RETRY) {
                stopGpo(ipAddress, client, delayTime);
            }
            return false;
        }
    }

    public void stopGpo(String ipAddress, GClient client, int delayTime) {
        scheduledExecutorService.schedule(new TimerTask() {
            @Override
            public void run() {
                changeGpo(ipAddress, 0, client, 0);
            }
        }, delayTime, TimeUnit.SECONDS);
    }

    /**
     * 订阅6c标签信息上报
     *
     * @param client   客户端
     * @param hardware 设备数据
     */
    public void subscribeHandler(GClient client, RemoteLabHardware hardware) {
        String ipAddress = hardware.getIpAddress();
        clientMap.computeIfAbsent(ipAddress, f -> client);
        String serialNumber = client.getSerialNumber();
        client.onTagEpcLog = (s, logBaseEpcInfo) -> {
            if (logBaseEpcInfo.getResult() == 0) {
                int antId = logBaseEpcInfo.getAntId();
                String epc = logBaseEpcInfo.getEpc();
                String key=epc+antId;
                if (Boolean.FALSE.equals(redisTemplate.hasKey(key))){
                    log.info("===========》{},index: {}", epc, antId);
                    redisTemplate.opsForValue().set(key,hardware,30,TimeUnit.SECONDS);
                    scheduledExecutorService.execute(()->{
                        InventoryTag tag = new InventoryTag();
                        BeanUtils.copyProperties(logBaseEpcInfo, tag);
                        String readerSerialNumber = logBaseEpcInfo.getReaderSerialNumber();
                        log.info("===========》读取器设备编码：{}", readerSerialNumber);
                        if (StringUtils.hasLength(serialNumber)){
                            tag.setSerialNumber(serialNumber);
                            tag.setRemoteLabHardware(redisTemplate.opsForValue().get(serialNumber));
                        }else {
                            tag.setRemoteLabHardware(hardware);
                        }
                        sendService.send(tag);
                    });
                }
            }
        };
    }

    /**
     * 订阅6c标签信息上报停止事件
     *
     * @param client   客户端
     * @param hardware 设备数据
     */
    public void subscribeHandlerTagEpcOver(GClient client, RemoteLabHardware hardware) {
        client.onTagEpcOver= (s, logBaseEpcInfo) -> {
            log.info("HandlerTagEpcOver:"+s);
            close(hardware);
            open(hardware);
        };
    }
}
