package gmt.io.server;

import gmt.common.type.LogLevelType;
import gmt.common.type.MonitorUpdateType;
import gmt.common.type.NetStatType;
import gmt.common.type.SocketType;
import gmt.common.util.GmtUtils;
import gmt.common.vo.MonitorInfoVO;
import gmt.common.vo.MsgObjVO;
import gmt.common.vo.NetStatVO;
import gmt.io.server.event.GmtServerEventListener;
import gmt.io.server.vo.ConnObjVO;
import gmt.io.server.vo.ServiceClientVO;
import gmt.logger.GmtLogManager;
import gmt.protocol.event.GmtEncryptEventListener;
import ipworks.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Queue;
import java.util.TooManyListenersException;
import java.util.concurrent.LinkedBlockingQueue;

public class GmtServer {
	
	private Ipdaemon ipdaemon;
	private GmtServerEventListener eventListener;
	private GmtEncryptEventListener encryptListener;
	private GmtEncryptEventListener decryptListener;
	private GmtLogManager   logger;
	private int				id;
	private int				tag;
	private String			name;
	private int				port;
	private boolean			use;
	private int				recvMsgCnt;
	private LocalDateTime	recvdt;
	private LocalDateTime	senddt;
	private boolean			chkRecvDt;
	private boolean			singleConn;
	private boolean			allowAny;
	private boolean			lineMode;	
	private boolean			reconnUse;
	private int				interval;
	private boolean			doResend;
	private boolean			isRunning;	
	private Queue<MsgObjVO>	recvQ;
	private Queue<MsgObjVO>	sendQ;
	private MonitorInfoVO   monitorInfoVO;
	private Thread			sendThread;
	private HashMap<String, ServiceClientVO> serviceClientMap;
	private int 			maxWord;
	private ArrayList<ConnObjVO>	allowList;
	private ArrayList<ConnObjVO>	reJectList;
	
