package com.eactive.eai.adapter.socket2.protocol;

import java.util.Date;
import java.util.Hashtable;
import java.util.Map;

import org.apache.mina.common.IoSession;

import com.eactive.eai.adapter.AdapterVO;
import com.eactive.eai.adapter.EAIException;
import com.eactive.eai.adapter.socket2.EAIConnectException;
import com.eactive.eai.adapter.socket2.common.CircularQueue;
import com.eactive.eai.adapter.socket2.common.ExceptionStackTrace;
import com.eactive.eai.adapter.socket2.config.SocketAdapterContext;
import com.eactive.eai.adapter.socket2.config.SocketLoggerManager;
import com.eactive.eai.common.util.Logger;

import edu.emory.mathcs.backport.java.util.concurrent.ExecutorService;
import edu.emory.mathcs.backport.java.util.concurrent.Executors;
import edu.emory.mathcs.backport.java.util.concurrent.ThreadPoolExecutor;
import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;

public abstract class SocketAdapter implements ISocketAdapter {
	static final int MAX_LOG_MESSAGE_SIZE = 100000;
	
	protected final Logger logger;
	protected final Logger errLogger;
    
	protected State state = State.STOPPED;
	
	protected SocketAdapterContext context;
	protected ProtocolHandler handler;	
	protected ExecutorService executor;
	protected Hashtable<String, Thread> threadMap = new Hashtable<>();
	
	protected static CircularQueue globalErrorQueue = new CircularQueue(10); 
	protected CircularQueue recentErrorQueue = new CircularQueue(10); 
	protected CircularQueue recentSendRecvQueue = new CircularQueue(10); 
	
	protected Date uptime;
	
	protected void startExecutor() {
		this.executor = Executors.newCachedThreadPool();
		((ThreadPoolExecutor) this.executor).setKeepAliveTime(1L, TimeUnit.SECONDS);
	}
	
	protected SocketAdapter(SocketAdapterContext context) {
		this.context = context;
		startExecutor();
	    logger    = SocketLoggerManager.getInstance().getLogger(context.getAdapterGroupName());
	    errLogger = SocketLoggerManager.getInstance().getErrLogger();
	    
	    this.uptime = new Date();
	}
	
	public SocketAdapterContext getContext() {
		return context;
	}
	
	public void setTraceLevel(int traceLevel) {
		context.setTraceLevel(traceLevel);
	}

	public boolean connect() throws Exception {
		throw new UnsupportedOperationException();
	}
	
	public IoSession connectOne() throws EAIConnectException {
		throw new UnsupportedOperationException();
	}

	public SessionPool getSessionPool() {
		return handler.getSessionPool();
	}	
	
	public Map getThreadMap() {
		return threadMap;
	}
	
	public long getMessageSend() {
		return handler.getMessageSend();
	}
	
	public long getMessageRecv() {
		return handler.getMessageRecv();
	}
	
	public void incMessageSend() {
		handler.incMessageSend();
	}
	
	public void incMessageRecv() {
		handler.incMessageRecv();
	}
	
	public State getState() {
		return this.state;
	}
	
	public void setState(State state) {
		this.state = state;
	}
	
	public int getRecentErrorCount() {
		return recentErrorQueue.getRecordCount();
	}
	
	public String[] getRecentErrors() {
		return recentErrorQueue.getRecords();
	}

	public static String[] getGlobalErrors() {
		return globalErrorQueue.getRecords();
	}

	public void addRecentError(String message) {
		if (message == null)
			return;
		if (message.length() > MAX_LOG_MESSAGE_SIZE/10)
			message = message.substring(0, MAX_LOG_MESSAGE_SIZE/10);
		recentErrorQueue.addRecord(message);
		globalErrorQueue.addRecord(message);
	}

	public void addRecentError(String message, Throwable t) {
		if (message == null)
			return;
		if (message.length() > MAX_LOG_MESSAGE_SIZE/10)
			message = message.substring(0, MAX_LOG_MESSAGE_SIZE/10);
		recentErrorQueue.addRecord(message + "\n" + ExceptionStackTrace.dump(t));
		globalErrorQueue.addRecord(message + "\n" + ExceptionStackTrace.dump(t));
	}
	
