package com.its.pis.service;

import com.its.app.AppUtils;
import com.its.app.utils.SysUtils;
import com.its.pis.dao.mapper.VdsCtlrMapper;
import com.its.pis.dao.mapper.VdsDtctMapper;
import com.its.pis.domain.NET;
import com.its.pis.entity.TbVdsCtlr;
import com.its.pis.entity.TbVdsCtlrStts;
import com.its.pis.entity.TbVdsDtct;
import com.its.pis.global.AppRepository;
import com.its.pis.process.DbmsJobData;
import com.its.pis.process.DbmsJobProcess;
import com.its.pis.process.DbmsJobType;
import com.its.pis.xnettcp.vds.protocol.VdsReqRTC;
import com.its.pis.xnetudp.protocol.CENTER_VDS_RES_STATE;
import com.its.pis.xnetudp.protocol.voVdsState;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.nio.ByteBuffer;
import java.util.*;

@Slf4j
@Service
@Transactional(rollbackFor = {Exception.class})
public class VdsCtlrService {

    private VdsCtlrMapper vdsCtlrMapper;
    private VdsDtctMapper vdsDtctMapper;

    private int sttsMin;

    @PostConstruct
    private void init() {
        log.info("VdsCtlrService.init: Start.");
        this.vdsCtlrMapper = (VdsCtlrMapper) AppUtils.getBean(VdsCtlrMapper.class);
        this.vdsDtctMapper = (VdsDtctMapper) AppUtils.getBean(VdsDtctMapper.class);

        //loadDb();
        //updateCtlrStts(true);
        log.info("VdsCtlrService.init: ..End.");
    }

    public void loadDb() {
        loadVdsCtlr();
        loadVdsDtct();
    }

    public void initVdsCtlr() {
        try {
            for (Map.Entry<String, TbVdsCtlr> e : AppRepository.getInstance().getCtlrMap().entrySet()) {
                // 제어기가 삭제됐다고 초기화
                TbVdsCtlr obj = e.getValue();
                obj.setDEL_YN("Y");
            }
        }
        catch (Exception e) {
            log.error("VdsCtlrService.initVdsCtlr: Exception: {}", e.toString());
        }
    }

    public void loadVdsCtlr() {
        initVdsCtlr();
        try {
            List<TbVdsCtlr> infoList = this.vdsCtlrMapper.selectAll();
            for (TbVdsCtlr obj : infoList) {
                obj.setVDS_CTLR_IP(obj.getVDS_CTLR_IP().trim());
                if (obj.getVDS_CTLR_IP() != null && obj.getVDS_CTLR_IP().length() > 0) {
                    if (obj.getTRAF_CLCT_CYCL() <  14) obj.setTRAF_CLCT_CYCL(14);
                    if (obj.getTRAF_CLCT_CYCL() > 120) obj.setTRAF_CLCT_CYCL(120);

                    long ipAddr = SysUtils.ipToLong(obj.getVDS_CTLR_IP());
                    String dstIpAddr = String.format("%03d.%03d.%03d.%03d-", (ipAddr>>24)&0xFF, (ipAddr>>16)&0xFF, (ipAddr>>8)&0xFF, (ipAddr>>0)&0xFF);
                    obj.setDstIpAddr(dstIpAddr);

                    obj.getStts().setVDS_CTLR_NMBR(obj.getVDS_CTLR_NMBR());
                    obj.getStts().initStts(false);

                    // 요청 패킷 초기화
                    obj.initReqPacket();

                    AppRepository.getInstance().getCtlrMap().put(obj.getVDS_CTLR_NMBR(), obj);
                    AppRepository.getInstance().getCtlrIpMap().put(obj.getVDS_CTLR_IP(), obj);

                    // TODO
                    //AppRepository.getInstance().getCtlrKeyMap().put(obj.getVDS_CTLR_IP() + ":" + obj.getVDS_CTLR_PORT(), obj);
                }
            }
        }
        catch (Exception e) {
            log.error("VdsCtlrService.loadVdsCtlr: Exception: {}", e.toString());
        }

        log.debug("VdsCtlrService.VDS Controller List............... {} EA.", AppRepository.getInstance().getCtlrMap().size());
        for (Map.Entry<String, TbVdsCtlr> e : AppRepository.getInstance().getCtlrMap().entrySet()) {
            log.debug("  {}", e.toString());
        }
        log.debug("VdsCtlrService.VDS Controller IP List............ {} EA.", AppRepository.getInstance().getCtlrIpMap().size());
        for (Map.Entry<String, TbVdsCtlr> e : AppRepository.getInstance().getCtlrIpMap().entrySet()) {
            log.debug("  {}", e.toString());
        }
    }

