package com.icontrols.bridge.wallpad;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Arrays;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;

import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.icontrols.bridge.common.COMMON_INFO;
import com.icontrols.bridge.common.CommonUtil;
import com.icontrols.bridge.dto.ServiceDetail;

public class MsgServerHandler extends Thread {
	private final Logger logger = LoggerFactory.getLogger(MsgServerHandler.class);

	private Socket clientSocket;
	private String rcvMsgStr; // 입력 받은 XML String

	private long lSocketConnectionPeriod = 0;
	private int TIMEOUT_iMAP_WAIT = 3000; // iMAP 타임아웃
	private int SOCKET_BUFFER_SIZE_READ = 50;

	private final int SOCKET_WAIT_TIME = 3000; // 전송할 때
	private final int SOCKET_BUFFER_SIZE = 100; // 전송할 때

	public MsgServerHandler(Socket clientSocket) {
		this.clientSocket = clientSocket;
	}

	public void run() {
		InputStream is = null;
		InputStreamReader isr = null;
		BufferedReader br = null;
		try {
			is = clientSocket.getInputStream();
			isr = new InputStreamReader(is, "euc-kr");

			clientSocket.setSoLinger(true, 3000);

			br = new BufferedReader(isr);
			rcvMsgStr = "";

			StringBuffer sbRcvMSG = new StringBuffer();
			StringBuffer sbInBuffer = null;

			boolean rcvOK = false;
			lSocketConnectionPeriod = System.currentTimeMillis() + TIMEOUT_iMAP_WAIT;

			while (System.currentTimeMillis() < lSocketConnectionPeriod) {
				try {
					if (br.ready()) {
						try {
							char[] readData = new char[SOCKET_BUFFER_SIZE_READ * 1024];
							sbInBuffer = new StringBuffer();

							while (br.ready()) {
								long charCnt = br.read(readData);
								sbInBuffer.append(new String(readData, 0, (int) charCnt));
							}

							if (sbInBuffer != null || sbInBuffer.toString().trim().length() > 0)
								sbRcvMSG.append(sbInBuffer.toString());

							if (0 < sbInBuffer.toString().indexOf("</imap>")) {
								rcvOK = true;
								logger.info("Client Message Receive DONE!!!");
								break;
							}
						} catch (Exception e) {
							logger.error("Message Read ERROR --> " + e.getMessage());
							return;
						}
					} else {
						Thread.sleep(100);
					}
				} catch (Exception e) {
					logger.error("Message Read Error 2 ...  --> " + e.getMessage());
					clientSocket.close();
					return;
				} finally {
					rcvMsgStr = sbRcvMSG.toString();
				}
			}

			if (!rcvOK) {
				logger.error("FAIL...  iMap message is unacceptable!! : \n\r " + rcvMsgStr);
				clientSocket.close();
				return;
			}

			logger.info(" :: >> " + rcvMsgStr);
		} catch (Exception e) {

		} finally {
			try {
				if (!clientSocket.isClosed())
					clientSocket.close();
			} catch (Exception e) {
			}
			try {
				br.close();
			} catch (Exception e) {
			}
			try {
				isr.close();
			} catch (Exception e) {
			}
			try {
				is.close();
			} catch (Exception e) {
			}
		}

		try {
			rcvMsgStr = rcvMsgStr.substring(rcvMsgStr.indexOf("<")); // 이 부분에 대한 예시 데이터가 있으면 이해하기 좋을거 같다. 테스트 하면서 뽑아내고
																		// 주석으로 잘아보자.
																		// 내가 생각하기엔 앞에 공백이 있을 경우? 혹은 다른 문자가 있을경우를 대비하기
																		// 위함이지 않을까 생각한다.
			// 이벤트명 가져오기
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); // XML 문서를 읽기 위한 Factory 객체 생성
			DocumentBuilder builder = factory.newDocumentBuilder(); // Builder 객체 생성
			InputStream istream = new ByteArrayInputStream(rcvMsgStr.getBytes("UTF-8"));
			Document doc = builder.parse(istream);

			XPath xpath = XPathFactory.newInstance().newXPath();
			Node iMap = (Node) xpath.evaluate("//imap/service", doc, XPathConstants.NODE);

			final String eventName = iMap.getAttributes().getNamedItem("name").getTextContent();

			// 이벤트 명이 null 이거나 공란이면 종료.
			if (eventName == null || eventName.trim().length() == 0)
				return;

			final boolean isContainCompany = rcvMsgStr.contains("<req_company>") ? true : false; // <req_company>UPLUS</req_company>
																									// Element가 있는지
																									// 확인한다.
																									// 없으면 설정파일에 등록된 모든
																									// 서비스에 이벤트를 전송하고
																									// 있으면 해당 Element에
																									// 정의된 서비스에만 전송한다.

			final String midStr = isContainCompany
					? rcvMsgStr.substring(rcvMsgStr.indexOf("<req_company>"), rcvMsgStr.indexOf("</req_company>"))
					: "";

			final String reqStr = isContainCompany ? midStr.substring(midStr.indexOf(">") + 1) : "";
			final String topStr = isContainCompany ? rcvMsgStr.substring(0, rcvMsgStr.indexOf(reqStr)) : "";
			final String bottomStr = isContainCompany ? rcvMsgStr.substring(rcvMsgStr.indexOf("</req_company>")) : "";

			String[] companies = reqStr.split("_");

			// 서비스 목록 가져오기
			for (String companyName : companies) {
//	    	for(JsonElement jsonObj : COMMON_INFO.propList) {
//	    		ServiceDetail service = new Gson().fromJson(jsonObj.toString(), ServiceDetail.class);
				ServiceDetail service = COMMON_INFO.serviceDtailList.get(companyName);
				if (service == null)
					continue;

				if (!Arrays.asList(service.getEvents()).contains(eventName))
					continue;

				StringBuffer sb = new StringBuffer();

				if (isContainCompany)
					rcvMsgStr = sb.append(topStr).append(service.getName()).append(bottomStr).toString();

				logger.debug("최종 - \r\n" + rcvMsgStr);

				Runnable task = () -> {
					Socket danziSocket = null;
					DataOutputStream outStream = null;
					try {
						danziSocket = new Socket();
						danziSocket.setSoLinger(true, 3000);
						danziSocket.setSendBufferSize(SOCKET_BUFFER_SIZE * 1024);
						danziSocket.setSoTimeout(SOCKET_WAIT_TIME);

						InetSocketAddress iSocAddr = new InetSocketAddress(service.getAddress(), service.getPort());
						danziSocket.connect(iSocAddr, SOCKET_WAIT_TIME);

						outStream = new DataOutputStream(danziSocket.getOutputStream());
						String sendMsg = rcvMsgStr + "\r\n";
						byte[] outBF = sendMsg.getBytes("euc-kr");

						outStream.write(outBF);
						outStream.flush();

						StringBuffer logSB = new StringBuffer();

						logSB.append("   S E N D    T O " + service.getPort() + "  C O M P L I T E ! ! ! !  \n");

						logger.info(logSB.toString());
					} catch (Exception e) {
						// TODO Auto-generated catch block

						StringBuffer errSB = new StringBuffer();

						errSB.append("\n***************************************************\n")
								.append("   F A I L   T O " + service.getPort()
										+ "   S E N D   E V E N T   ! ! ! !  \n")
								.append("   ERROR MESSAGE = " + e.getMessage() + " \n")
								.append("***************************************************");

						logger.error(errSB.toString());

						e.printStackTrace();
					} finally {
						try {
							Thread.sleep(100);
							outStream.close();
							danziSocket.close();
						} catch (Exception e) {
							e.printStackTrace();
						}
					}
				};
				new Thread(task).start();
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			logger.error("[ERROR] IOEXCEPTION-" + e.getMessage());
		} catch (Exception e) {
			logger.error("[ERROR] EXCEPTION - " + e.getMessage());
			e.printStackTrace();
		}
	}
}