	public GmtServer(Queue<MsgObjVO> recvQ, GmtServerEventListener gmtServerEventListener) {
		try {
//			GmtLogManager.setpath("");
			
			ipdaemon 	  	= new Ipdaemon();			
			ipdaemon.setRuntimeLicense("31504A42564131535542524131535542414E44323132323200000000000000000000000000000000505A4E384E5758350000325242435038313258424E480000");
			String eol = new String(ipdaemon.getDefaultEOL());
			eventListener 	= gmtServerEventListener;
			logger          = GmtLogManager.getInstance();
			id 				= -1;
			tag 			= 0;
			name 			= "";

			lineMode 		= true;
			recvMsgCnt 		= 0;
			recvdt 			= LocalDateTime.now();
			senddt 			= LocalDateTime.now();
			reconnUse 		= true;
			interval 		= 60;
			doResend		= false;
			isRunning 		= true;
			monitorInfoVO 	= new MonitorInfoVO();
			allowList		= new ArrayList<ConnObjVO>();
			reJectList		= new ArrayList<ConnObjVO>();
			this.recvQ 		= recvQ;
			maxWord = 65535;
			
			
			sendQ = new LinkedBlockingQueue<MsgObjVO>(100000);
			serviceClientMap = new HashMap<String, ServiceClientVO>();
			try {
				ipdaemon.setDefaultEOL("\r\n".getBytes());
			} catch (IPWorksException e2) {
				// TODO Auto-generated catch block
				e2.printStackTrace();
			}
			ipdaemon.addIpdaemonEventListener(new DefaultIpdaemonEventListener() {
				@Override
				public void SSLClientAuthentication(IpdaemonSSLClientAuthenticationEvent arg0) {
					if (eventListener != null) {
						eventListener.SSLClientAuthentication(port);
					}
				}
				
				@Override
				public void connected(IpdaemonConnectedEvent e) {
					ConnectionMap connections = ipdaemon.getConnections();
					Connection connection = (Connection) connections.get(e.connectionId);
					if(e.statusCode!=0) {
						try {
							ipdaemon.disconnect(e.connectionId);
							logger.writeLevelLog("[Server] IP : "+connection.getRemoteHost()+" | PORT :"+connection.getRemotePort()+"SC Connect Status Fail.. ", LogLevelType.LOG_ERROR, "AllLog");
						} catch (IPWorksException e1) {
						}
					}
					
					if (lineMode) {
						
					}
					
					ServiceClientVO serviceClientVO = new ServiceClientVO();
					String SCKey = connection.getRemoteHost()+"|"+connection.getRemotePort()+"|"+connection.getConnectionId();
					
					String connId = connection.getConnectionId();
					MonitorInfoVO connectedScInfo = serviceClientVO.getMonitorInfoVO();
					connectedScInfo.setConnected(true);
					connectedScInfo.setCheckRecvDT(chkRecvDt);
					connectedScInfo.setDisConnInterval(interval);
					connectedScInfo.setLastRecvDT(LocalDateTime.now());
					serviceClientVO.setInfo(id, port, connId, connection.getRemoteHost(), connection.getRemotePort(), getConnectionName(connection.getRemoteHost()));					
					serviceClientMap.put(SCKey, serviceClientVO);
					if (eventListener != null) {
						eventListener.connected(port, connection.getRemoteHost(), connection.getRemotePort());
					}
				}

				@Override
				public void dataIn(IpdaemonDataInEvent e) {
					ConnectionMap connections = ipdaemon.getConnections();
					Connection connection = (Connection) connections.get(e.connectionId);
					String remoteHost = connection.getRemoteHost();
					int remotePort = connection.getRemotePort();
					processRcvData(e,remoteHost,remotePort);
					if (eventListener != null) {
						eventListener.dataIn(port, remoteHost, remotePort, new String(e.text));
					}
				}

				@Override
				public void disconnected(IpdaemonDisconnectedEvent e) {
					
					ConnectionMap connections = ipdaemon.getConnections();
					Connection connection = (Connection) connections.get(e.connectionId);
					if(connection != null) {
						String remoteHost = connection.getRemoteHost();
						int    remotePort = connection.getRemotePort();
						String SCKey = remoteHost+"|"+remotePort+"|"+connection.getConnectionId();
						serviceClientMap.get(SCKey).getReSendQ().clear();
						serviceClientMap.remove(SCKey);
						SCKey = null;
						if (eventListener != null) {
							eventListener.disconnected(port, remoteHost, remotePort);
						}
					}
				}
				
				@Override
				public void error(IpdaemonErrorEvent e) {					
					ConnectionMap connections = ipdaemon.getConnections();
					Connection connection = (Connection) connections.get(e.connectionId);
					if(connection != null) {
						String remoteHost = connection.getRemoteHost();
						int    remotePort = connection.getRemotePort();
						if (eventListener != null) {
							eventListener.error(port, remoteHost, remotePort, e.errorCode, e.description);
						}
					}					
				}
				/**
				 * AllowAny Ȯ�� ��, Ture : RejectList�� �ִ� ��츦 ������ ��� ���� ���/ False: AllowList�� �ִ� ��쿡�� ���� ���
				 * 
				 */
				@Override
				public void connectionRequest(IpdaemonConnectionRequestEvent e) {
					int i = 0;
					e.accept = false;
					
					int rejectSize = reJectList.size();
					for(i=0;i<rejectSize;i++) {
						if(reJectList.get(i).getIp().equals(e.address)) {
							e.accept = false;
							logger.writeLevelLog("[PORT "+ String.valueOf(port) +"/"+ name +"]"+ e.address +" Connect Not Allowed.. " , LogLevelType.LOG_INFO, "AllLog");
							return;
						}
					}
					
					if(allowAny==true) {
						e.accept = checkSingleConn(e.address);
						return;
					}else {
						int allowSize = allowList.size();
						for(i=0;i<allowSize;i++) {
							if(allowList.get(i).getIp().equals(e.address)) {
								e.accept = checkSingleConn(e.address);
								return;
							}
						}
						//WriteLog('[PORT ' + IntToStr(FServerInfo.Port) + '/' + FServerInfo.Name+']' + Address + ' Connect Not Allowed.. ', LOG_INFO);
						logger.writeLevelLog("[PORT "+ String.valueOf(port) +"/"+ name +"]"+ e.address +" Connect Not Allowed.. " , LogLevelType.LOG_INFO, "AllLog");
					}
				}
				
			});		
			
			sendThread = new Thread(new Runnable() {			
				@Override
				public void run() {
					while(isRunning) {
						try {
							serverSendMsg();
							reSendMsg();
							Thread.sleep(1);
						} catch (InterruptedException e) {
							logger.writeLevelLog("[Server] SendThread Error "+GmtUtils.getStatckTrace(e), LogLevelType.LOG_ERROR, "AllLog");
						}
					}
					
				}
			});		
			sendThread.start();
		}catch (TooManyListenersException e) {
			logger.writeLevelLog("[Server] GmtServer Error "+GmtUtils.getStatckTrace(e), LogLevelType.LOG_ERROR, "AllLog");
		}
	}
	
