package com.its.vms.xnettcp.vms.process;

import com.its.vms.config.CommunicationConfig;
import com.its.vms.dto.TbVmsCtlrDto;
import com.its.vms.xnettcp.vms.handler.VmsServerIdleStateHandler;
import com.its.vms.xnettcp.vms.process.response.VmsResponse;
import com.its.vms.xnettcp.vms.process.response.impl.*;
import com.its.vms.xnettcp.vms.protocol.VmsFramePacket;
import com.its.vms.xnettcp.vms.protocol.enums.eVmsOpCode;
import com.its.vms.xnettcp.vms.protocol.enums.eVmsProtocolVersion;
import com.its.vms.xnettcp.vms.protocol.impl.VmsResFramePacket;
import com.its.vms.xnettcp.vms.protocol.impl.dle.VmsDleFramePacket;
import com.its.vms.xnettcp.vms.protocol.impl.std.VmsStdFramePacket;
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();
            TbVmsCtlrDto vmsObj = data.getObj();
            VmsResFramePacket resFramePacket = (VmsResFramePacket)data.getData();

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

            VmsFramePacket packet;
            if (vmsObj.getProtocolVer() == eVmsProtocolVersion.PROTOCOL_ANDONG.getValue()) {
                packet = new VmsStdFramePacket(resFramePacket.getPacket(), resFramePacket.getMsgLen());
            }
            else if (vmsObj.getProtocolVer() == eVmsProtocolVersion.PROTOCOL_EX_FIGURE.getValue()) {
                packet = new VmsDleFramePacket(resFramePacket.getPacket(), resFramePacket.getMsgLen());
            }
            else {
                packet = new VmsDleFramePacket(resFramePacket.getPacket(), resFramePacket.getMsgLen());
            }

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

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

            switch(opCode) {
                case OP_VMS_STATUS_REQ:
                    response = new VmsResStatus(vmsObj, packet);
                    break;
                case OP_VMS_POWER_MODULE_STATUS_REQ:
                    response = new VmsResPowerModuleStatus(vmsObj, packet);
                    break;
                case OP_VMS_DISPLAY_MODULE_STATUS_REQ:
                    response = new VmsResDisplayModuleStatus(vmsObj, packet);
                    break;
                case OP_VMS_PARAMETER_REQ:
                    response = new VmsResParameter(vmsObj, packet);
                    break;
                case OP_VMS_DOWNLOAD_FORM:
                    response = new VmsResDownloadForm(vmsObj, packet);
                    break;
                case OP_VMS_DOWNLOAD_FORM_SCHEDULE:
                    response = new VmsResDownloadFormSchedule(vmsObj, packet);
                    break;
                case OP_VMS_BLANK:
                    response = new VmsResBlank(vmsObj, packet);
                    break;
                case OP_VMS_STATUS_CONTROL:
                    response = new VmsResStatusControl(vmsObj, packet);
                    break;
                case OP_VMS_DATA_DOWNLOAD:
                    response = new VmsResDataDownload(vmsObj, packet);
                    break;
                case OP_VMS_FORM_DATA_DISPLAY:
                    response = new VmsResFormDataDisplay(vmsObj, packet);
                    break;
                case OP_VMS_DATA_UPLOAD:
                    response = new VmsResDataUpload(vmsObj, packet);
                    break;
                case OP_VMS_STOP_IMAGE_REQ:
                    response = new VmsResStopImage(vmsObj, packet);
                    break;
                case OP_VMS_UPLOAD_CURRENT_DISPLAY_FORM:
                    response = new VmsResUploadCurrentDisplayForm(vmsObj, packet);
                    break;
                case OP_VMS_DISPLAY_DEFAULT_FORM:
                    response = new VmsResDisplayDefaultForm(vmsObj, packet);
                    break;
                case OP_VMS_DISPLAY_FORM_ID:
                    response = new VmsResDisplayFormId(vmsObj, packet);
                    break;
                case OP_VMS_UPLOAD_FORM_SCHEDULE:
                    response = new VmsResUploadFormSchedule(vmsObj, packet);
                    break;
                default:
                    break;
            }

            if (response != null) {
                if (!response.process()) {
                    log.error("TcpServerRecvDataProcess.process: response error. VMS {}, {}, will be closed.", vmsObj.getVmsCtlrNmbr(), opCodeValue);
                    VmsServerIdleStateHandler.disconnectChannel(vmsObj, ctx.channel());
                }
            }
            else {
                log.error("TcpServerRecvDataProcess.process: Unknown Op Code Packet: VMS {}, {}, will be closed.", vmsObj.getVmsCtlrNmbr(), opCodeValue);
                VmsServerIdleStateHandler.disconnectChannel(vmsObj, ctx.channel());
            }

            MDC.remove(vmsObj.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);
        }
    }

}
