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

import com.yeejoin.amos.iec104.tcp.IEC104Decoder;
import com.yeejoin.amos.iec104.tcp.IEC104Encoder;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.timeout.IdleStateHandler;
import org.apache.log4j.Logger;

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

public class IEC104Client {
    private final String host;
    private final int port;
    private Channel channel;
    private Bootstrap b = null;
    private String commonAddress;
    private int receiveSeqNum = 0; //接收序号
    private int sendSeqNum = 0; // 发送序号，每发送一个后需+1
    private boolean isConnected = false;
    private boolean isSendTest = false;
    private ReentrantLock lock = new ReentrantLock(true);
    private ScheduledExecutorService service_test = null;
    private ScheduledExecutorService service_connect = null;
    private Semaphore semaphore = new Semaphore(1);

    private String clientId;

    private boolean isClose = false;
    ChannelFuture future = null;

    public ChannelFutureListener channelFutureListener = new ChannelFutureListener() {

        @Override
        public void operationComplete(ChannelFuture future) throws Exception {
            if (isClose) {
                return;
            }
            if (future.isSuccess() && future.channel().isActive()) {
                isConnected = true;
                if (service_connect != null) {
                    service_connect.shutdownNow();
                    service_connect = null;
                }
                Logger.getLogger(this.getClass()).debug("连接服务器成功");
            } else {
                isConnected = false;

                Logger.getLogger(this.getClass()).debug("连接服务器失败, 重新连接");
//				future.cause().printStackTrace();
                connect2Clinet(clientId);
            }
        }
    };

    // 连接服务端的端口号地址和端口号
    public IEC104Client(String host, int port) {
        this.host = host;
        this.port = port;
    }

    private void connect2Clinet(String serviceId) {
        final EventLoopGroup group = new NioEventLoopGroup();
        IEC104Client client = this;
        if (service_connect != null) {
            service_connect.shutdownNow();
            service_connect = null;
        }
        Runnable connect_runnable = new Runnable() {
            public void run() {
                try {
                    b = new Bootstrap();
                    b.group(group).channel(NioSocketChannel.class) // 使用NioSocketChannel来作为连接用的channel类
                            .handler(new ChannelInitializer<SocketChannel>() { // 绑定连接初始化器
                                @Override
                                public void initChannel(SocketChannel ch) throws Exception {
                                    Logger.getLogger(this.getClass()).debug("正在连接中...");
                                    ChannelPipeline pipeline = ch.pipeline();
                                    pipeline.addLast(new IdleStateHandler(30, 30, 60, TimeUnit.SECONDS));
                                    pipeline.addLast(new IEC104Decoder(serviceId)); // 编码
                                    pipeline.addLast(new IEC104Encoder(serviceId)); // 解码
                                    pipeline.addLast(new IEC104NewHandler(client)); // 业务处理类
                                }
                            });
                    // 发起异步连接请求，绑定连接端口和host信息
                    future = b.connect(host, port).sync();

                    future.addListener(channelFutureListener);

                    channel = future.channel();
                    channel.closeFuture().sync();
                } catch (Exception e) {
                    Logger.getLogger(this.getClass()).error("connect to " + host + ":" + port + "error: " + e.getMessage());
                }
            }
        };
        service_connect = Executors.newSingleThreadScheduledExecutor();
        // 第二个参数为首次执行的延时时间，第三个参数为定时执行的间隔时间
        service_connect.scheduleAtFixedRate(connect_runnable, 10, 10, TimeUnit.SECONDS);
    }

    public void closeChannel() {
        future.addListener(channelFutureListener);
        channel.close();
    }

    public void init(String serviceId) throws Exception {
        connect2Clinet(serviceId);
    }

    public Channel getChannel() {
        return channel;
    }

//	public void start() {
//		if (ObjectUtils.isEmpty(channel)) {
//			throw new YeeException("客户端初始化失败");
//		}
//	}

//	public void sendMessage(String strBytes) {
//		ChannelPromise promise = this.channel.newPromise();
//		this.channel.writeAndFlush(strBytes, promise);
//	}

    public boolean isConnected() {
        return isConnected;
    }

//	public String sendACKMessage() {
//		String recStr = null;
//		try {
//			if(lock.tryLock(0, TimeUnit.SECONDS)) {
//				ChannelPromise promise = this.channel.newPromise();
//				if (this.receiveSeqNum >= sendSeqNum + 1) {
//					this.receiveSeqNum += 1;
//				} else {
//					this.receiveSeqNum = sendSeqNum + 1;
//				}
//				
//				byte[] recNum = new byte[2];
//		        recNum[0] = (byte) (this.receiveSeqNum << 1);
//		        recNum[1] = (byte) (this.receiveSeqNum >> 7);
//		        recStr = ChangeUtils.toHexString(recNum);
//				this.channel.writeAndFlush("68040100" + recStr, promise);
//
//				if (lock.isLocked()) {
//					lock.unlock();
//				}
//			} else {
//				sendTestMessage();
//			}
//		} catch (InterruptedException e) {
//			Logger.getLogger(this.getClass()).error(e.getMessage());
//		}
//		return recStr;
//	}

//	public boolean sendSOEMessage(String asduBytes) {
//		return sendIMessage(asduBytes);
//	} 