	/**
	 * Server.xm
	 * l�� ���ǵ� singleConn�� ���� singleConn ���� /
	 * singleConn : singleConnection ����. True : �ش� IP�� 1���� ServiceClient�� ���� ���, False: �ټ� ���
	 * @param address : ���� �õ��� ServiceClient�� ip�ּ�
	 * @return boolean
	 * 
	 */
	protected boolean checkSingleConn(String address) {
		boolean result = true;
		if(singleConn) {
			ConnectionMap connections = ipdaemon.getConnections();
			Object[] keys = connections.keySet().toArray();
			int keySize = keys.length;
			for(int i = 0; i < keySize; i++) {
				Connection connection = (Connection) connections.get(keys[i]);
				if(connection.getRemoteHost().equals(address)) {
					try {
						ipdaemon.disconnect(keys[i].toString());
						logger.writeLevelLog("[Server] checkSingleConn Disconnect IP : "+address, LogLevelType.LOG_INFO, "AllLog");
					} catch (IPWorksException e) {
						logger.writeLevelLog("[Server] checkSingleConn Error "+GmtUtils.getStatckTrace(e), LogLevelType.LOG_ERROR, "AllLog");
					}
				}
			}
		}
		return result;
	}
	
	/**
	 * ���޹��� IP�� ���� Service Client�� �̸��� �˻�. 
	 * AllowClient.xml ���Ͽ� ������ IP �� �̸��� ���
	 * �ش� IP�� ���� �̸��� ���� ��� 'Any Client'�̸��� ����
	 * @param address : ���� �õ��� ServiceClient�� ip�ּ�
	 * @return String
	 * 
	 */
	protected String getConnectionName(String address) {
		String result = "";
		if (allowList != null) {
			for (int i=0; i < allowList.size(); i++) {
				if (allowList.get(i).getIp().equals(address)) {
					result = allowList.get(i).getIp();
					break;
				}
			}
		}
		
		if (result.equals("")) {
			result = "Any Client";
		}
		
		return result;
	}

	/**
	 * �������� Data�� Recv���� ��� ����
	 * @param e
	 * @param remoteHost : ServiceClient IP�ּ�
	 * @param remotePort : ServiceClient Port
	 */
	public void processRcvData(IpdaemonDataInEvent e, String remoteHost, int remotePort) {
		if (recvQ == null) {
			return;
		}
		
		MsgObjVO vo = new MsgObjVO();
				
		vo.setMsgSeq(0);
		vo.setRecvIP(remoteHost);
		vo.setRecvPort(remotePort);
		vo.setRecvID(id);
		vo.setRecvTag(tag);
		vo.setRecvDT(LocalDateTime.now());
		vo.setRecvServerPort(port);			
//		vo.setMsg(new String(e.text));
		vo.setMsg(e.text);
		vo.setRecvSocketType(SocketType.SERVER);
		recvMsgCnt = recvMsgCnt +1;
		recvdt     = LocalDateTime.now();
		String SCKey = remoteHost+"|"+remotePort+"|"+e.connectionId;
		updateScMonitorInfo(vo.getMsg(), MonitorUpdateType.RECV,SCKey);
		updateMonitorInfo(vo.getMsg(), MonitorUpdateType.RECV);	
		
		if (decryptListener != null) {
			vo.setMsg(decryptListener.getByteArray(this.id, vo.getMsg()));
		}
		recvQ.offer(vo);
		vo = null;
		SCKey = null;
	}	
	
