package com.xinqihd.sns.gameserver.transport;

import org.apache.mina.core.future.IoFuture;
import org.apache.mina.core.future.IoFutureListener;
import org.apache.mina.core.future.WriteFuture;
import org.apache.mina.core.service.IoHandler;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.xinqihd.sns.gameserver.Stat;
import com.xinqihd.sns.gameserver.config.Constant;
import com.xinqihd.sns.gameserver.config.GlobalConfig;
import com.xinqihd.sns.gameserver.util.ClientPool;

/**
 * The MessageClient will transport messages to other GameServer
 * 
 * @author wangqi
 * 
 */
public class GameProxyClient {

	public static final int CONNECT_TIMEOUT = 1000;

	private static final Logger logger = LoggerFactory.getLogger(GameProxyClient.class);

	private static final XinqiProxyMessage HEART_BEAT_MSG = new XinqiProxyMessage();

	private static final StatIoFilterListener statListener = new StatIoFilterListener();

	private GameProtocolCodecFilter filter = new GameProtocolCodecFilter();

	private IoHandler[] handlers = null;

	private ClientPool clientPool = null;
	
	private String remoteHost;
	
	private int remotePort;

	public GameProxyClient(String remoteHost, int remotePort) {
		this(remoteHost, remotePort, Constant.CPU_CORES);
	}
	
	public GameProxyClient(String remoteHost, int remotePort, int count) {
		this.remoteHost = remoteHost;
		this.remotePort = remotePort;

		handlers = new MessageIoHandler[count];
		for ( int i=0; i<handlers.length; i++ ) {
			handlers[i] = new MessageIoHandler();
		}
		int heartBeatSecond = GlobalConfig.getInstance().getIntProperty("message.heartbeat.second");
		if ( logger.isDebugEnabled() ) {
			logger.debug("heartBeatSecond : " + heartBeatSecond);
		}
		clientPool = new ClientPool(filter, handlers, remoteHost, remotePort, count);
		clientPool.setStatListener(statListener);
		clientPool.setHeartBeatSecond(heartBeatSecond);
		clientPool.setHeartBeatMessage(HEART_BEAT_MSG);
		clientPool.initQueue();
	}

	/**
	 * Connect to remote message server.
	 * 
	 * @return
	 */
	public boolean connectToServer() {
		return clientPool.connectToServer();
	}

	/**
	 * Disconnect from remote message server and release resources.
	 * 
	 * @return
	 */
	public boolean disconnectFromServer() {
		return clientPool.connectToServer();
	}

	/**
	 * Send a message to server.
	 * 
	 * @param msg
	 */
	public WriteFuture sendMessageToServer(XinqiProxyMessage msg) {
		return clientPool.sendMessageToServer(msg);
	}

	@Override
	public String toString() {
		StringBuilder buf = new StringBuilder(32);
		buf.append(remoteHost).append(Constant.COLON).append(remotePort);
		return buf.toString();
	}

	/**
	 * Internal MessageIoHandler.
	 * @author wangqi
	 *
	 */
	private class MessageIoHandler extends IoHandlerAdapter {

		/**
		 * Session opened
		 */
		@Override
		public void sessionOpened(IoSession session) throws Exception {
			try {
				if (logger.isDebugEnabled()) {
					logger.debug("Proxy session has been opened. ");
				}
			} finally {
			}
		}

		/**
		 * Session closed
		 */
		@Override
		public void sessionClosed(IoSession session) throws Exception {
			try {
				if (logger.isDebugEnabled()) {
					logger.debug("Proxy session has been closed. ");
				}
			} finally {
			}
		}

		/**
		 * When a session is idle for configed seconds, it will send a heart-beat
		 * message to remote.
		 */
		@Override
		public void sessionIdle(IoSession session, IdleStatus status)
				throws Exception {
			try {
				if (logger.isDebugEnabled()) {
					logger.debug("Proxy session has been idle for a while. Send a heartbeat message.");
				}
				sendMessageToServer(HEART_BEAT_MSG);
				Stat.getInstance().gameHearbeatSent++;
			} finally {
			}
		}

		/**
		 * Invoked when any exception is thrown by user IoHandler implementation or
		 * by MINA. If cause is an instance of IOException, MINA will close the
		 * connection automatically.
		 * 
		 */
		@Override
		public void exceptionCaught(IoSession session, Throwable cause)
				throws Exception {
			try {
				Stat.getInstance().gameClientSentFail++;
				if (logger.isDebugEnabled()) {
					logger.debug("Proxy Caught Exception: {}", cause.getMessage());
				}
				if (!session.isConnected()) {
					if (logger.isInfoEnabled()) {
						logger.info("Proxy reconnect to server due to closed session");
					}
					disconnectFromServer();
					connectToServer();
				}
			} finally {
			}
		}

		/**
		 * A message received when testing.
		 */
		@Override
		public void messageReceived(IoSession session, Object message)
				throws Exception {
			// Server will never send a message to clients.
			try {
				logger.info("Proxy message received");
			} catch (Exception e) {
				logger.error("Proxy Caught Exception: {}", e.getMessage());
			}
		}
	}

	static class StatIoFilterListener implements IoFutureListener<IoFuture> {

		@Override
		public void operationComplete(IoFuture future) {
			Stat.getInstance().gameClientSent++;
		}

	}

}