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

import com.its.vds.domain.NET;
import com.its.vds.dto.TbVdsCtlrDto;
import com.its.vds.xnettcp.vds.protocol.impl.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;

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

@Slf4j
@RequiredArgsConstructor
public class TcpServerSendDataProcess {

    private final TbVdsCtlrDto vdsObj;

    private final LinkedBlockingQueue<TcpServerSendData> reqCommandBlockingQueue = new LinkedBlockingQueue<>(100);
    private final ThreadPoolExecutor taskExecutor = (ThreadPoolExecutor)Executors.newFixedThreadPool(1);

    public void run() {
        log.info("TcpServerReqDataProcess[{}].run: Start.", this.vdsObj.getVdsCtlrNmbr());
        this.taskExecutor.execute(() -> {
            boolean isRunning = true;
            while (isRunning) {
                try {
                    TcpServerSendData reqData = this.reqCommandBlockingQueue.take();
                    process(reqData);
                }
                catch (InterruptedException e) {
                    log.error("TcpServerReqDataProcess.Task: Exception: {}", e.getMessage(), e);
                    Thread.currentThread().interrupt();
                    isRunning = false;
                }
            }
        });
    }

    public void process(TcpServerSendData data) {

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

        boolean isConnected = (this.vdsObj.getNetState() > NET.CLOSED && this.vdsObj.getChannel() != null);
        boolean isRequested = false;
        if (data.elapsed() > 500) {
            log.info("TcpServerReqDataProcess.process: VDS {}, command({}), connected({}), Elapsed: {} ms.", this.vdsObj.getVdsCtlrNmbr(), data.getOpCode(), isConnected, data.elapsed());
        }

        boolean result = true;
        if (isConnected) {
            try {
                switch (data.getOpCode()) {
                    case OP_VDS_DATA_VOLUME:
                        VdsReqDataVolume reqStatus = new VdsReqDataVolume(this.vdsObj);
                        result = this.vdsObj.sendData(reqStatus, 1);
                        break;
                    case OP_VDS_DATA_VEHICLE:
                        VdsReqDataVehicle reqPowerModuleStatus = new VdsReqDataVehicle(this.vdsObj);
                        result = this.vdsObj.sendData(reqPowerModuleStatus, 1);
                        break;
                    case OP_VDS_STATUS_VALIDATION:
                        VdsReqStatusValidation reqDisplayModuleStatus = new VdsReqStatusValidation(this.vdsObj);
                        result = this.vdsObj.sendData(reqDisplayModuleStatus, 1);
                        break;
                    case OP_VDS_DATA_IMAGE:
                        VdsReqDataImage reqParameter = new VdsReqDataImage(this.vdsObj);
                        result = this.vdsObj.sendData(reqParameter, 1);
                        break;
                    case OP_VDS_STATUS_INITIALIZE:
                        VdsReqStatusInitialize reqUploadCurrentDisplayForm = new VdsReqStatusInitialize(this.vdsObj);
                        result = this.vdsObj.sendData(reqUploadCurrentDisplayForm, 1);
                        break;
                    case OP_VDS_STATUS_TEMPERATURE:
                        VdsReqStatusTemperature reqDisplayDefaultForm = new VdsReqStatusTemperature(this.vdsObj);
                        result = this.vdsObj.sendData(reqDisplayDefaultForm, 1);
                        break;
                    case OP_VDS_CHECK_ONLINE:
                        VdsReqCheckOnline reqBlank = new VdsReqCheckOnline(this.vdsObj);
                        result = this.vdsObj.sendData(reqBlank, 1);
                        break;
                    case OP_VDS_CHECK_MEMORY:
                        VdsReqCheckMemory reqDisplayFormId = new VdsReqCheckMemory(this.vdsObj, (Integer)data.getData());
                        result = this.vdsObj.sendData(reqDisplayFormId, 1);
                        break;
                    case OP_VDS_PARAM_UPLOADING:
                        VdsReqParamUploading reqUploadScheduleFrom = new VdsReqParamUploading(this.vdsObj);
                        result = this.vdsObj.sendData(reqUploadScheduleFrom, 1);
                        break;

                    case OP_VDS_DATA_TRAFFIC:
                        if (data.getData() == null) {
                            //this.vdsObj.downloadSymbLib();
                        }
                        break;
                    case OP_VDS_DATA_LENGTH:
                        VdsReqDataLength statusControl = (VdsReqDataLength) data.getData();
                        result = this.vdsObj.sendData(statusControl, 1);
                        break;
                    default:
                        break;
                }
            } catch (Exception e) {
                log.error("TcpServerReqDataProcess.process: VDS {}, Exception: {}", this.vdsObj.getVdsCtlrNmbr(), e.toString());
            }
        }
        if (!result) {
            log.info("TcpServerReqDataProcess.process: VDS {}, request failed. command({}), connected({}), Requested({}).", this.vdsObj.getVdsCtlrNmbr(), data.getOpCode(), isConnected, isRequested);
        }

        MDC.remove(this.vdsObj.getLogKey());
        MDC.clear();
    }

    public boolean add(TcpServerSendData data) {
        boolean result = true;
        try {
            //offer => full -> return
            //add   => full -> wait
            //큐가 차더라도 바로 리턴함.
            if (!this.reqCommandBlockingQueue.offer(data)) {
                log.error("TcpServerReqDataProcess[{}].add: QueueFull: {}", this.vdsObj.getVdsCtlrNmbr(), this.reqCommandBlockingQueue.size());
                result = false;
            }
        } catch (Exception e) {
            log.error("TcpServerReqDataProcess[{}].add: QueueError: {}", this.vdsObj.getVdsCtlrNmbr(), e.getMessage());
            result = false;
        }
        return result;
    }

}