	/**
	 * ResendQ�� ������� ���, sendMsg���� ����(msg�Ϲ� ����) WouldBlock�� �ش� ServiceClient�� ResendQ�� �޽��� ���� (Resend�� ��� �ϴ� Server��)/ ResendQ�� �����Ͱ� ���� ���, Resend ����
	 */
	public void serverSendMsg() {
		try {
			int resultCode = -1;
			int cnt = sendQ.size();
			
			if (cnt > 1000) {
				cnt = 1000; 
			}
			
			for (int i = 0; i <= cnt-1; i++) {
				MsgObjVO vo = sendQ.poll();
				
				if (vo == null) {
					continue;
				}
				
				Connection connection = ipdaemon.getConnections().get(vo.getDestID());
				
				if (connection != null && connection.getConnected()) {
					
					String SCKey = connection.getRemoteHost()+"|"+connection.getRemotePort()+"|"+connection.getConnectionId();
					
					// ������ Queue�� �޽����� ����ִ��� �˻�
					if(serviceClientMap.get(SCKey).getReSendQ().isEmpty()) {
						if(serviceClientMap.get(SCKey).getReSendQ().size() == 0) {
							//���� �������� �޽��� ����
//							String msg = new String(vo.getMsg().getBytes(), "EUC-KR");
//							resultCode = sendMsg(vo.getDestID(), msg);
							
							resultCode = sendMsg(vo.getDestID(),vo.getMsg());
							
							ServiceClientVO targetServiceClientVO = serviceClientMap.get(SCKey);
							if(targetServiceClientVO==null) {
								return;
							}
							updateNetStat(resultCode, SCKey);
	
							switch(resultCode) {
							case GmtUtils.NET_SEND_OK :
								updateScMonitorInfo(vo.getMsg(), MonitorUpdateType.SEND,SCKey);
								updateMonitorInfo(vo.getMsg(), MonitorUpdateType.SEND);		
								break;
							default:
								if (doResend) {
									targetServiceClientVO.getReSendQ().offer(vo);
								}								
								break;
							}
						}
					} else {
						//������ Queue�� �޽����� ������, ���� �޽����� ������ Queue�� �����ϰ� ������ Queue�� ù ���� �޽����� �����Ѵ�.
						ServiceClientVO targetServiceClientVO = serviceClientMap.get(SCKey);
						targetServiceClientVO.getReSendQ().offer(vo);
						
						Duration duration = Duration.between(targetServiceClientVO.getNetStatVO().getErrorTime(), LocalDateTime.now());
						//���� Error ������ 10ms �̸��� ��� ���۽õ��� �����ش�.
						if (duration.toMillis() < 10) {
							continue;
						}
						duration = Duration.between(targetServiceClientVO.getNetStatVO().getWouldBlockFromTime(), LocalDateTime.now());
						if (duration.toMillis() < 10) {
							continue;
						}
						MsgObjVO resendVO = targetServiceClientVO.getReSendQ().peek();
						resultCode = sendMsg(resendVO.getDestID(), resendVO.getMsg());
						
						updateNetStat(resultCode, SCKey);

						switch(resultCode) {
						case GmtUtils.NET_SEND_OK :
							updateScMonitorInfo(resendVO.getMsg(), MonitorUpdateType.SEND,SCKey);
							updateMonitorInfo(resendVO.getMsg(), MonitorUpdateType.SEND);
							MsgObjVO garbageVO = targetServiceClientVO.getReSendQ().poll();
							garbageVO = null;
							break;
						default:
							break;
						}
//						reSendMsg();
						duration = null;
					}
					SCKey = null;
				}
				vo = null;
			}
		} catch (Exception e) {
			logger.writeLevelLog("[Server] serverSendMsg Error "+GmtUtils.getStatckTrace(e), LogLevelType.LOG_ERROR, "AllLog");
		}
	}
	
	/**
	 * sendMsg : �޽��� ���� ����, WouldBlock�� �ش� ServiceClient�� ResendQ�� �޽��� ���� (Resend�� ��� �ϴ� Server��)
	 * @param msg
	 */
	public int sendMsg(String connID, byte[] msg) {		
		if(msg.equals("".getBytes())) {
			return -1;
		}
		int resultCode = GmtUtils.NET_SEND_OK;		
		Connection connection = ipdaemon.getConnections().get(connID);
		if (connection != null && connection.getConnected()) {
			try {
				/*
				if (lineMode) {
					msg = msg + "\r\n";
				}
				*/
				if (encryptListener != null) {
					msg = encryptListener.getByteArray(this.id, msg);
				}
//				connection.setDataToSend(msg.getBytes("EUC-KR"));\
				/*stringMsg = msg.replace(System.getProperty("line.separator"), "ASASASAS");*/
				
				connection.setDataToSend(msg);
				if (lineMode) {
					connection.setDataToSend("\r\n".getBytes());
				}
				
			} catch (IPWorksException e) {
				if (!doResend && e.getCode() == GmtUtils.NET_WOULD_BLOCK) {
					resultCode = GmtUtils.NET_SEND_OK;
				} else {
					resultCode = e.getCode();
					/*
					if (resultCode == GmtUtils.NET_WOULD_BLOCK) {
						logger.writeLevelLog("[SERVER]["+ name +":"+ String.valueOf(port) +"] [" + connection.getRemoteHost() + ":" + connection.getRemotePort() + "] "+ e.getMessage(), 
								             "AllLog", LogLevelType.LOG_WARN
								             );
					}
					*/
				}
				return resultCode;
			} catch (Exception e) {
				logger.writeLevelLog("[Server] sendMsg Error "+GmtUtils.getStatckTrace(e), LogLevelType.LOG_ERROR, "AllLog");
			}
		}
		return resultCode;
	}
	