    /**
     * 	启动循环发送测试报文
     */
//	public void sendTestMessage() {
//		synchronized(this) {
//			if (service_test != null) {
//				return;
//			}
//			isSendTest = true;
//			Runnable test_runnable = new Runnable() {
//	            public void run() {
//	                try {
//	                    ChannelPromise promise = channel.newPromise(); 
//	                    channel.writeAndFlush("680443000000", promise);
//	                } catch (Exception e) {
//	                    e.printStackTrace();
//	                }
//	            }
//	        };
//	        service_test = Executors.newSingleThreadScheduledExecutor();
//	        // 第二个参数为首次执行的延时时间，第三个参数为定时执行的间隔时间
//	        service_test.scheduleAtFixedRate(test_runnable, 0, 10, TimeUnit.SECONDS);
//		}
//	}

    /**
     * 	结束循环发送测试报文
     */
//	public void relaseTest() {
//		this.isSendTest = false;
//		if (service_test != null) {
//			service_test.shutdownNow();
//			service_test = null;
//			semaphore.release();
//		}
//	}

    /**
     * 发送I帧报文
     *
     * @param
     * @return
     */
//	public boolean sendIMessage(String asduBytes) {
//		
//		StringBuffer sBuffer = new StringBuffer("68");
//		int asduLen = ChangeUtils.hexStringToBytes(asduBytes).length;
//		int apduLen = asduLen + 4;
//		boolean startSend = true;
//		try {
//			do {
//				if (!startSend) {
//					sendTestMessage();
//				} else {
//					startSend = false;
//				}		
//			} while(!semaphore.tryAcquire(15, TimeUnit.SECONDS));
//		
//		
////			if (!isSendTest && lock.tryLock(15, TimeUnit.SECONDS)) {
//			byte[] recNum = new byte[2];
//		    recNum[0] = (byte) (this.receiveSeqNum << 1);
//		    recNum[1] = (byte) (this.receiveSeqNum >> 7);
//		    String recStr = ChangeUtils.toHexString(recNum);
//		    
//		    byte[] sendNum = new byte[2];
//		    sendNum[0] = (byte) (this.sendSeqNum << 1);
//		    sendNum[1] = (byte) (this.sendSeqNum >> 7);
//		    this.sendSeqNum += 1;
//		    String sendStr = ChangeUtils.toHexString(sendNum);
//		    
//		    byte len = (byte) apduLen;
//		    sBuffer.append(Integer.toHexString(len & 0xFF));
//		    sBuffer.append(sendStr);
//		    sBuffer.append(recStr);
//		    sBuffer.append(asduBytes);
//		    
//		    ChannelPromise promise = this.channel.newPromise(); 
//		    
//		    this.channel.writeAndFlush(sBuffer.toString(), promise);
//
//		    return true;
//		} catch (InterruptedException e) {
//			e.printStackTrace();
//		}
//		return false;
//	}

//	public void relase() {
//		if (lock.isLocked()) {
//			lock.unlock();
//		}
//		semaphore.release();
//	}

//	public int getReceiveSeqNum() {
//		return receiveSeqNum;
//	}
//
//	public void setReceiveSeqNum(int receiveSeqNum) {
//		this.receiveSeqNum = receiveSeqNum;
//	}
//
//	public int getSendSeqNum() {
//		return sendSeqNum;
//	}
//
//	public void setSendSeqNum(int sendSeqNum) {
//		this.sendSeqNum = sendSeqNum;
//	}
//
//	public boolean isSendTest() {
//		return isSendTest;
//	}
    public String getCommonAddress() {
        return commonAddress;
    }

    public void setCommonAddress(String commonAddress) {
        this.commonAddress = commonAddress;
    }

    public void close() {
//		isClose = true;
//		this.channel.close();
//		if (lock.isLocked()) {
//			lock.unlock();
//		}
//		relaseTest();
//		if (service_connect != null) {
//			service_connect.shutdownNow();
//			service_connect = null;
//		}
    }

    public String getClientId() {
        return clientId;
    }

    public void setClientId(String clientId) {
        this.clientId = clientId;
    }


}