    public void initVdsDtct() {
        try {
            for (Map.Entry<String, TbVdsCtlr> obj : AppRepository.getInstance().getCtlrMap().entrySet()) {
                TbVdsCtlr ctlr = obj.getValue();
                for (Map.Entry<String, TbVdsDtct> dtct : ctlr.getVdsDtctMap().entrySet()) {
                    // 제어기의 검지기가 삭제되었다고 초기화
                    dtct.getValue().setDEL_YN("Y");
                }
            }
        }
        catch (Exception e) {
            log.error("VdsCtlrService.initVdsDtct: Exception: {}", e.toString());
        }
    }

    public void loadVdsDtct() {
        initVdsDtct();
        try {
            List<TbVdsDtct> infoList = this.vdsDtctMapper.selectAll();
            for (TbVdsDtct obj : infoList) {
                TbVdsCtlr ctlr = AppRepository.getInstance().getCtlrMap().get(obj.getVDS_CTLR_NMBR());
                if (ctlr == null) {
                    log.error("VdsCtlrService.loadVdsDtct.UNKNOWN VDS Detector: {}", obj.toString());
                }
                else {
                    obj.getClct().setVDS_DTCT_NMBR(obj.getVDS_DTCT_NMBR());
                    String DETECT_LANE = Integer.toString(obj.getDETECT_LANE());
                    ctlr.getVdsDtctMap().put(DETECT_LANE, obj);
                }
            }
        }
        catch (Exception e) {
            log.error("VdsCtlrService.loadVdsDtct: Exception: {}", e.toString());
        }

        log.debug("VdsCtlrService.VDS Detector List.................");
        for (Map.Entry<String, TbVdsCtlr> e : AppRepository.getInstance().getCtlrMap().entrySet()) {
            log.debug("VdsCtlrService.VDS Controller: {}", e.getValue().getVDS_CTLR_NMBR());
            for (Map.Entry<String, TbVdsDtct> l : e.getValue().getVdsDtctMap().entrySet()) {
                log.debug("  {}", l.toString());
            }
        }
    }

    public void updateCtlrStts(boolean isRun) {
        String keyData = "unit";
        boolean insHs = false;
        Calendar cal = Calendar.getInstance();
        int min = cal.get(Calendar.MINUTE);
        if ((min % 5) == 0) {
            insHs = true;
            this.sttsMin = min;
        }

        //제어기 상태정보 업데이트
        List<TbVdsCtlrStts> ctlrSttsList = Collections.synchronizedList(new ArrayList<>());
        String UPDT_DT = SysUtils.getSysTime();
        int normal = 0;
        int error  = 0;
        CENTER_VDS_RES_STATE stts = new CENTER_VDS_RES_STATE((byte)0x00);
        for (Map.Entry<String, TbVdsCtlr> e : AppRepository.getInstance().getCtlrMap().entrySet()) {
            TbVdsCtlr vds = e.getValue();
            vds.getStts().setUPDT_DT(UPDT_DT);

            if (isRun && vds.getNetState() >= NET.LOGIN_REQ && vds.getChannel() != null) {
                //제어기 통신정상
                normal++;
                vds.getStts().setCMNC_STTS_CD("CMS0");
            }
            else {
                //제어기 통신이상
                error++;
                vds.getStts().initStts(false);
            }

            voVdsState ss = vds.getVdsStts();
            stts.getObjList().add(ss);

            if (isRun) {
                ctlrSttsList.add(vds.getStts());
            }
            else {
                this.vdsCtlrMapper.updateVdsCtlrStts(vds.getStts());
                if (insHs) {
                    this.vdsCtlrMapper.insertVdsCtlrSttsHs(vds.getStts());
                }
            }
        }

        // 제어기 상태정보 DB 업데이트
        if (ctlrSttsList.size() > 0) {
            DbmsJobProcess dbmsJobProcess = (DbmsJobProcess) AppUtils.getBean(DbmsJobProcess.class);
            dbmsJobProcess.add(new DbmsJobData(DbmsJobType.DATA_TYPE_CTLR_STTS, insHs, ctlrSttsList));
        }

        stts.setTotal((short)(normal+error));
        stts.setError((short)error);
        stts.setNormal((short)normal);
        stts.setModule((short)0);

        /**
         * 운영단말로 상태정보 등을 전송하지 않는다.(웹 기반)
         */
//        // 제어기 상태정보 운영단말 전송
//        CenterCommClientSender notify = (CenterCommClientSender) AppUtils.getBean(CenterCommClientSender.class);
//        notify.run(null, 0, stts.getBuffer());
//
//        // 프로세스 상태정보 운영단말 전송
//        byte db = CENTER_PG_STATE_RES.STATE_ERROR;
//        byte comm = error > 0 ? CENTER_PG_STATE_RES.STATE_ERROR : CENTER_PG_STATE_RES.STATE_NORMAL;
//        byte action = CENTER_PG_STATE_RES.STATE_NORMAL;
//        if (!isRun) {
//            db = CENTER_PG_STATE_RES.STATE_ERROR;
//            comm = CENTER_PG_STATE_RES.STATE_ERROR;
//            action = CENTER_PG_STATE_RES.STATE_ERROR;
//        }
//
//        // 프로그램 상태정보 운영단말 전송
//        CENTER_PG_STATE_RES pgStts = new CENTER_PG_STATE_RES(CENTER_COMM_DEFINE.INT_ID_VDS_SERVER, db, comm, action);
//        notify.run(null, 0, pgStts.getBuffer());

        log.info("VdsCtlrService.updateCtlrStts: total {}, normal {}, error {}", normal + error, normal, error);
    }