	/**
	 * ServiceClient�� ResendQ���ִ� �޽��� ������ ����
	 */
	public void reSendMsg() {
		try {
			if(serviceClientMap.size()!=0) {			
				Object[] keys = serviceClientMap.keySet().toArray();	
				int keySize = keys.length;
				MsgObjVO vo;
				
				for(int i = 0; i<keySize ;i++) {
					
					ServiceClientVO targetServiceClientVO = serviceClientMap.get(keys[i]);
					
					if(!targetServiceClientVO.getReSendQ().isEmpty()) {
						Duration duration = Duration.between(targetServiceClientVO.getNetStatVO().getErrorTime(), LocalDateTime.now());
						if (duration.toMillis() < 10) {
							continue;
						}
						
						duration = Duration.between(targetServiceClientVO.getNetStatVO().getWouldBlockFromTime(), LocalDateTime.now());
						if (duration.toMillis() < 10) {
							continue;
						}
						
						int sendCnt = targetServiceClientVO.getReSendQ().size();
						
						if (sendCnt > 10) {
							sendCnt = 10;
						}
						
						for (int j = 0; j < sendCnt; j++) {
							vo = targetServiceClientVO.getReSendQ().peek();
							if(vo != null) {
//								String reSendMsg =vo.getMsg(); 
								byte[] reSendMsg = vo.getMsg();
								int resultCode = 0;
								
								resultCode = sendMsg(vo.getDestID(), reSendMsg);
								updateNetStat(resultCode, keys[i]);
								
								switch(resultCode) {
								case GmtUtils.NET_SEND_OK :
									updateScMonitorInfo(reSendMsg, MonitorUpdateType.SEND, keys[i].toString());
									updateMonitorInfo(reSendMsg, MonitorUpdateType.SEND);
									MsgObjVO garbageVO = targetServiceClientVO.getReSendQ().poll();
									garbageVO = null;
									break;
								default:
									break;
								}
							}
						}
						vo = null;
						duration = null;
					}
					targetServiceClientVO = null;
				}
				keys = null;
			}
		}catch(Exception e) {
			logger.writeLevelLog(GmtUtils.getStatckTrace(e), LogLevelType.LOG_ERROR, "AllLog");
		}
	}
	
	/**
	 * vo�� �����͸� SendQ�� ����
	 * @param vo : �޽��� ����
	 */
	public void addSendMsg(MsgObjVO vo) {
		
		if(vo != null && sendQ != null) {
			if (sendQ.size() >= 100000) {
				MsgObjVO garbageVO = sendQ.poll();
				garbageVO = null;
			}
			sendQ.offer(vo);			
		}
	}
	
