package com.yeejoin.amos.iec104.tcp.client;

import java.util.Date;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.log4j.Logger;
import org.springframework.util.ObjectUtils;

import com.yeejoin.amos.iec104.tcp.client.invoke.ApduQueue;
import com.yeejoin.amos.iec104.tcp.entity.Apdu;
import com.yeejoin.amos.iec104.tcp.entity.ServerData;
import com.yeejoin.amos.iec104.tcp.utils.ChangeUtils;
import com.yeejoin.amos.iec104.tcp.utils.Constant;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.channel.SimpleChannelInboundHandler;

public class IEC104NewHandler extends SimpleChannelInboundHandler<Apdu>{
	public IEC104Client client = null; //接收序号
	private ChannelHandlerContext ctx;
	int receiveNum = 0;
	int sendNum = 0;
	
	private long recordTime;
	private ReentrantLock serialLock = new ReentrantLock();
	private boolean stopCommunication = false;
	private boolean recIpackate = false;
	
	ScheduledExecutorService test_Communication = null;
	ScheduledExecutorService eecruiting_Communication = null;
	ScheduledExecutorService send_Communication = null;
	ScheduledExecutorService ack_Communication = null;
	ScheduledExecutorService startService = null;
	
	private  BlockingQueue<String> handleBlockingSendQueue = new LinkedBlockingQueue<String>();
	
	public IEC104NewHandler(IEC104Client client) {
		this.client = client;
	}
	
	@Override
	protected void channelRead0(ChannelHandlerContext ctx, Apdu apdu) throws Exception {
		try {
    		Date date = new Date();
    		this.recordTime = date.getTime();
            if (apdu.getApciType() == Apdu.ApciType.I_FORMAT) {
            	receiveNum = apdu.getSendSeqNumber();
            	recIpackate = true;
            	handleData(apdu);
            } else if (apdu.getApciType() == Apdu.ApciType.S_FORMAT) {
            	Logger.getLogger(this.getClass()).warn("返回S确认帧");
            	if (apdu.getReceiveSeqNumber() != sendNum) {
    				close();
    			}
            } else if (apdu.getApciType() == Apdu.ApciType.STARTDT_ACT) {
            	handleBlockingSendQueue.add("68040B000000");
            	if (test_Communication == null) {
                	test_Communication = Executors.newSingleThreadScheduledExecutor();
            		test_Communication.scheduleAtFixedRate(test_runnable, Constant.Z_Z_INITIALDELAY, 1, TimeUnit.SECONDS);
                }
            } else if (apdu.getApciType() == Apdu.ApciType.STOPDT_ACT) {
            	handleBlockingSendQueue.add("680423000000");
            	close();
            } else if (apdu.getApciType() == Apdu.ApciType.TESTFR_ACT) {
            	handleBlockingSendQueue.add("680483000000");
                Logger.getLogger(this.getClass()).debug("确认测试消息，U类型");
            } else if (apdu.getApciType() == Apdu.ApciType.STARTDT_CON) {
                Logger.getLogger(this.getClass()).debug("启动确认帧回复，U类型");
                if (startService != null) {
        			startService.shutdownNow();
        			startService = null;
        		}
               // 添加测试报文
                if (test_Communication == null) {
                	test_Communication = Executors.newSingleThreadScheduledExecutor();
            		test_Communication.scheduleAtFixedRate(test_runnable, Constant.Z_Z_INITIALDELAY, 1, TimeUnit.SECONDS);
                }
                if (ack_Communication == null) {
                	addACKDatalistener();
                }
            } else if (apdu.getApciType() == Apdu.ApciType.TESTFR_CON) {
                Logger.getLogger(this.getClass()).debug("确认测试消息回复，U类型");
                if (eecruiting_Communication == null) {
                	eecruiting_Communication = Executors.newSingleThreadScheduledExecutor();
            		eecruiting_Communication.scheduleAtFixedRate(Recruiting_runnable, Constant.Z_Z_INITIALDELAY, Constant.Z_Z_PREIOD, TimeUnit.SECONDS);
                }
            } else {
            	Logger.getLogger(this.getClass()).debug("其它报文：" + apdu.getApciType());
            }
        } catch (Exception e){
        	e.printStackTrace();
        	Logger.getLogger(this.getClass()).debug("异常错误,"+ e);
        }
	}
	
	private void handleData(Apdu apdu) {
    	if (apdu.getAsdu().getTypeId()== 100) {
    		return;
    	}
    	ServerData serverData = new ServerData();
    	serverData.setApdu(apdu);
    	serverData.setServerId(client.getClientId());
    	ApduQueue.getInstance().add(serverData);
	}
	
