package com.its.dsrc.entity;

import com.beanit.asn1dsrc.dsrc.C2CAuthenticatedMessage;
import com.beanit.asn1dsrc.dsrc.HeaderOptions;
import com.beanit.asn1dsrc.dsrc.Login;
import com.beanit.asn1dsrc.dsrc.PDUs;
import com.beanit.asn1dsrc.util.DsrcAsn1Sequence;
import com.its.app.utils.SysUtils;
import com.its.dsrc.vo.NET;
import com.its.dsrc.xnettcp.dsrc.task.DsrcAsn1TimeoutTask;
import io.netty.channel.Channel;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;

import java.net.InetSocketAddress;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Getter
@Setter
@ToString
public class TbRseCtlr {

	private int    index;
	private String RSE_CTLR_NMBR;
	private String RSE_CTLR_ID;
	private String RSE_NM;
	private String RSE_CTLR_IP;
	private int RSE_CTLR_PORT;
	private int RSE_CTLR_LOCAL_NO;
	private String RSE_TYPE_CD;
	private String RSE_MCU_ID;
	private String LOG_CNNC_ID;
	private String LOG_CNNC_PW;
	private int    MNFC_CMPY_CD;
	private int    CLCT_ABNR_BASI;
	private String DEL_YN;

	private TbRseCtlrStts 			stts;
	private int 			  netState;
	private boolean           isDupCon;
	private boolean           isDupLogin;
	private String 		      dstIpAddr;
	private Channel           channel;
	private Channel           dupChannel;
	private InetSocketAddress cliReq;
	private long              syncTime;
	private String            LOG_ID;
	private DsrcAsn1Sequence seq;
	private HeaderOptions headerOptions = null;
	private Login login = null;

	private ConcurrentHashMap<Integer, C2CAuthenticatedMessage> registeredCommands = null;
	private ConcurrentHashMap<Integer, Timer> registeredCommandsTimer = null;
	private ConcurrentHashMap<Integer, TbRseCtrlHs> userCommands = null;
	private ConcurrentHashMap<String, TbRseOffrSect> offrSectMap = null;

	private int connectCount;
	private String connectTm;
	private String disConnectTm;
	public Integer getId() {
		return Integer.parseInt(this.RSE_CTLR_NMBR);
	}

	public TbRseCtlr() {

		this.stts = new TbRseCtlrStts();
		this.seq  = new DsrcAsn1Sequence();
		this.registeredCommands = new ConcurrentHashMap<Integer, C2CAuthenticatedMessage>();
		this.registeredCommandsTimer = new ConcurrentHashMap<Integer, Timer>();
		this.userCommands = new ConcurrentHashMap<Integer, TbRseCtrlHs>();
		this.offrSectMap = new ConcurrentHashMap<String, TbRseOffrSect>();

		this.connectCount = 0;
		this.connectTm = "";
		this.disConnectTm = "";

		initNet();
	}

	public String getID() {
		return this.RSE_CTLR_NMBR;
	}
	public String getRSE_ID() {
		return this.RSE_CTLR_ID;
	}
	public String getLogKey() {
		return this.RSE_CTLR_ID;
	}
	public void resetConnectCount() {
		if (this.netState == com.its.dsrc.vo.NET.CLOSED)
			this.connectCount = 0;
		else
			this.connectCount = 1;
	}
	public void setConnectTm() {
		this.connectTm = SysUtils.getSysTimeStr();
		this.connectCount++;
	}
	public void setDisConnectTm() {
		this.disConnectTm = SysUtils.getSysTimeStr();
	}

	void initNet() {
		this.netState   = com.its.dsrc.vo.NET.CLOSED;
		this.isDupCon   = false;
		this.isDupLogin = false;
		this.dstIpAddr  = "";
		this.channel    = null;
		this.dupChannel = null;
		this.cliReq     = null;
		this.syncTime   = 0;
	}

