package com.shata.migration.netty;

import java.io.IOException;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadPoolExecutor;

import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.shata.migration.constants.Commands;
import com.shata.migration.server.ServerHandler;

public class NettyServerHandler extends SimpleChannelUpstreamHandler {

	private static final Logger log = LoggerFactory.getLogger(NettyServerHandler.class);

	private ExecutorService threadpool;
	
	private long timeout;

	public NettyServerHandler(ExecutorService threadpool, long timeout) {
		this.threadpool = threadpool;
		this.timeout = timeout;
	}

	public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
		if (!(e.getCause() instanceof IOException)) {
			log.error("catch some exception not IOException", e.getCause());
		}
	}

	public void messageReceived(final ChannelHandlerContext ctx, MessageEvent e) throws Exception {
		Object message = e.getMessage();
		if (!(message instanceof String)) {
			log.error("receive message error,only support String");
			throw new Exception("receive message error,only support String");
		}
		handleRequest(ctx, message);
	}

	private void handleRequest(final ChannelHandlerContext ctx, final Object message) {
		try {
			threadpool.execute(new HandlerRunnable(ctx, message, threadpool, timeout));
		} catch (RejectedExecutionException exception) {
			log.error("server threadpool full,threadpool maxsize is:" + ((ThreadPoolExecutor) threadpool).getMaximumPoolSize());
			sendErrorResponse(ctx, message);
		}
	}

	private void sendErrorResponse(final ChannelHandlerContext ctx, final Object message) {
		ChannelFuture wf = ctx.getChannel().write(Commands.ERROR);
		wf.addListener(new ChannelFutureListener() {
			public void operationComplete(ChannelFuture future) throws Exception {
				if (!future.isSuccess()) {
					log.error("server write response error! request message is:" + message);
				}
			}
		});
	}

	class HandlerRunnable implements Runnable {

		private ChannelHandlerContext ctx;

		private Object message;

		private ExecutorService threadPool;
		
		private long timeout;

		public HandlerRunnable(ChannelHandlerContext ctx, Object message, ExecutorService threadPool, long timeout) {
			this.ctx = ctx;
			this.message = message;
			this.threadPool = threadPool;
			this.timeout = timeout;
		}

		@SuppressWarnings("rawtypes")
		public void run() {
			//暂不支持List
			if (message instanceof List) {
				List messages = (List) message;
				for (Object messageObject : messages) {
					threadPool.execute(new HandlerRunnable(ctx, messageObject, threadPool, timeout));
				}
			} else {
				long beginTime = System.currentTimeMillis();
				String response = ServerHandler.handleRequest(message);
				log.debug("request:" + (String)message + " response:" + response);
				// already timeout,so not return
				if ((System.currentTimeMillis() - beginTime) >= timeout) {
					log.warn("timeout,so give up send response to client,request message is:" + message + ",response is:" + response + ",client is:" + ctx.getChannel().getRemoteAddress()
							+ ",consumetime is:" + (System.currentTimeMillis() - beginTime) + ",timeout is:" + timeout);
					return;
				}
				ChannelFuture wf = ctx.getChannel().write(response);
				wf.addListener(new ChannelFutureListener() {
					public void operationComplete(ChannelFuture future) throws Exception {
						if (!future.isSuccess()) {
							log.error("server write response error,request message is:" + message);
						}
					}
				});
			}
		}

	}

}