package com.its.pis.entity;

import com.its.app.utils.SysUtils;
import com.its.pis.domain.NET;
import com.its.pis.xnettcp.vds.handler.VdsTcpClientIdleHandler;
import com.its.pis.xnettcp.vds.protocol.*;
import com.its.pis.xnetudp.protocol.voVdsState;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.*;

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

	private int    index;
	private String VDS_CTLR_NMBR;
	private String VDS_CTLR_ID;
	private String VDS_NM;
	private String VDS_TYPE_CD;
	private String VDS_CTLR_IP;
	private int    VDS_CTLR_PORT;
	private int    GROUP_NO;
	private int    VDS_CTLR_LOCAL_NO;
	private int    FAN_MODE;
	private int    FAN_RUN_TMPR;
	private int    HETR_MODE;
	private int    HETR_RUN_TMPR;
	private int    DETECT_LANES;
	private int    TRAF_CLCT_CYCL;
	private int    STTS_CLCT_CYCL;
	private String VALD_YN;
	private String DEL_YN;

	private Map<String, TbVdsDtct> 	vdsDtctMap;
	private int 			 		netState;
	private boolean       			isDupCon;
	private boolean                 isDupLogin;
	private String 		 			dstIpAddr;
	private TbVdsCtlrStts 			stts;
	private Channel 				channel;
	private Channel 				dupChannel;
	private long          			syncTime;
	private InetSocketAddress 		cliReq;

	private int connectCount;
	private String connectTm;
	private String disConnectTm;

	private boolean dump = false;

	private VdsReqSynchronize reqSynchronize = null;
	private VdsReqData reqData = null;
	private VdsReqTemperature reqTemperature = null;
	private VdsReqTraffic reqTraffic = null;

	private boolean requestStopImage = false;
	private List<Channel> requestImageList = new ArrayList<>();
	private int frameNo;
	private int cameraNo;
	private int imageSize;
	private ByteBuffer imageBuffer;

	public Integer getId() {
		return Integer.parseInt(this.VDS_CTLR_NMBR);
	}
	public void initDtctClct() {
		for (Map.Entry<String, TbVdsDtct> e : this.vdsDtctMap.entrySet()) {
			TbVdsDtct tmp = e.getValue();
			tmp.getClct().initVal();
		}
	}

	public void addImageData(byte[] imageData) {
		if (imageData == null) {
			return;
		}

		if (this.imageBuffer == null) {
			this.imageBuffer = ByteBuffer.wrap(imageData);
			imageBuffer.order(VdsProtocol.byteOrder);
		}
		else {
			byte[] prevImage = this.imageBuffer.array();
			this.imageBuffer = ByteBuffer.allocate(prevImage.length + imageData.length);
			this.imageBuffer.put(prevImage);
			this.imageBuffer.put(imageData);
		}
	}
	public ByteBuffer getImageData() {
		return this.imageBuffer;
	}
	public void addImageSize(int imageSize) {
		this.imageSize += imageSize;
	}
	public void setImageSize(int imageSize) {
		this.imageSize = imageSize;
	}
	public int getImageSize() {
		return this.imageSize;
	}
	public int getFrameNo() {
		return this.frameNo;
	}
	public int getCameraNo() {
		return this.cameraNo;
	}
	public List<Channel> getRequestImageList() {
		return this.requestImageList;
	}
	public void setStopImageRequest(Channel channel, int cameraNo, int frameNo) {
		if (!this.requestStopImage) {
			this.requestImageList.clear();
			this.requestImageList = new ArrayList<>();
			//this.cameraNo = cameraNo;
			//this.frameNo = frameNo;
			//this.imageSize = 0;
		}
		// 마지막 요청을 유지하자
		this.imageBuffer = null;
		this.imageSize = 0;
		this.cameraNo = cameraNo;
		this.frameNo = frameNo;
		this.requestImageList.add(channel);
		this.requestStopImage = true;
	}
	public void setStopImageResponse() {
		this.imageBuffer = null;
		this.imageSize = 0;
		this.requestStopImage = false;
		this.cameraNo = 0;
		this.frameNo = 0;
		this.requestImageList.clear();
		this.requestImageList = new ArrayList<>();
	}

	public TbVdsCtlr() {
		this.vdsDtctMap = Collections.synchronizedMap(new HashMap<String, TbVdsDtct>());
		this.stts = new TbVdsCtlrStts();

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

		initNet();
	}

	public String getLogKey() {
		return this.VDS_CTLR_ID;
	}

	public void resetConnectCount() {
		if (this.netState == NET.CLOSED)
			this.connectCount = 0;
		else
			this.connectCount = 1;
	}

	public void setConnectTm() {
		this.connectTm = SysUtils.getSysTimeStrMMDD();
		this.connectCount++;
	}

	public void setDisConnectTm() {
		this.disConnectTm = SysUtils.getSysTimeStrMMDD();
	}

	void initNet() {
		this.netState   = 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 channelOpen(Channel channel) {
		this.netState = NET.LOGIN_REQ;
		this.channel = channel;
		getStts().initStts(true);
		setConnectTm();
	}

	public synchronized void channelLogin(Channel channel) {
		this.netState = NET.LOGINED;
		this.channel = channel;
		getStts().initStts(true);
		setConnectTm();
	}

	public synchronized void channelClosed() {
		if (this.netState != NET.CLOSED) {
			setDisConnectTm();
		}
		this.netState = NET.CLOSED;
		this.channel = null;
		getStts().initStts(false);
	}

	/**
	 * 통신 패킷 초기화
	 */
	public void initReqPacket() {
		if (this.reqSynchronize == null) {
			this.reqSynchronize = new VdsReqSynchronize((short)this.GROUP_NO, (short)this.VDS_CTLR_LOCAL_NO);
			//this.reqSynchronize = new VdsReqSynchronize((short)0xFFFF, (short)0xFFFF);
			this.reqSynchronize.makeCRC();
		}
		if (this.reqData == null) {
			this.reqData = new VdsReqData((short)this.GROUP_NO, (short)this.VDS_CTLR_LOCAL_NO);
			this.reqData.makeCRC();
		}
		if (this.reqTemperature == null) {
			this.reqTemperature = new VdsReqTemperature((short)this.GROUP_NO, (short)this.VDS_CTLR_LOCAL_NO);
			this.reqTemperature.makeCRC();
		}
		if (this.reqTraffic == null) {
			this.reqTraffic = new VdsReqTraffic((short)this.GROUP_NO, (short)this.VDS_CTLR_LOCAL_NO);
			this.reqTraffic.makeCRC();
		}

	}

	/**
	 * Channel Send Data
	 * @param sendBuffer
	 * @param delayMilliSeconds
	 * @param packetDesc
	 * @return
	 */
	public boolean sendData(ByteBuffer sendBuffer, int delayMilliSeconds, String packetDesc) {
		boolean result = false;
		if (this.channel != null) {
			ChannelFuture f = this.channel.writeAndFlush(sendBuffer);
			f.awaitUninterruptibly();
			if (f.isDone() || f.isSuccess()) {
				result = true;
				//log.info("[{}]. sendData: OK. {}, {} Bytes.", this.VDS_CTLR_ID, packetDesc, sendBuffer.array().length);
				if (delayMilliSeconds > 0) {
					VdsProtocol.sleep(delayMilliSeconds);
				}
			} else {
				log.error("[{}]. sendData: Failed. {}, {} Bytes.", this.VDS_CTLR_ID, packetDesc, sendBuffer.array().length);
			}
		} else {
			log.error("[{}]. sendData: Failed. Not Connected. {}, {} Bytes.", this.VDS_CTLR_ID, packetDesc, sendBuffer.array().length);
		}
		return result;
	}

	public voVdsState getVdsStts() {
		voVdsState ss = new voVdsState();
		ss.setCTLR_NMBR(getVDS_CTLR_NMBR());
		ss.setComm(getStts().getSttsComm());				//Comm
		ss.setVideoInput(getStts().getSttsVideoInput());	//VideoInput
		ss.setHeater(getStts().getSttsHeater());			//Heater
		ss.setFan(getStts().getSttsFan());				    //Fan
		ss.setBackDoorOpen(getStts().getSttsBackDoor());	//BackDoorOpen
		ss.setFrontDoorOpen(getStts().getSttsFrontDoor());	//FrontDoorOpen
		ss.setTemperature(getStts().getSttsTemp());		    //Temperature
		ss.setFanMode(getStts().getSttsFanMode());			//FanMode
		ss.setFanRunTemp(getStts().getSttsFanRunTemp());	//FanRunTemp
		ss.setHetrMode(getStts().getSttsHetrMode());		//HetrMode
		ss.setHetrRunTemp(getStts().getSttsHetrRunTemp());	//HetrRunTemp

		return ss;
	}

	public boolean channelClose() {
		if (getChannel() == null || getNetState() == NET.CLOSED) {
			log.error("Close Request: channel not connected: [{}]", this);
			return false;
		}
		VdsTcpClientIdleHandler.disconnectChannel(getChannel());
		return true;
	}

	public boolean reset() {
		if (getChannel() == null || getNetState() == NET.CLOSED) {
			log.error("Reset Request: channel not connected: [{}]", this);
			return false;
		}
		VdsReqReset vdsReq = new VdsReqReset((short)getGROUP_NO(), (short)getVDS_CTLR_LOCAL_NO());
		vdsReq.makeCRC();
		ByteBuffer sendBuffer = vdsReq.getByteBuffer();
		if (!sendData(sendBuffer, 0, "vds_Reset")) {
			log.error("Reset Data Send Failed: [{}]", this);
			return false;
		} else {
			log.error("Reset Data Send Failed: [{}]", this);
			return true;
		}
	}

	public boolean initialize() {
		if (getChannel() == null || getNetState() == NET.CLOSED) {
			log.error("Initialize Request: channel not connected: [{}]", this);
			return false;
		}
		VdsReqInitialize vdsReq = new VdsReqInitialize((short)getGROUP_NO(), (short)getVDS_CTLR_LOCAL_NO());
		vdsReq.makeCRC();
		ByteBuffer sendBuffer = vdsReq.getByteBuffer();
		if (!sendData(sendBuffer, 0, "vds_Initialize")) {
			log.error("Initialize Data Send Failed: [{}]", this);
			return false;
		} else {
			log.error("Initialize Data Send Failed: [{}]", this);
			return true;
		}
	}

	public boolean stopImage(byte cameraNo) {
		if (getChannel() == null || getNetState() == NET.CLOSED) {
			log.error("StopImage Request: channel not connected: [{}]", this);
			return false;
		}
		int frameNo = 0;    // first image request
		setStopImageRequest(getChannel(), cameraNo, frameNo);
		VdsReqImage reqImage = new VdsReqImage((short)getGROUP_NO(), (short)getVDS_CTLR_LOCAL_NO());
		reqImage.setCameraNo(cameraNo);
		reqImage.setFrameNo(frameNo);
		reqImage.makeCRC();
		ByteBuffer sendBuffer = reqImage.getByteBuffer();
		if (!sendData(sendBuffer, 0, "vds_Image")) {
			setStopImageResponse();
			log.error("StopImage Data Send Failed: [{}]", this);
			return false;
		} else {
			log.error("StopImage Data Send Failed: [{}]", this);
			return true;
		}
	}
}
