package com.eactive.eai.adapter.websocket;

import java.net.InetSocketAddress;
import java.util.Properties;

import org.java_websocket.WebSocket;
import org.java_websocket.handshake.ClientHandshake;
import org.java_websocket.server.WebSocketServer;

import com.eactive.eai.adapter.AdapterVO;
import com.eactive.eai.common.session.ChannelSessionFacade;
import com.eactive.eai.common.util.Logger;

public class WebSocketHandler extends WebSocketServer {

	private Properties prop;
	private AdapterVO adapterVO;

	static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);

	private static String PORT_NUMBER = "port.number";
	private static String POLLING_REQUEST = "polling.request";
	private static String POLLING_RESPONSE = "polling.response";
	
	private boolean useConnectionIdle = false;
	private int DEFAULT_CONNECTION_IDLEOUT= 60;
	private int DEFAULT_CONNECTION_IDLEINTERVAL= 10;
	private static String CONNECTION_IDLETIME_USE = "connection.idletime.use";
	private static String CONNECTION_IDLETIME = "connection.idletime";
	private static String CONNECTION_IDLEINTERVAL= "connection.idleintervals";
	
	private String pollingRequest = "POLLREQ@@";
	private String pollingResponse = "POLLRES@@";
//	private boolean testMode = false;

	private String logPrefix = "WebSocketHandler] ";

	// connect and do nothing
	private WebSocketExpiringMap<String, WebSocket> connectionMap = null;
	/**
	 * Creates a new WebSocketServer with the wildcard IP accepting all connections.
	 */
	public WebSocketHandler(AdapterVO adapterVO, Properties prop) {
		super(new InetSocketAddress(Integer.parseInt(prop.getProperty(PORT_NUMBER))));

		logPrefix = logPrefix + "[" + adapterVO.getAdapterGroupName() + "] ";

		String pollingReq = prop.getProperty(POLLING_REQUEST);
		if (pollingReq != null)
			pollingRequest = pollingReq;
		String pollingRes = prop.getProperty(POLLING_RESPONSE);
		if (pollingRes != null)
			pollingResponse = pollingRes;

		String sConnectionIdleTimeUse = prop.getProperty(CONNECTION_IDLETIME_USE);
		if (sConnectionIdleTimeUse != null && "Y".contentEquals(sConnectionIdleTimeUse)) {
			useConnectionIdle = true;
		}

		String sConnectionIdleTime = prop.getProperty(CONNECTION_IDLETIME);
		int connectionIdleTime = 60;
		if (sConnectionIdleTime != null) {
			try {
				connectionIdleTime = Integer.parseInt(sConnectionIdleTime);
			} catch (Exception ex) {
				connectionIdleTime = DEFAULT_CONNECTION_IDLEOUT;
			}
		}

		String sConnectionIdleInterval = prop.getProperty(CONNECTION_IDLEINTERVAL);
		int connectionIdleInterval = 10;
		if (sConnectionIdleInterval != null) {
			try {
				connectionIdleInterval = Integer.parseInt(sConnectionIdleInterval);
			} catch (Exception ex) {
				connectionIdleInterval = DEFAULT_CONNECTION_IDLEINTERVAL;
			}
		}

		if(logger.isInfo()) {
			logger.info(logPrefix + "- port : " + prop.getProperty(PORT_NUMBER));
			logger.info(logPrefix + "- pollingRequest : " + pollingRequest);
			logger.info(logPrefix + "- pollingResponse : " + pollingResponse);
	
			logger.info(logPrefix + "- useConnectionIdle : " + useConnectionIdle);
			logger.info(logPrefix + "- connectionIdleTime : " + connectionIdleTime);
			logger.info(logPrefix + "- connectionIdleInterval : " + connectionIdleInterval);
		}
		
		this.adapterVO = adapterVO;
		this.prop = prop;

		if (useConnectionIdle) {
			connectionMap = new WebSocketExpiringMap<>(connectionIdleTime, connectionIdleInterval);
		}
	}

	/**
	 * Method handler when a new connection has been opened.
	 */
	@Override
	public void onOpen(WebSocket conn, ClientHandshake handshake) {
		WebSocketSessionManager.getInstance().sessionOpen(adapterVO.getAdapterGroupName(), conn);
		if(logger.isDebug()) logger.debug(logPrefix + "New connection from " + conn.getRemoteSocketAddress());
		if (useConnectionIdle) {
			connectionMap.put(conn.getRemoteSocketAddress().toString(), conn);
		}
	}

	/**
	 * Method handler when a connection has been closed.
	 */
	@Override
	public void onClose(WebSocket conn, int code, String reason, boolean remote) {
		
		if (logger.isDebug()) {
			logger.debug(logPrefix + "Closed connection to " + conn.getRemoteSocketAddress());
		}
		
		ChannelSessionFacade.getInstance().onDetachWebSocketSession(adapterVO.getAdapterGroupName(), conn);
		
		if(useConnectionIdle) {
			connectionMap.remove(conn.getRemoteSocketAddress().toString());

		}
	}

	private void send(WebSocket conn, String message) {
		conn.send(message);
	}

	private void sendAndClose(WebSocket conn, String message) {
		conn.send(message);
		if(logger.isWarn()) logger.warn(logPrefix + "close websocket - " + conn.getRemoteSocketAddress());
		conn.close();
	}

	/**
	 * Method handler when a message has been received from the client.
	 */
	@Override
	public void onMessage(WebSocket conn, String message) {
		
		if (logger.isDebug()) {
			logger.debug(logPrefix + "Received: " + message + " : " + conn.getRemoteSocketAddress().toString());
		}

//		String adapterGroupName = adapterVO.getAdapterGroupName();
//		String response = "";
//		if (message.startsWith("ICONREQ") && message.length() == 57) {
//			// 57 bytes
//			// ICON(4) + REQ/RES(3) + 정상여부(1) + 사용자 ID(9) + UUID(40)
//			String id = message.substring(8, 8 + 9);
//			String uuid = message.substring(8 + 9, 57);
//			String trimingId = StringUtils.trim(id);
//			SessionVO sessionVo = null;
//			String isAsync = PropManager.getInstance().getProperty("LOGIN", "IS_ASYNC", "false");
//			if ("false".equals(isAsync)){
//				sessionVo = SessionManager.getInstance().getLogin(trimingId);
//			}else{
//				int i=0;
//	    		while(true){
//	    			sessionVo = SessionManager.getInstance().getLogin(trimingId);
//	    			if ( sessionVo != null || i > 100){
//	    				if (i > 100 ){
//	    					logger.error("WebSocketHandler] onMessage sessionVo = "+sessionVo + ",count="+i);  
//	    				}else if (100 >= i && i > 0){
//	    					logger.info("WebSocketHandler] onMessage sessionVo = "+sessionVo + ",count="+i);  
//	    				}
//	    				break;
//	    			}
//	    			try {
//						Thread.sleep(20L);
//					} catch (InterruptedException e) {
//						e.printStackTrace();
//					}
//	    			i++;
//	    		}				
//			}
//			
//			if (sessionVo == null) {
//				logger.error(logPrefix + "ICONREQ HTTP login session not found - loginId=(" + trimingId + ") " + conn.getRemoteSocketAddress() + " uuid=" + uuid);
//				response = "ICONRESN" + id + uuid;
//					
//				SessionHistoryLogger.getInstance().log(
//						new SessionHistoryDTO(message, SessionHistoryVO.HISTORYDSTCD_WEBSOCKET, "E", uuid, conn.getRemoteSocketAddress().toString(), false));
//				
//				if (logger.isDebug()) {
//					logger.debug(logPrefix + "ICONREQ response loginId=(" + trimingId + ") " + conn.getRemoteSocketAddress() + " response=" + response);
//				}
//				sendAndClose(conn, response);
//				return;
//			} else {
//				String sid = sessionVo.getUuid();
//				if (logger.isDebug()) {
//					logger.debug(logPrefix + "ICONREQ loginId=(" + trimingId + ") " + conn.getRemoteSocketAddress() + " uuid=" + uuid + " sid=" + sid);
//				}
//				
//				if (sid != null && sid.equals(uuid)) {
//					// websocket 인증
//					if (logger.isDebug()) {
//						logger.debug(logPrefix + "ICONREQ confirm loginId=(" + trimingId + ") " + conn.getRemoteSocketAddress() + " uuid=" + uuid +" sid="+sid);
//					}
//
//		        	ChannelSessionFacade.getInstance().onAttachWebSocketSession(adapterGroupName, conn, trimingId);
//		        	
//		        	SessionHistoryLogger.getInstance().log(
//		        			new SessionHistoryDTO(message, SessionHistoryVO.HISTORYDSTCD_WEBSOCKET, "N", uuid, conn.getRemoteSocketAddress().toString(), false));
//					
//					if(useConnectionIdle) {
//						connectionMap.remove(conn.getRemoteSocketAddress());
//					}
//					
//					response = "ICONRESY" + id + uuid;
//					if (logger.isDebug()) {
//						logger.debug(logPrefix + "ICONREQ response loginId=(" + trimingId + ") " + conn.getRemoteSocketAddress() + " response=" + response);
//					}
//					send(conn, response);
//					return;
//				} else {
//					if (logger.isDebug()) {
//						logger.debug(logPrefix + "ICONREQ uuid not match loginId=(" + trimingId + ") " + conn.getRemoteSocketAddress() + " uuid=" + uuid +" sid="+sid);
//					}
//					response = "ICONRESN" + id + uuid;
//					if (logger.isDebug()) {
//						logger.debug(logPrefix + "ICONREQ response loginId=(" + trimingId + ") " + conn.getRemoteSocketAddress() + " response=" + response);
//					}
//
//					sendAndClose(conn, response);
//					return;
//				}
//			}
//		} else if (message.startsWith(pollingRequest)) {
//			// Check polling session id and refresh cache
//			String connectionId = null;
//
//			// TODO : 로그인정보가 없는 경우에 에러로 처리할 것인지 확인 필요.
//			// 현재 비정상 접속은 강제 종료함(close)
//			try {
//				connectionId = WebSocketSessionManager.getInstance().getWebSocketConnectionId(adapterGroupName, conn);
//				String trimingConnectionId = StringUtils.trim(connectionId);
//				SessionVO sVo = SessionManager.getInstance().getLogin(trimingConnectionId);
//				PropManager pManager  = PropManager.getInstance();
//                String cacheType = pManager.getProperty(SessionManager.SESSION_MANAGER_GROUPNAME, SessionManager.SESSION_MANAGER_CACHETYPE, "EHCACHE");
////                if ("EHCACHE".equals(cacheType)){
//                	logger.info(logPrefix + "POLL loginId=("+ connectionId +") " + conn.getRemoteSocketAddress() + "] refresh LOGIN");
//                	ChannelSessionFacade.getInstance().refreshLoginCache(sVo.getLoginUserId(), sVo);
//                	
////                }else{
////                	logger.info(logPrefix + "POLL loginId=("+ connectionId +") " + conn.getRemoteSocketAddress() + "] skip LOGIN");
////                }
//				response = pollingResponse;
//				if (logger.isDebug()) {
//					logger.debug(logPrefix + "POLL loginId=(" + connectionId + ") " + conn.getRemoteSocketAddress() + ", response=" + response);
//				}
//				send(conn, response);
//			} catch (Exception ex) {
//				logger.warn(logPrefix + "POLL Login Session not found, POLL conn=" + conn + ", loginId=(" + connectionId+") ", ex);
//				conn.close();
//				logger.warn(logPrefix + "POLL Close websocket connection, POLL conn=" + conn + ", loginId=(" + connectionId+") ", ex);
//			}
//		} else {
//			// 책임자 승인 응답거래
//			logger.error(logPrefix + "ApproveResponse message : " + message);
//			// Send to JMSTopic or Ehcache response topic
//			// TODO : 책임자 승인거래인지 확인
//			ExtMessage extMessage = null;
//			try {
//				extMessage = ExtMessageConverter.getExtMessageForStandard(message, "");
//				String traceKey = extMessage.getGuid();
//				; // get from Message Header
////				SessionManager.getInstance().putResponseTopic(traceKey, message);
//				// 동일 WebSocket으로 송수신하므로 불필요한 Cache 사용을 줄임. 
//				Future future = ResponseMap.getInstance().get(traceKey);
//				if (future == null){
//					logger.warn(logPrefix + "ApproveResponse logPrefix setEvent["+traceKey+"] Future not found traceKey=" + traceKey);
//				}
//				else {
//					logger.debug(logPrefix + "ApproveResponse logPrefix setEvent["+traceKey+"] put to Future traceKey=" + traceKey);
//					future.getSlot().put(message);
//				}
//			} catch (Exception e) {
//				response = "Invalid message - format error";
//				logger.error(logPrefix + "ApproveResponse Parsing error " + conn.getRemoteSocketAddress() + " response=" + response, e);
//				sendAndClose(conn, response);
//			}
//		}
	}

	/**
	 * Method handler when an error has occured.
	 */
	@Override
	public void onError(WebSocket conn, Exception ex) {
		if (conn != null) {
			WebSocketSessionManager.getInstance().sessionError(adapterVO.getAdapterGroupName(), conn);
			if (logger.isError()) {
				logger.error(logPrefix + "ERROR from " + conn.getRemoteSocketAddress(), ex);
			}
		} else {
			if (logger.isError()) {
				logger.error(logPrefix + "ERROR " + ex.getMessage(), ex);
			}
		}
	}

	public AdapterVO getAdapterVO() {
		return this.adapterVO;
	}

	public Properties getProp() {
		return this.prop;
	}

	@Override
	public String toString() {
		return "WebSocketHandler [adapterVO=" + adapterVO + ", prop=" + prop + "]";
	}

	public void onStart() {
		// TODO Auto-generated method stub
	}
}