	public void addRecentErrorOnly(String message) {
		if (message == null)
			return;
		if (message.length() > MAX_LOG_MESSAGE_SIZE/10)
			message = message.substring(0, MAX_LOG_MESSAGE_SIZE/10);
		recentErrorQueue.addRecord(message);
		//globalErrorQueue.addRecord(message);
	}

	public void addRecentErrorOnly(String message, Throwable t) {
		if (message == null)
			return;
		if (message.length() > MAX_LOG_MESSAGE_SIZE/10)
			message = message.substring(0, MAX_LOG_MESSAGE_SIZE/10);
		recentErrorQueue.addRecord(message + "\n" + ExceptionStackTrace.dump(t));
		//globalErrorQueue.addRecord(message + "\n" + ExceptionStackTrace.dump(t));
	}	
	
	public void addRecentLog(String message) {
		if (message == null)
			return;
		if (message.length() > MAX_LOG_MESSAGE_SIZE)
			message = message.substring(0, MAX_LOG_MESSAGE_SIZE);
		recentSendRecvQueue.addRecord(message);
	}

	public void addRecentSend(IoSession session, String message) {
		if (message == null)
			return;
		if (message.length() > MAX_LOG_MESSAGE_SIZE)
			message = message.substring(0, MAX_LOG_MESSAGE_SIZE);
		recentSendRecvQueue.addRecord("<SEND> " + (session==null ? "" : "(" + session + ") - ") + message);
	}
	
	public void addRecentRecv(IoSession session, String message) {
		if (message == null)
			return;
		if (message.length() > MAX_LOG_MESSAGE_SIZE)
			message = message.substring(0, MAX_LOG_MESSAGE_SIZE);
		recentSendRecvQueue.addRecord("<RECV> " + (session==null ? "" : "(" + session + ") - ") + message);
	}

	public void addRecentSendTrace(IoSession session, String message) {
		if (message == null)
			return;
		if (message.length() > MAX_LOG_MESSAGE_SIZE)
			message = message.substring(0, MAX_LOG_MESSAGE_SIZE);
		recentSendRecvQueue.addRecord("<font color='#B0B0B0'><SEND> " + (session==null ? "" : "(" + session + ") - ") + message + "</font>");
	}

	public void addRecentRecvTrace(IoSession session, String message) {
		if (message == null)
			return;
		if (message.length() > MAX_LOG_MESSAGE_SIZE)
			message = message.substring(0, MAX_LOG_MESSAGE_SIZE);
		recentSendRecvQueue.addRecord("<font color='#B0B0B0'><RECV> " + (session==null ? "" : "(" + session + ") - ") + message + "</font>");
	}

	public String[] getRecentSendRecv() {
		return recentSendRecvQueue.getRecords();
	}
	
	public void clearStat() {
		recentErrorQueue.clear();
		recentSendRecvQueue.clear();
		handler.clearStats();
	}
	
	public void resumeRead() {
	}

	public void resumeWrite() {
	}

	public void suspendRead() {
	}

	public void suspendWrite() {
	}	
	
	public Object sendMessage(Object message) throws EAIException {
		return handler.sendMessage(null, message);
	}
	
	public Object sendMessage(Object message, int responseTimeout) throws EAIException {
		return handler.sendMessage(null, message, responseTimeout);
	}	
	
	public Object sendMessage(IoSession reservedSession, Object message) throws EAIException {
		return handler.sendMessage(reservedSession, message);
	}

	public IoSession acquireSession() throws Exception {
		return handler.acquireSession();
	}
	
	public void releaseSession(IoSession session) {
		handler.releaseSession(session);
	}
	
	public void setAdapterStatus(boolean status) {
		AdapterVO vo = getContext().getAdapterVO();
        if (vo != null) {
        	vo.setStatus(status);
        }
	}
	
	public Date getUptime() {
		return uptime;
	}

	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append(context.getAdapterName() + " | sessionPool size : " + handler.getSessionPool().size());
		return sb.toString();
	}
}
