package com.its.dsrc.xnettcp.dsrc.process.response;

import com.beanit.asn1dsrc.dsrc.Accept;
import com.beanit.asn1dsrc.dsrc.C2CAuthenticatedMessage;
import com.beanit.asn1dsrc.dsrc.PDUs;
import com.its.app.AppUtils;
import com.its.app.utils.NettyUtils;
import com.its.dsrc.config.RunningConfig;
import com.its.dsrc.dao.mapper.RseCtlrMapper;
import com.its.dsrc.entity.TbRseCtlr;
import com.its.dsrc.entity.TbRseCtlrCnncHs;
import com.its.dsrc.entity.TbRseCtrlHs;
import com.its.dsrc.process.DbmsData;
import com.its.dsrc.process.DbmsDataProcess;
import com.its.dsrc.process.DbmsDataType;
import com.its.dsrc.vo.NET;
import com.its.dsrc.xnettcp.dsrc.process.service.LoginDeviceService;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class AcceptResponse implements DsrcAsn1Response {
    private TbRseCtlr obj;
    private ChannelHandlerContext ctx;
    private C2CAuthenticatedMessage c2c;
    private DbmsDataProcess dbmsDataProcess;

    public AcceptResponse(TbRseCtlr obj, ChannelHandlerContext ctx, C2CAuthenticatedMessage c2c) {
        this.obj = obj;
        this.ctx = ctx;
        this.c2c = c2c;
        this.dbmsDataProcess = (DbmsDataProcess) AppUtils.getBean(DbmsDataProcess.class);
    }

    @Override
    public boolean response(RunningConfig runningConfig) {
        String ipAddress = NettyUtils.getRemoteIpAddress(this.ctx.channel());
        log.info("AcceptResponse.response: {}", ipAddress);

        PDUs pdus = this.c2c.getPdu();
        Accept accept = pdus.getAccept();
        if (accept == null) {
            log.error("AcceptResponse.response: {}, accept data null", ipAddress);
            return false;
        }

        TbRseCtrlHs command = null;
        long dataPacketNmbr = c2c.getDatexDataPacketNumber().value.longValue();
        Long acceptPacketNmbr = accept.getDatexAcceptPacketNbr().value.longValue();
        Accept.DatexAcceptType acceptType = accept.getDatexAcceptType();
        if (acceptType.getLogIn() != null) {
            // Login = 1, 여기는 들어오지 않는다.
            log.info("AcceptResponse.response: {}. Login: {}, {}", ipAddress, dataPacketNmbr, acceptPacketNmbr);
            loginAccept(runningConfig);
        }
        else if (acceptType.getSingleSubscription() != null) {
            // SingleSubscription = 2
            // Publication = 3
            // 제어명령에 대한 응답, 제어목록에서 삭제하고 이력으로 저장한다.
            this.obj.removeRegisteredCommands(acceptPacketNmbr, true);
            command = this.obj.getUserCommands(acceptPacketNmbr);
            log.info("AcceptResponse.response: {}. SingleSubscription: {}, {}", ipAddress, dataPacketNmbr, acceptPacketNmbr);
        }
        else if (acceptType.getPublication() != null) {
            // SingleSubscription = 2
            // Publication = 3
            // 제어명령에 대한 응답, 제어목록에서 삭제하고 이력으로 저장한다.
            this.obj.removeRegisteredCommands(acceptPacketNmbr, true);
            command = this.obj.getUserCommands(acceptPacketNmbr);
            log.info("AcceptResponse.response: {}. Publication: {}, {}", ipAddress, dataPacketNmbr, acceptPacketNmbr);
        }
        else if (acceptType.getRegisteredSubscription() != null) {
            // RegisteredSubscription = 4
            // 요청에 대한 응답, 제어목록에서 삭제한다.
            // 상태정보요청: 269677936
            if (269677936 == acceptType.getRegisteredSubscription().longValue()) {
                if (acceptPacketNmbr == 1) {
                    /**
                     * requestObuGatherInfo 에 대한 Accept 응답
                     */
                    LoginDeviceService.getInstance().requestObuStatusInfo(this.obj, this.ctx.channel());
                }
                else if (acceptPacketNmbr == 2) {
                    /**
                     * requestObuStatusInfo 에 대한 Accept 응답
                     */
                    //LoginDeviceService.getInstance().requestObuStatusInfo(this.obj, this.ctx.channel());
                }
                //log.info("AcceptResponse.response: {}. 상태정보요청응답: {}, {}", ipAddress, dataPacketNmbr, acceptPacketNmbr);
            }
            this.obj.removeRegisteredCommands(acceptPacketNmbr, true);
            log.info("AcceptResponse.response: {}. RegisteredSubscription: {}, {}, {}", ipAddress, dataPacketNmbr, acceptPacketNmbr, acceptType.getRegisteredSubscription().longValue());
        }

        if (command != null) {
            log.info("AcceptResponse.response: {}. Accept User Command: {}, {}", ipAddress, dataPacketNmbr, acceptPacketNmbr);
            this.obj.removeUserCommands(acceptPacketNmbr);
            // TODO: 제어명령 성공 전송(성공)
            command.setRspsType("0");  // 명령 성공으로 설정
            RseCtlrMapper mapper = (RseCtlrMapper) AppUtils.getBean(RseCtlrMapper.class);
            mapper.updateRseCtrlHs(command);
        }
        return true;
    }

    /**
     * 클라이언트 모드 동작시 로그인 요청에 대한 응답으로 로그인 처리
     * @param runningConfig
     */
    private void loginAccept(RunningConfig runningConfig) {
        if (this.obj.getNetState() > NET.LOGIN_REQ && this.obj.getChannel() != null) {
            log.warn("AcceptResponse.loginAccept: {}, already login. older channel will be closed.", this.obj.getMcuID());
            this.obj.setDupLogin(true);
            this.obj.setDupChannel(this.obj.getChannel());
//            DsrcAsn1ServerIdleStatePacketHandler.disconnectChannel(this.obj, this.obj.getDupChannel());
        }

        this.obj.channelLoginInit();

        TbRseCtlrCnncHs voLog = new TbRseCtlrCnncHs();
        voLog.setRSE_CTLR_NMBR(this.obj.getID());
        voLog.setLOG_ADDRESS(this.obj.getRSE_CTLR_IP());
        voLog.setLoginInfo(c2c);
        voLog.setLOG_ID(this.obj.getMcuID());
        voLog.setLOG_TYPE(Integer.toString(TbRseCtlrCnncHs.LOG_TYPE_LOGIN));
        this.obj.setLOG_ID(voLog.getLOG_ID());
        if (this.obj.isDupLogin()) {
            // 중복 접속(이미 통신중인 세션이 존재함)
            voLog.setLOG_TYPE(Integer.toString(TbRseCtlrCnncHs.LOG_TYPE_DUP_LOGIN));
        }
        this.dbmsDataProcess.add(new DbmsData(DbmsDataType.DBMS_DATA_LOG_HS, false, voLog));

        this.obj.getSeq().resetValue();
        //this.obj.setLogin(login);
        // 로그인이 성공하면 제어기에 정보를 요청한다.
        log.info("AcceptResponse.loginAccept: {}, Request Subscriptions", obj.getMcuID());

        /**
         * OBU Gather Info request
         */
        LoginDeviceService.getInstance().requestObuGatherInfo(this.obj, this.ctx.channel());
    }
}
