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

import com.beanit.asn1bean.ber.types.BerEnum;
import com.beanit.asn1bean.ber.types.BerInteger;
import com.beanit.asn1bean.ber.types.BerOctetString;
import com.beanit.asn1bean.ber.types.string.BerUTF8String;
import com.beanit.asn1dsrc.dsrc.*;
import com.beanit.asn1dsrc.enums.eAuthInfo;
import com.beanit.asn1dsrc.enums.ePdusPr;
import com.beanit.asn1dsrc.util.DsrcAsn1Utils;
import com.its.app.AppUtils;
import com.its.app.utils.NettyUtils;
import com.its.dsrc.config.RunningConfig;
import com.its.dsrc.entity.TbRseCtlr;
import com.its.dsrc.entity.TbRseCtlrCnncHs;
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.handler.DsrcAsn1ServerIdleStatePacketHandler;
import com.its.dsrc.xnettcp.dsrc.process.service.LoginDeviceService;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;

import java.math.BigInteger;

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

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

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

        PDUs pdus = this.c2c.getPdu();
        eAuthInfo cmd = eAuthInfo.getByValue(this.c2c.getDatexAuthenticationInfoText().value[0]);
        ePdusPr pdu = DsrcAsn1Utils.getPduChoice(pdus);
        if (cmd == null || pdu == null) {
            log.error("LoginResponse.response: {}. AuthInfo: {}, PDUs: {}", ipAddress, cmd, pdu);
            return false;
        }

        log.info("LoginResponse.response: {}. AuthInfo: {}, PDUs: {}", ipAddress, cmd.toString(), pdu.toString());
        if (cmd != eAuthInfo.AI_Login || pdu != ePdusPr.PDUs_PR_login) {
            log.error("LoginResponse.response: {}. LOGIN WAIT, Miss Matched Login Message. will be closed.v {}", ipAddress, cmd);
            return false;
        }

        boolean loginAccept = false;
        int loginResult = TbRseCtlrCnncHs.LOG_TYPE_OTHER;
        Login login = pdus.getLogin();
        PDUs   resPdus = new PDUs();
        byte[] resAcks = new byte[1];

         if (this.obj.getNetState() > NET.LOGIN_REQ && this.obj.getChannel() != null) {
             log.warn("LoginResponse.response: {}. already login. older channel will be closed.", ipAddress);
             this.obj.setDupLogin(true);
             this.obj.setDupChannel(this.obj.getChannel());
             DsrcAsn1ServerIdleStatePacketHandler.disconnectChannel(this.obj, this.obj.getDupChannel());
         }

        int heartbeatSec = login.getDatexLoginHeartbeatDurationMaxQty().value.intValue();
        if (heartbeatSec == 0 || heartbeatSec > 120) {
            heartbeatSec = 120;
        }
        if (heartbeatSec < 30) {
            heartbeatSec = 30;
        }

        //log.info("LoginResponse.response: {}. Login: {}", tcpAddress, login.toString());
        loginResult = checkValidLoginInfo(ipAddress, login);
        if (loginResult == TbRseCtlrCnncHs.LOG_TYPE_LOGIN) {
            loginAccept = true;
            resAcks[0] = (byte)eAuthInfo.AI_Accept.getValue();
            Accept acpt = new Accept();
            Accept.DatexAcceptType acptType = new Accept.DatexAcceptType();
            acptType.setLogIn(login.getDatexLoginEncodingRulesId().getBerObjectIdentifier().get(0));
            acpt.setDatexAcceptPacketNbr(c2c.getDatexDataPacketNumber());
            acpt.setDatexAcceptType(acptType);
            resPdus.setAccept(acpt);
            //log.info("LoginResponse.response: {}. Login Accept: {}, **** Pdus: {}", tcpAddress, acpt.toString(), resPdus.toString());
        }

        TbRseCtlrCnncHs voLog = new TbRseCtlrCnncHs();
        voLog.setRSE_CTLR_NMBR(this.obj.getID());
        voLog.setLOG_ADDRESS(ipAddress);
        voLog.setLoginInfo(c2c);
        voLog.setLOG_ID(new String(login.getDatexLoginUserNameTxt().value));
        voLog.setLOG_TYPE(Integer.toString(loginResult));
        this.obj.setLOG_ID(voLog.getLOG_ID());

        if (loginAccept) {
            //obj.channelLogin(c2c.getOptions(), login);
            // 헤더옵션이 널값으로 수신됨
            //HeaderOptions loginHdr = DsrcAsn1Utils.getHeaderOptions(
            //        login.getDatexSenderTxt(),
            //        login.getDatexSenderTxt(),
            //        login.getDatexDestinationTxt(),
            //        login.getDatexDestinationTxt());
            HeaderOptions loginHdr = DsrcAsn1Utils.getHeaderOptions(
                    new BerUTF8String("DSRC Main Control Unit"),
                    new BerUTF8String("DSRC Main Control Unit"),
                    new BerUTF8String("DSRC Center Unit"),
                    new BerUTF8String("DSRC Center Unit"));
            this.obj.channelLogin(this.ctx.channel(), loginHdr, login);
        }
        else {
            resAcks[0] = (byte)eAuthInfo.AI_Reject.getValue();
            Reject rjt = new Reject();
            RejectType rjtType = new RejectType();
            rjtType.setDatexRejectLoginCd(new BerEnum((long)0));
            rjt.setDatexRejectPacketNbr(c2c.getDatexDataPacketNumber());
            rjt.setDatexRejectType(rjtType);
            resPdus.setAccept(null);
            resPdus.setReject(rjt);
            log.error("LoginResponse.response: {}. Login Reject: {}, **** Pdus: {}", ipAddress, rjt.toString(), resPdus.toString());
        }

        //HeaderOptions hdrOpts = DsrcAsn1Utils.getHeaderOptions(
        //        login.getDatexDestinationTxt(), //this.dsrcServerConfig.getSenderText(),
        //        login.getDatexDestinationTxt(), //this.dsrcServerConfig.getSenderText(), //NettyUtils.getLocalIpAddress(ctx.channel()),
        //        login.getDatexSenderTxt(),// c2c.getOptions().getDatexSenderText(),
        //        login.getDatexSenderTxt());// c2c.getOptions().getDatexSenderAddressLocation());
        HeaderOptions hdrOpts = DsrcAsn1Utils.getHeaderOptions(
                new BerUTF8String("DSRC Center Unit"),
                new BerUTF8String("DSRC Center Unit"),
                new BerUTF8String("DSRC Main Control Unit"),
                new BerUTF8String("DSRC Main Control Unit"));

        //log.debug("LoginResponse.response: {}, hdrOpts: {}", tcpAddress, hdrOpts.toString());

        C2CAuthenticatedMessage resC2c = new C2CAuthenticatedMessage();
        resC2c.setDatexAuthenticationInfoText(new BerOctetString(resAcks));
        resC2c.setDatexDataPacketNumber(new BerInteger(this.obj.getSeq().nextValue()));
        resC2c.setDatexDataPacketPriorityNumber(new BerInteger(1));
        resC2c.setOptions(hdrOpts);
        resC2c.setPdu(resPdus);

        //log.info("LoginResponse.response: {}. C2CAuth: {}", tcpAddress, resC2c.toString());

        ChannelFuture f = this.ctx.channel().writeAndFlush(resC2c);
        f.awaitUninterruptibly();
        if (f.isDone() || f.isSuccess()) {
            log.info("LoginResponse.response: {}. Login Result, packet send OK.", ipAddress);
        } else {
            log.error("LoginResponse.response: {}. Login Result, packet send Failed.", ipAddress);
            loginAccept = false;
        }

        if (loginAccept == false) {
            log.error("LoginResponse.response: {}, Remote: {}. Login Reject. tcp channel will be closed.", ipAddress, ipAddress);
            voLog.setLOG_TYPE(Integer.toString(TbRseCtlrCnncHs.LOG_TYPE_OTHER));
            this.obj.channelLogout();
            this.ctx.channel().disconnect();
            this.ctx.channel().close();
        }

        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));

        if (loginAccept) {
            this.obj.getSeq().resetValue();
            this.obj.setLogin(login);
            // 로그인이 성공하면 제어기에 정보를 요청한다.
            log.info("LoginResponse.response: {}, Request Subscriptions", ipAddress);
            LoginDeviceService.getInstance().initLoginDevice(
                    this.obj,
                    this.ctx.channel(),
                    this.runningConfig.isPublicationNonCryptObu(),
                    this.runningConfig.isSubscriptionStatus(),
                    this.runningConfig.isSubscriptionTraffic(),
                    this.runningConfig.getSubscriptionStatusCycle(),
                    this.runningConfig.getPublicationAntenna());
        }
        return loginAccept;
    }

    private int checkValidLoginInfo(String ipAddress, Login login) {
        String senderTxt           = new String(login.getDatexSenderTxt().value);
        String destinationTxt      = new String(login.getDatexDestinationTxt().value);
        String loginUserName       = new String(login.getDatexLoginUserNameTxt().value);
        String loginUserPswd       = new String(login.getDatexLoginPasswordTxt().value);
        BigInteger durationMaxQty  = login.getDatexLoginHeartbeatDurationMaxQty().value;
        BigInteger loginInitCd     = login.getDatexLoginInitiatorCd().value;
        BigInteger responseTimeout = login.getDatexLoginResponseTimeOutQty().value;
        BigInteger datagramSize    = login.getDatexLoginDatagramSizeQty().value;

        /*
       	datexSenderTxt: DSRC Main Control Unit,
        datexDestinationTxt: DSRC Center Unit,
        datexLoginUserNameTxt: 4150,
        datexLoginPasswordTxt: 41544D535F525345,
        datexLoginEncodingRulesId: {
            2.1.1
        },
        datexLoginHeartbeatDurationMaxQty: 120,
        datexLoginResponseTimeOutQty: 3,
        datexLoginInitiatorCd: 1,
        datexLoginDatagramSizeQty: 2048
         */
        /*
        senderTxt: DSRC Main Control Unit,
        destinationTxt: DSRC Center Unit,
        loginUserName: AP,
        loginUserPswd: ATMS_RSE, durationMaxQty: 120, loginInitCd: 1, responseTimeout: 3, datagramSize: 2048
         */
        log.info("LoginResponse.response: {}. senderTxt: {}, destinationTxt: {}, loginUserName: {}, loginUserPswd: {}, durationMaxQty: {}, loginInitCd: {}, responseTimeout: {}, datagramSize: {}",
                ipAddress, senderTxt, destinationTxt, loginUserName, loginUserPswd, durationMaxQty, loginInitCd, responseTimeout, datagramSize);

        return TbRseCtlrCnncHs.LOG_TYPE_LOGIN;
    }

}