	/**
	 * �ش� ServiceClient�� network���� ������Ʈ
	 * @param resultCode : ���� ���  /// resultCode 0: �޽��� ���� ���� / 135: WouldBlock�߻� / Other : �޽��� ���� ����
	 * @param keys : ServiceClient�� key
	 */
	public void updateNetStat(int resultCode, Object keys) {
		LocalDateTime tempdate = GmtUtils.defaultTime();
		NetStatVO targetNetStatVO = serviceClientMap.get(keys.toString()).getNetStatVO();
		if (targetNetStatVO == null) {
			return;
		}
		switch(resultCode) {
		case GmtUtils.NET_SEND_OK:
			targetNetStatVO.setStat(NetStatType.SEND_OK);
			targetNetStatVO.setWouldBlockFromTime(tempdate);
			targetNetStatVO.setWouldBlockToTime(tempdate);
			targetNetStatVO.setDescription("");
			targetNetStatVO.setLastSendTime(LocalDateTime.now());
			targetNetStatVO.setRetryCount(0);			
			break;
		case GmtUtils.NET_WOULD_BLOCK:
			targetNetStatVO.setStat(NetStatType.WOULDBLOCK);
			
			if(targetNetStatVO.getWouldBlockFromTime().isEqual(tempdate)) {
				targetNetStatVO.setWouldBlockFromTime(LocalDateTime.now());
			}
			
			targetNetStatVO.setWouldBlockToTime(LocalDateTime.now());
			targetNetStatVO.setDescription("Operation Would Block");
			
			if(targetNetStatVO.getLastSendTime().isEqual(tempdate)) {
				targetNetStatVO.setLastSendTime(LocalDateTime.now());
			}
			break;
		default:
			targetNetStatVO.setStat(NetStatType.ERROR);
			targetNetStatVO.setWouldBlockFromTime(tempdate);
			targetNetStatVO.setWouldBlockToTime(tempdate);
			targetNetStatVO.setErrorTime(LocalDateTime.now());
			targetNetStatVO.setDescription("");
			targetNetStatVO.setLastSendTime(LocalDateTime.now());
			if(targetNetStatVO.getRetryCount() == 0) {
				targetNetStatVO.setRetryCount(targetNetStatVO.getRetryCount()+1);
				targetNetStatVO.setLastSendTime(LocalDateTime.now());
			}
			break;
		}
//		targetNetStatVO = null;
	}
	/**
	 * ����� �Ǵ� �ϳ��� ServiceClient ���� ����
	 * @param serviceClientIP : target ServiceClient IP
	 * @param serviceClientPort : target ServiceClient Port
	 * 
	 */
	public void closeServiceClient(String serviceClientIP, int serviceClientPort) {
		try {
			ConnectionMap connections = ipdaemon.getConnections();
			Object[] keys = connections.keySet().toArray();
			int keySize = keys.length;
			if (keySize > 0) {
				for(int i = 0; i < keySize; i++) {
					Connection connection = (Connection) connections.get(keys[i]);
					if(connection.getRemoteHost().equals(serviceClientIP)&&connection.getRemotePort()==serviceClientPort) {
						ipdaemon.disconnect(keys[i].toString());
						logger.writeLevelLog("[Server] IP : "+serviceClientIP+"| PORT : "+serviceClientPort+" Disconnect", LogLevelType.LOG_INFO, "AllLog");
						break;
					}
				}
			}
			connections = null;
			keys = null;
		} catch (IPWorksException e) {
			logger.writeLevelLog("[Server] closeServiceClient Error "+GmtUtils.getStatckTrace(e), LogLevelType.LOG_ERROR, "AllLog");
		}
	}
	
	/**
	 * �ش� ServiceClient�� ip�� ����ϴ� ��� ���� ���� 
	 * @param targetIP : target ServiceClient IP
	 * 
	 */
	public void closeServiceClient(String targetIP) {
		try {
			ConnectionMap connections = ipdaemon.getConnections();
			Object[] keys = connections.keySet().toArray();
			int keySize = keys.length;
			if (keySize > 0) {
				for(int i = 0; i < keySize; i++) {
					Connection connection = (Connection) connections.get(keys[i]);
					if(connection.getRemoteHost().equals(targetIP)) {
						ipdaemon.disconnect(keys[i].toString());
						logger.writeLevelLog("[Server] IP : "+targetIP+" Disconnect", LogLevelType.LOG_INFO, "AllLog");
					}
				}
			}
			connections = null;
			keys = null;
		} catch (IPWorksException e) {
			logger.writeLevelLog("[Server] closeServiceClient Error "+GmtUtils.getStatckTrace(e), LogLevelType.LOG_ERROR, "AllLog");
		}
	}
	
	/**
	 * �ش� ConnectionId�� ����ϴ� ServiceClient �ϳ� ���� ����
	 * @param connID
	 */
	public void closeServiceClientOne(String connID) {
		try {
			ipdaemon.disconnect(connID);
			logger.writeLevelLog("[Server] Disconnect", LogLevelType.LOG_INFO, "AllLog");
		} catch (IPWorksException e) {
			logger.writeLevelLog("[Server] closeServiceClient Error "+GmtUtils.getStatckTrace(e), LogLevelType.LOG_ERROR, "AllLog");			
		}
	}
	