    /**
     * 제어기 동기화 실행
     * 1. 온도정보 요청(0x1E)
     * 2. 제어기 동기화(0x01)
     *   - 제어기 동기화 후 5초 후에 데이터를 요청한다.
     */
    public void requestSynchronization() {
        long curTime = System.currentTimeMillis();
        int min  = (int)(curTime / (1000 * 60) );
        int sec  = (int)(curTime /  1000);

        TbVdsCtlr obj;
        /*
         * send Synchronize
         */
        //byte frameNo;
        for (Map.Entry<String, TbVdsCtlr> e : AppRepository.getInstance().getCtlrMap().entrySet()) {
            obj = e.getValue();
            MDC.put("id", obj.getLogKey());
            if (obj.getChannel() != null) {
                obj.setSyncTime(curTime);
                //frameNo = (byte)((min*60 + sec) / obj.getTRAF_CLCT_CYCL() + 1);
                ByteBuffer sendBuffer = obj.getReqTemperature().getByteBuffer();
                obj.sendData(sendBuffer, 0, "vds_Temperature");
            } else {
                log.info("[{}]. vds_Temperature: Request Failed. Not Connected. {}", obj.getVDS_CTLR_ID(), obj.getVDS_CTLR_IP());
            }
            MDC.remove(obj.getLogKey());
            MDC.clear();
        }

        try {
            Thread.sleep(500);
        } catch (InterruptedException e1) {
            log.error("requestSynchronization: InterruptedException");
        }

        /*
         * send req traffic
         */
        for (Map.Entry<String, TbVdsCtlr> e : AppRepository.getInstance().getCtlrMap().entrySet()) {
            obj = e.getValue();
            MDC.put("id", obj.getLogKey());
            if (obj.getChannel() != null) {
                obj.setSyncTime(curTime);
                ByteBuffer sendBuffer = obj.getReqSynchronize().getByteBuffer();
                obj.sendData(sendBuffer, 0, "vds_Synchronization");
            } else {
                log.info("[{}]. vds_Synchronization: Request Failed. Not Connected. {}", obj.getVDS_CTLR_ID(), obj.getVDS_CTLR_IP());
            }
            MDC.remove(obj.getLogKey());
            MDC.clear();
        }
    }

    /**
     * 교통 데이터 요청
     * - 제어기 동기화 명령 후 5초 지난 후에 실행
     */
    public void requestData() {
        TbVdsCtlr obj;
        for (Map.Entry<String, TbVdsCtlr> e : AppRepository.getInstance().getCtlrMap().entrySet()) {
            obj = e.getValue();
            MDC.put("id", obj.getLogKey());
            if (obj.getChannel() != null) {
                ByteBuffer sendBuffer = obj.getReqData().getByteBuffer();
                obj.sendData(sendBuffer, 0, "vds_Data");
            } else {
                log.info("[{}]. vds_Data: Request Failed. Not Connected. {}", obj.getVDS_CTLR_ID(), obj.getVDS_CTLR_IP());
            }
            MDC.remove(obj.getLogKey());
            MDC.clear();
        }
    }

    /**
     * VDS 제어기 RTC 전송
     */
    public void requestRTC() {
        TbVdsCtlr obj;
        for (Map.Entry<String, TbVdsCtlr> e : AppRepository.getInstance().getCtlrMap().entrySet()) {
            obj = e.getValue();
            MDC.put("id", obj.getLogKey());
            if (obj.getChannel() != null) {
                VdsReqRTC reqRTC = new VdsReqRTC((short)obj.getGROUP_NO(), (short)obj.getVDS_CTLR_LOCAL_NO());
                reqRTC.makeCRC();
                ByteBuffer sendBuffer = reqRTC.getByteBuffer();
                obj.sendData(sendBuffer, 0, "vds_RTC");
            } else {
                log.info("[{}]. vds_RTC: Request Failed. Not Connected. {}", obj.getVDS_CTLR_ID(), obj.getVDS_CTLR_IP());
            }
            MDC.remove(obj.getLogKey());
            MDC.clear();
        }
    }
}