	public synchronized void channelLogin(Channel channel, HeaderOptions headerOptions, Login login) {
		this.channel = channel;
		this.headerOptions = headerOptions;
		this.login = login;

		this.registeredCommands.clear();
		for (Map.Entry<Integer, Timer> e : this.registeredCommandsTimer.entrySet()) {
			Timer task = e.getValue();
			task.cancel();
		}
		this.registeredCommandsTimer.clear();

		this.netState = com.its.dsrc.vo.NET.LOGINED;
		setConnectTm();
	}

	public synchronized void channelLogout() {
		this.netState = NET.CLOSED;
		this.registeredCommands.clear();
		this.registeredCommandsTimer.clear();
		this.headerOptions = null;
		this.login = null;
		this.channel = null;
		getStts().initError();
		setDisConnectTm();
	}

	public synchronized boolean addUserCommands(int packetNmbr, TbRseCtrlHs command) {

		this.userCommands.put(Integer.valueOf(packetNmbr), command);
		return addCommandTimer(packetNmbr);
	}

	public synchronized boolean removeUserCommands(int packetNmbr) {

		TbRseCtrlHs command = this.userCommands.get(Integer.valueOf(packetNmbr));
		if (command != null) {
			this.userCommands.remove(Integer.valueOf(packetNmbr));
			return true;
		}
		return false;
	}

	public synchronized TbRseCtrlHs getUserCommands(int packetNmbr) {

		TbRseCtrlHs command = this.userCommands.get(Integer.valueOf(packetNmbr));
		return command;
	}

	private boolean addCommandTimer(int packetNmbr) {

		long timeoutSec = this.login.getDatexLoginResponseTimeOutQty().value.longValue();
		if (timeoutSec == 0L || timeoutSec > 30L) {
			timeoutSec = 30L;
		}
		if (timeoutSec < 5L)
			timeoutSec = 5L;

		timeoutSec = 1000L * timeoutSec;
		Timer timer = new Timer();
		timer.schedule((TimerTask)new DsrcAsn1TimeoutTask(this, packetNmbr), timeoutSec);
		this.registeredCommandsTimer.put(Integer.valueOf(packetNmbr), timer);

		log.info("addCommandTimer: [{}], packetNmbr: {}, timeoutSec: {} ms, {}", this.RSE_CTLR_NMBR, packetNmbr, timeoutSec, timer);
		return true;
	}

	public synchronized boolean putRegisteredCommands(C2CAuthenticatedMessage c2c) {

		if (this.login == null || this.headerOptions == null) {
			return false;
		}
		PDUs pdUs = c2c.getPdu();
		if (pdUs.getSubscription() == null && pdUs.getPublication() == null) {
			// Subscription 또는 Publication 요청에 대해서만 타임아웃 체크
			return false;
		}

		int packetNmbr = c2c.getDatexDataPacketNumber().value.intValue();
		this.registeredCommands.put(Integer.valueOf(packetNmbr), c2c);
		return addCommandTimer(packetNmbr);
	}

	public synchronized boolean removeRegisteredCommandsTimer(int packetNmbr) {

		Timer timer = this.registeredCommandsTimer.get(Integer.valueOf(packetNmbr));
		if (timer != null) {
			this.registeredCommandsTimer.remove(Integer.valueOf(packetNmbr));
		}
		return true;
	}

	public synchronized boolean removeRegisteredCommands(int packetNmbr, boolean cancelTimer) {

		Timer timer = this.registeredCommandsTimer.get(Integer.valueOf(packetNmbr));
		if (timer != null) {
			if (cancelTimer)
				timer.cancel();
			this.registeredCommandsTimer.remove(Integer.valueOf(packetNmbr));
		}

		C2CAuthenticatedMessage c2c = this.registeredCommands.get(Integer.valueOf(packetNmbr));
		if (c2c != null) {
			this.registeredCommands.remove(Integer.valueOf(packetNmbr));
		}
		return false;
	}

	public synchronized C2CAuthenticatedMessage getRegisteredCommands(int packetNmbr) {
		C2CAuthenticatedMessage c2c = this.registeredCommands.get(Integer.valueOf(packetNmbr));
		return c2c;
	}

}