	public void Listen(int port) throws Exception{
		if (lineMode) {
			ipdaemon.setDefaultEOL("\r\n".getBytes());
		} else {
			ipdaemon.setDefaultEOL("");
		}
		ipdaemon.config("BindExclusively=false");
		ipdaemon.setLocalPort(port);
		ipdaemon.setListening(true);		
	}
	
	/**
	 * Server�� MonitorInfo ����
	 * @param msg : �޽��� ����
	 * @param monitorType : SEND : �۽Ž� / RECV : ���Ž� / UPDATE : ������Ʈ
	 */
	public synchronized void updateMonitorInfo(byte[] msg, MonitorUpdateType monitorType) {
		try {
		switch(monitorType) {
			case SEND:
				monitorInfoVO.increseSendCount(1);
				monitorInfoVO.increseSendByte(msg.length);
				monitorInfoVO.setLastSendDT(LocalDateTime.now());
				break;
				
			case RECV:
				monitorInfoVO.increseRecvCount(1);
				monitorInfoVO.increseRecvByte(msg.length);
				monitorInfoVO.setLastRecvDT(LocalDateTime.now());
				break;
				
			case UPDATE:
				monitorInfoVO.settleUpMonitorInfo();
				break;
			}
		} catch(Exception e) {
			logger.writeLevelLog("[Server] updateMonitorInfo Error "+GmtUtils.getStatckTrace(e),LogLevelType.LOG_ERROR, "AllLog");	
		}
	}
	
	/**
	 * ServiceClient�� MonitorInfo ����, �ϳ��� ServiceClient�� MonitorInfo�� ����
	 * @param msg �޽��� ����
	 * @param monitorType SEND : �۽Ž� / RECV : ���Ž� / UPDATE : ������Ʈ
	 * @param SCKey ServiceClient Key
	 */
	public void updateScMonitorInfo(byte[] msg, MonitorUpdateType monitorType, String SCKey) {
		try {
			ServiceClientVO targetServiceClientVO = serviceClientMap.get(SCKey);
			if(targetServiceClientVO == null) {
				return;
			}
			switch(monitorType) {
			case SEND:
				targetServiceClientVO.getMonitorInfoVO().increseSendCount(1);
				targetServiceClientVO.getMonitorInfoVO().increseSendByte(msg.length);
				targetServiceClientVO.getMonitorInfoVO().setLastSendDT(LocalDateTime.now());
				int lastSq = targetServiceClientVO.getMonitorInfoVO().getLastSequence();
				if(lastSq==maxWord) {
					targetServiceClientVO.getMonitorInfoVO().setLastSequence(0);
				}else {
					targetServiceClientVO.getMonitorInfoVO().setLastSequence(lastSq+1);
				}
				break;
				
			case RECV:
				targetServiceClientVO.getMonitorInfoVO().increseRecvCount(1);
				targetServiceClientVO.getMonitorInfoVO().increseRecvByte(msg.length);
				targetServiceClientVO.getMonitorInfoVO().setLastRecvDT(LocalDateTime.now());
				break;
				
			case UPDATE:
				targetServiceClientVO.getMonitorInfoVO().settleUpMonitorInfo();
				break;
			}
			targetServiceClientVO = null;
		} catch(Exception e) {
			logger.writeLevelLog("[Server] updateScMonitorInfo Error "+GmtUtils.getStatckTrace(e), LogLevelType.LOG_ERROR, "AllLog");
		}
	}
	
	public ConnectionMap getConnections() {
		return ipdaemon.getConnections();
	}
	
	public int getSCReSendQCnt(String SCKey) {
		return serviceClientMap.get(SCKey).getReSendQ().size();
	}
	
	public NetStatVO getClientNetStatVO(String SCKey) {
		return serviceClientMap.get(SCKey).getNetStatVO();
	}
	
	public MonitorInfoVO getMonitorInfoVO(String SCKey) {
		return serviceClientMap.get(SCKey).getMonitorInfoVO();
	}

	public LocalDateTime getwouldBlockFromTime(String SCKey) {
		return serviceClientMap.get(SCKey).getNetStatVO().getWouldBlockFromTime();
	}
	public LocalDateTime getwouldBlockToTime(String SCKey) {
		return serviceClientMap.get(SCKey).getNetStatVO().getWouldBlockToTime();
	}
	