	private void addACKDatalistener() {
		recordTime = new Date().getTime();
		ack_Communication = Executors.newSingleThreadScheduledExecutor();
        // 第二个参数为首次执行的延时时间，第三个参数为定时执行的间隔时间
		ack_Communication.scheduleAtFixedRate(ack_runnable, 1, 1, TimeUnit.SECONDS);
	}
	
	Runnable ack_runnable = new Runnable() {
		
		@Override
		public void run() {
			long currentTime = new Date().getTime();
			if (recIpackate && ((currentTime - recordTime) / 1000) > 5) {
				recIpackate = false;
				byte[] recNum = new byte[2];
				receiveNum ++;
		        recNum[0] = (byte) (receiveNum << 1);
		        recNum[1] = (byte) (receiveNum >> 7);
		        String recStr = ChangeUtils.toHexString(recNum);
		        handleBlockingSendQueue.add("68040100" + recStr);
			}
		}
	};
	
	Runnable test_runnable = new Runnable() {
		
		@Override
		public void run() {
			Date date = new Date();
			if (!stopCommunication && ((date.getTime() - recordTime) / 1000) >= 10) {
				recordTime = date.getTime();
				handleBlockingSendQueue.add("680443000000");
			}
		}
	};
	
	Runnable Recruiting_runnable = new Runnable() {
        public void run() {
        	try {
        		boolean isSend = false;
        		do {
	        		byte[] sendNumBytes = new byte[2];
					byte[] recNumBytes = new byte[2];
					serialLock.lock();
					recIpackate = false;
			        sendNumBytes[0] = (byte) (sendNum << 1);
					sendNumBytes[1] = (byte) (sendNum >> 7);
			        
					recNumBytes[0] = (byte) (receiveNum << 1);
			        recNumBytes[1] = (byte) (receiveNum >> 7);
			        
			        String recStr = ChangeUtils.toHexString(recNumBytes);
			        String sendStr = ChangeUtils.toHexString(sendNumBytes);
			        sendNum ++;
			        serialLock.unlock();
	                String commonAddress = client.getCommonAddress();
	                String zzPackate = "680E"+sendStr+recStr+"64010600"+commonAddress+"00000014";
	                handleBlockingSendQueue.add(zzPackate.replace(" ", "").toUpperCase());
	                isSend = true;
	                
        		} while(!isSend);
    		 } catch (Exception e) {
                 e.printStackTrace();
             }
        }
    };

	private void addSendDatalistener() {
		send_Communication = Executors.newSingleThreadScheduledExecutor();
        // 第二个参数为首次执行的延时时间，第三个参数为定时执行的间隔时间
		send_Communication.execute(send_runnable);;
	}
	
	Runnable send_runnable = new Runnable() {
		
		@Override
		public void run() {
			while(!stopCommunication) {
				try {
					String data = handleBlockingSendQueue.take();
					ctx.channel().writeAndFlush(data.replace(" ", ""), ctx.channel().newPromise());
					recIpackate = false;
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	};
	
	Runnable runnable = new Runnable() {
        public void run() {
        	handleBlockingSendQueue.add("680407000000");
        }
    };

	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		super.channelActive(ctx);
		this.ctx = ctx;
		addSendDatalistener();
		startService = Executors.newSingleThreadScheduledExecutor();
        // 第二个参数为首次执行的延时时间，第三个参数为定时执行的间隔时间
        startService.scheduleAtFixedRate(runnable, Constant.Z_Z_INITIALDELAY, Constant.S_S_PREIOD, TimeUnit.SECONDS);
	}

	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception {
		super.channelInactive(ctx);
		this.ctx = null;
		close();
	}
	
	private void close() {
		receiveNum = 0;
		sendNum = 0;
		stopCommunication = true;
		if (startService != null) {
			startService.shutdownNow();
			startService = null;
		}
		if (send_Communication != null) {
			send_Communication.shutdownNow();
			send_Communication = null;
		}
		if (test_Communication != null) {
			test_Communication.shutdownNow();
			test_Communication = null;
		}
		if (ack_Communication != null) {
			ack_Communication.shutdownNow();
			ack_Communication = null;
		}
		if (eecruiting_Communication != null) {
			eecruiting_Communication.shutdownNow();
			eecruiting_Communication = null;
		}
		handleBlockingSendQueue.clear();
		this.client.closeChannel();
	}
}
