package com.its.vds.xnettcp.vds.process;

import com.its.vds.config.CommunicationConfig;
import com.its.vds.dto.TbVdsCtlrDto;
import com.its.vds.xnettcp.vds.handler.VdsServerIdleStateHandler;
import com.its.vds.xnettcp.vds.process.response.VdsResponse;
import com.its.vds.xnettcp.vds.process.response.impl.*;
import com.its.vds.xnettcp.vds.protocol.VdsFramePacket;
import com.its.vds.xnettcp.vds.protocol.enums.eVdsOpCode;
import com.its.vds.xnettcp.vds.protocol.impl.VdsResFramePacket;
import com.its.vds.xnettcp.vds.protocol.impl.andong.VdsAndongFramePacket;
import io.netty.channel.ChannelHandlerContext;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.stereotype.Service;

import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;

@Slf4j
@RequiredArgsConstructor
@Service
public class TcpServerRecvDataProcess {

    private final CommunicationConfig communicationConfig;
    private final TcpServerRecvDataTask serverDataTask;

    public static final LinkedBlockingQueue<TcpServerRecvData> SERVER_DATA_QUEUE = new LinkedBlockingQueue<>(1000);
    private final ThreadPoolExecutor taskExecutor = (ThreadPoolExecutor)Executors.newFixedThreadPool(1);
    private int maxCord = Runtime.getRuntime().availableProcessors();

    public void run() {
        log.info("TcpServerRecvDataProcess.run: Start.");
        if (this.maxCord < 8) {
            this.maxCord = 8;
        }
        int executePool = this.maxCord;
        for (int ii = 0; ii < executePool; ii++) {
            log.info("TcpServerRecvDataProcess.Task: {}", ii);
            this.taskExecutor.execute(() -> {
                boolean isRunning = true;
                while (isRunning) {
                    try {
                        TcpServerRecvData serverData = TcpServerRecvDataProcess.SERVER_DATA_QUEUE.take();
                        this.serverDataTask.run(this, serverData);
                    }
                    catch (Exception e) {
                        log.error("TcpServerRecvDataProcess.Task: Exception: {}", e.getMessage());
                        Thread.currentThread().interrupt();
                        isRunning = false;
                    }
                }
            });
        }

        log.info("TcpServerRecvDataProcess.run: ..End.");
    }

    public void process(TcpServerRecvData data) {
        try {
            ChannelHandlerContext ctx = data.getCtx();
            TbVdsCtlrDto vdsObj = data.getObj();
            VdsResFramePacket resFramePacket = (VdsResFramePacket)data.getData();

            MDC.put("id", vdsObj.getLogKey());

            VdsFramePacket packet = new VdsAndongFramePacket(resFramePacket.getPacket(), resFramePacket.getMsgLen());

            String opCodeValue = String.format("0x%02X", packet.getOpCode());

            VdsResponse response = null;
            eVdsOpCode opCode = eVdsOpCode.getValue(packet.getOpCode());
            /**
             * 제어기 명령 타임아웃 타스크 제거
             */
            vdsObj.removeRegisteredCommandsTimer(opCode);

            switch(opCode) {
                case OP_VDS_SYNCHRONIZATION:
                    log.warn("OP_VDS_SYNCHRONIZATION 패킷은 응답이 없는 패킷임... 패킷 수신 오류...");
                    break;
                case OP_VDS_DATA_TRAFFIC:
                    response = new VdsResDataTraffic(vdsObj, packet);
                    break;
                case OP_VDS_DATA_SPEED:
                    response = new VdsResDataSpeed(vdsObj, packet);
                    break;
                case OP_VDS_DATA_LENGTH:
                    response = new VdsResDataLength(vdsObj, packet);
                    break;
                case OP_VDS_DATA_VOLUME:
                    response = new VdsResDataVolume(vdsObj, packet);
                    break;
                case OP_VDS_DATA_IMAGE:
                    response = new VdsResDataImage(vdsObj, packet);
                    break;
                case OP_VDS_DATA_VEHICLE:
                    response = new VdsResDataVehicle(vdsObj, packet);
                    break;

                case OP_VDS_STATUS_VALIDATION:
                    response = new VdsResStatusValidation(vdsObj, packet);
                    break;
                case OP_VDS_STATUS_RESET:
                    response = new VdsResStatusReset(vdsObj, packet);
                    break;
                case OP_VDS_STATUS_INITIALIZE:
                    response = new VdsResStatusInitialize(vdsObj, packet);
                    break;
                case OP_VDS_STATUS_RTC:
                    response = new VdsResStatusRtc(vdsObj, packet);
                    break;
                case OP_VDS_STATUS_TEMPERATURE:
                    response = new VdsResStatusTemperature(vdsObj, packet);
                    break;

                case OP_VDS_PARAM_DOWNLOADING:
                    response = new VdsResParamDownloading(vdsObj, packet);
                    break;
                case OP_VDS_PARAM_UPLOADING:
                    response = new VdsResParamUploading(vdsObj, packet);
                    break;

                case OP_VDS_CHECK_ONLINE:
                    response = new VdsResCheckOnline(vdsObj, packet);
                    break;
                case OP_VDS_CHECK_MEMORY:
                    response = new VdsResCheckMemory(vdsObj, packet);
                    break;
                case OP_VDS_CHECK_ECHO:
                    response = new VdsResCheckEcho(vdsObj, packet);
                    break;
                case OP_VDS_CHECK_SEQUENCE:
                    response = new VdsResCheckSequence(vdsObj, packet);
                    break;
                case OP_VDS_CHECK_VERSION:
                    response = new VdsResCheckVersion(vdsObj, packet);
                    break;

                case OP_VDS_SET_TEMPERATURE:
                    response = new VdsResSetTemperature(vdsObj, packet);
                    break;

                case OP_VDS_ACK:
                    response = new VdsResAck(vdsObj, packet);
                    break;
                case OP_VDS_NAK:
                    response = new VdsResNak(vdsObj, packet);
                    break;
                case OP_VDS_ABORT:
                    response = new VdsResAbort(vdsObj, packet);
                    break;
                default:
                    break;
            }

            if (response != null) {
                if (!response.process()) {
                    log.error("TcpServerRecvDataProcess.process: response error. VDS {}, {}, will be closed.", vdsObj.getVdsCtlrNmbr(), opCodeValue);
                    VdsServerIdleStateHandler.disconnectChannel(vdsObj, ctx.channel());
                }
            }
            else {
                log.error("TcpServerRecvDataProcess.process: Unknown Op Code Packet: VDS {}, {}, will be closed.", vdsObj.getVdsCtlrNmbr(), opCodeValue);
                VdsServerIdleStateHandler.disconnectChannel(vdsObj, ctx.channel());
            }

            MDC.remove(vdsObj.getLogKey());
            MDC.clear();
        } catch (Exception e) {
            log.error("TcpServerRecvDataProcess.process: Exception: {}", e.toString());
        }
    }

    /*
    *  작업큐에 데이터 추가
     */
    public void add(TcpServerRecvData data) {
        try {
            //offer => full -> return
            //add   => full -> wait
            //큐가 차더라도 바로 리턴함.
            if (!SERVER_DATA_QUEUE.offer(data)) {
                log.error("TcpServerRecvDataProcess-QueueFull: {}", SERVER_DATA_QUEUE.size());
            }
        } catch (Exception e) {
            log.error("TcpServerRecvDataProcess-QueueAddError: {}", e.getMessage(), e);
        }
    }

}