	public MonitorInfoVO getServerMonitorInfoVO() {
		return monitorInfoVO;
	}
	
	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public int getTag() {
		return tag;
	}

	public void setTag(int tag) {
		this.tag = tag;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getPort() {
		return port;
	}

	public void setPort(int port) {
		this.port = port;
	}

	public boolean isLineMode() {
		return lineMode;
	}

	public void setLineMode(boolean lineMode) {
		this.lineMode = lineMode;
	}

	public int getRecvMsgCnt() {
		return recvMsgCnt;
	}

	public void setRecvMsgCnt(int recvMsgCnt) {
		this.recvMsgCnt = recvMsgCnt;
	}

	public LocalDateTime getRecvdt() {
		return recvdt;
	}

	public void setRecvdt(LocalDateTime recvdt) {
		this.recvdt = recvdt;
	}

	public LocalDateTime getSenddt() {
		return senddt;
	}

	public void setSenddt(LocalDateTime senddt) {
		this.senddt = senddt;
	}

	public boolean isReconnUse() {
		return reconnUse;
	}

	public void setReconnUse(boolean reconnUse) {
		this.reconnUse = reconnUse;
	}

	public int getInterval() {
		return interval;
	}

	public void setInterval(int interval) {
		this.interval = interval;
	}

	public boolean isDoResend() {
		return doResend;
	}

	public void setDoResend(boolean doResend) {
		this.doResend = doResend;
	}
	
	public void setEventListener(GmtServerEventListener eventListener) {
		this.eventListener = eventListener;
	}

	public boolean isRunning() {
		return isRunning;
	}

	public void setRunning(boolean isRunning) {
		this.isRunning = isRunning;
	}

	public boolean isUse() {
		return use;
	}

	public void setUse(boolean use) {
		this.use = use;
	}

	public boolean isChkRecvDt() {
		return chkRecvDt;
	}

	public void setChkRecvDt(boolean chkRecvDt) {
		this.chkRecvDt = chkRecvDt;
	}

	public boolean isSingleConn() {
		return singleConn;
	}

	public void setSingleConn(boolean singleConn) {
		this.singleConn = singleConn;
	}

	public boolean isAllowAny() {
		return allowAny;
	}

	public void setAllowAny(boolean allowAny) {
		this.allowAny = allowAny;
	}
	public int getConnectionsCount() {
		return ipdaemon.getConnections().size();
	}
	
	public void setAllowList(String allowName, String allowIp, int allowServerPort) {		
		ConnObjVO tempConnObjVO = new ConnObjVO();
		tempConnObjVO.setName(allowName);
		tempConnObjVO.setIp(allowIp);
		allowList.add(tempConnObjVO);
		tempConnObjVO = null;
	}
	
	public void setRejectList(String reJectNames, String reJectIp, int reJectServerPort) {
		ConnObjVO tempConnObjVO = new ConnObjVO();
		tempConnObjVO.setName(reJectNames);
		tempConnObjVO.setIp(reJectIp);
		reJectList.add(tempConnObjVO);
		tempConnObjVO = null;
	}
	
	public String getServerMsgBuffer(String SCKey) {
		return serviceClientMap.get(SCKey).getMsgBuffer();		
	}

	public void setServerMsgBuffer(String SCKey, String msgBuffer) {
		serviceClientMap.get(SCKey).setMsgBuffer(msgBuffer);
	}

	public boolean setServiceClientUserInfo(int serverPort, String SCKey, String userInfo) {
		boolean result = false;
		if(serviceClientMap.get(SCKey)!=null) {
			serviceClientMap.get(SCKey).setUserInfo(userInfo);
			result = true;
		}
		return result;
	}
	
	public ServiceClientVO getServiceClientInfo(String SCKey) {
		return serviceClientMap.get(SCKey);
	}

	public int getServerSendQCount() {
		return sendQ.size();
	}
	
	public int getServerBufferCount() {
		return sendQ.size();
	}
	
	public void setEncryptEventListener(GmtEncryptEventListener encryptListener) {
		this.encryptListener = encryptListener;
	}
	
	public void setDecryptEventListener(GmtEncryptEventListener decryptListener) {
		this.decryptListener = decryptListener;
	}

}
