package com.lenzhao.framework.server;

import java.io.IOException;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import com.lenzhao.framework.config.ServerConfig;
import com.lenzhao.framework.protocol.RpcRequestDecode;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.group.ChannelGroup;
import org.jboss.netty.channel.group.ChannelGroupFuture;
import org.jboss.netty.channel.group.DefaultChannelGroup;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.timeout.ReadTimeoutHandler;
import org.jboss.netty.util.HashedWheelTimer;
import org.jboss.netty.util.Timer;

import com.lenzhao.framework.protocol.RpcResponseEncode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *rpc服务实现类,在此开启长连接
 */
public class RpcServerBootstrap implements IRpcServer {

	private static final Logger logger = LoggerFactory.getLogger(RpcServerBootstrap.class);
	
	private ServerBootstrap bootstrap = null;
	
	private AtomicBoolean stopped = new AtomicBoolean(false);
	
	//处理超时事件
	private Timer timer=null;
	
	private void initHttpBootstrap(int myport) {
		logger.info("initHttpBootstrap...........");
		final ServerConfig serverConfig = new ServerConfig(myport);
		final ChannelGroup channelGroup = new DefaultChannelGroup(getClass().getName());
		bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(
				//建议用ThreadPoolExecutor代替
				Executors.newCachedThreadPool(),
				Executors.newCachedThreadPool(), serverConfig.getThreadCnt()));
		//设置常见参数
		bootstrap.setOption("tcpNoDelay","true");//禁用nagle算法
		bootstrap.setOption("reuseAddress", "true");
		bootstrap.setOption("SO_RCVBUF",1024*128);
		bootstrap.setOption("SO_SNDBUF",1024*128);
		timer = new HashedWheelTimer();
		bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
			public ChannelPipeline getPipeline() throws Exception {
				ChannelPipeline pipeline = Channels.pipeline();
				int readTimeout = serverConfig.getReadTimeout();
				if (readTimeout > 0) {
					pipeline.addLast("timeout", new ReadTimeoutHandler(timer, readTimeout, TimeUnit.MILLISECONDS));
				}
				pipeline.addLast("decoder", new RpcRequestDecode());
				pipeline.addLast("encoder", new RpcResponseEncode());
				pipeline.addLast("handler", new NettyRpcServerHandler(channelGroup));
				return pipeline;
			}
		});
		
		int port = serverConfig.getPort();
		if (!checkPortConfig(port)) {
			throw new IllegalStateException("port: " + port + " already in use!");
		}

		Channel channel = bootstrap.bind(new InetSocketAddress(port));
		channelGroup.add(channel);
		logger.info("voyage server started");

		waitForShutdownCommand();
		ChannelGroupFuture future = channelGroup.close();
		future.awaitUninterruptibly();
		bootstrap.releaseExternalResources();
		timer.stop();
		timer = null;

		logger.info("voyage server stoped");

	}
	
	public void start(int port) {
		ExtensionLoader.init();
		initHttpBootstrap(port);
	}

	public void stop() {
		stopped.set(true);
		synchronized (stopped) {
			stopped.notifyAll();
		}
	}
	
	private void waitForShutdownCommand() {
		synchronized (stopped) {
			while (!stopped.get()) {
				try {
					stopped.wait();
				} catch (InterruptedException e) {
				}
			}
		}
	}
	
	private boolean checkPortConfig(int listenPort) {
		if (listenPort < 0 || listenPort > 65536) {
			throw new IllegalArgumentException("Invalid start port: " + listenPort);
		}
		ServerSocket ss = null;
		DatagramSocket ds = null;
		try {
			ss = new ServerSocket(listenPort);
			ss.setReuseAddress(true);
			ds = new DatagramSocket(listenPort);
			ds.setReuseAddress(true);
			return true;
		} catch (IOException e) {
		} finally {
			if (ds != null) {
				ds.close();
			}
			if (ss != null) {
				try {
					ss.close();
				} catch (IOException e) {
				}
			}
		}
		return false;
	}

}