package kr.co.icontrols.ilp300_700.ethernet;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

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 javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import kr.co.icontrols.ilp300_700.ethernet.iMAPCommon.iMAPResult;
import kr.co.icontrols.ilp300_700.global.Configure.ConfigValue;
import kr.co.icontrols.ilp300_700.global.Configure.SetValue;

public class SendNWaitMSG {

    String TAG = "SendNWaitMSG";

    public final int HANDLE_MSG_DONE_REPLY = 100;
    public final int HANDLE_MSG_DONE_SEND = 200;
    public final int HANDLE_MSG_ING = 300;
    public final int HANDLE_MSG_UNREACHABLE = 500;
    public final int HANDLE_MSG_TIMEOUT = 600;
    public final int HANDLE_MSG_ERROR_SEND = 700;
    public final int HANDLE_MSG_ERROR_READ = 800;
    public final int HANDLE_MSG_ERROR_XML = 900;
    public final int HANDLE_MSG_ERROR_OTHER = 1000;
    final String CHARSET_EUC_KR = "euc-kr";
    public int SOCKET_BUFFER_SIZE_SEND = 50;
    public int SOCKET_BUFFER_SIZE_RECEIVE = 50;
    public int SOCKET_BUFFER_SIZE_READ = 50;
    public int TIMEOUT_SOCKET_CONNECT = 5000;
    public int TIMEOUT_iMAP_WAIT = 3500;   // iMAP 타임아웃은 실제 3초 이지만, 마진을 두어 3.5초로 한다.
    protected Document DocMsg;

    boolean bLOG = true;   // true, false
    boolean bLOG_DEBUG = true;   // true, false
    boolean bLOG_INFO = true;   // true, false
    Socket SendNWaitSock;
    String strDefaultServerIP = "10.0.1.1";
    int nDefaultServerPort = 10000;
    String strRcvMSG;
    long lSocketConnectionPeriod = 0;
    NodeList Items;
    boolean bReply;
    Handler SendNWaitHandler = new Handler(Looper.getMainLooper()) {
        public void handleMessage(Message msg) {
            LOG_DEBUG("SendNWaitHandler : " + msg.what);
//            LOG_DEBUG("[SendNWaitHandler] strRcvMSG = " + strRcvMSG);
            switch (msg.what) {
                case HANDLE_MSG_DONE_REPLY:
                    if (bReply) deliveryReplyMsg(strRcvMSG);
                    onProcessDone(iMAPResult.RcvDone);
                    break;

                case HANDLE_MSG_DONE_SEND:
                    if (bReply) deliveryReplyMsg(strRcvMSG);
                    onProcessDone(iMAPResult.SendOK);
                    break;

                case HANDLE_MSG_ING:
                    onProcessDoing();
                    break;

                case HANDLE_MSG_UNREACHABLE:
                    onProcessDone(iMAPResult.Unreachable);
                    break;

                case HANDLE_MSG_TIMEOUT:
                    onProcessDone(iMAPResult.Timeout);
                    break;

                case HANDLE_MSG_ERROR_SEND:
                    onProcessDone(iMAPResult.SendFailure);
                    break;

                case HANDLE_MSG_ERROR_READ:
                    onProcessDone(iMAPResult.ReplyHasFault);
                    break;

                case HANDLE_MSG_ERROR_XML:
                    onProcessDone(iMAPResult.Parcel);
                    break;

                case HANDLE_MSG_ERROR_OTHER:
                    onProcessDone(iMAPResult.Other);
                    break;

                default:
                    onProcessDone(iMAPResult.Other);
                    break;
            }
        }
    };
    public SendNWaitMSG() {
        try {
            LOG_DEBUG("[SendNWaitMSG] ConfigValue.eMainServerType [" + ConfigValue.eMainServerType + "]");
        } catch (Exception e) {
            Log.e(TAG, "[Exception] SendNWaitMSG()");
            e.printStackTrace();
        }
    }

    private void LOG(String log) {
        if (bLOG) Log.v(TAG, log);
    }

    private void LOG_DEBUG(String log) {
        if (bLOG_DEBUG) Log.d(TAG, log);
    }

    private void LOG_INFO(String log) {
        if (bLOG_INFO) Log.i(TAG, log);
    }

    public boolean sendMSG(String Msg) {
        // Send a message to predefined target address without reply (IP: 10.0.1.1, Port: 10000)
        try {
//            LOG_DEBUG("[sendMSG] ===== Msg =====\n" + Msg);
            return procSendMGS(strDefaultServerIP, nDefaultServerPort, Msg, false);
        } catch (Exception e) {
            Log.e(TAG, "[Exception] sendMSG(Msg)");
            e.printStackTrace();
            return false;
        }
    }

    public boolean sendMSG(String TargetIP, int TargetPort, String Msg) {
        // Send a message to given target address without reply
        try {
//            LOG_DEBUG("[sendMSG] TargetIP [" + TargetIP + "], TargetPort [" + TargetPort + "]");
//            LOG_DEBUG("[sendMSG] ===== Msg =====\n" + Msg);
            return procSendMGS(TargetIP, TargetPort, Msg, false);
        } catch (Exception e) {
            Log.e(TAG, "[Exception] sendMSG(TargetIP, TargetPort, Msg)");
            e.printStackTrace();
            return false;
        }
    }

    public boolean sendNwaitMSG(String Msg) {
        // Send a message to predefined target address and wait a reply (IP: 10.0.1.1, Port: 10000)
        try {
//            LOG_DEBUG("[sendNwaitMSG] ===== Msg =====\n" + Msg);
            return procSendMGS(strDefaultServerIP, nDefaultServerPort, Msg, true);
        } catch (Exception e) {
            Log.e(TAG, "[Exception] sendNwaitMSG(Msg)");
            e.printStackTrace();
            return false;
        }
    }

    public boolean sendNwaitMSG(int TargetPort, String Msg) {
        // Send a message to predefined target address and wait a reply (IP: 10.0.1.1, Port: 10000)
        try {
//        	LOG_DEBUG("[sendNwaitMSG] TargetPort [" + TargetPort + "]");
//            LOG_DEBUG("[sendNwaitMSG] ===== Msg =====\n" + Msg);
            return procSendMGS(strDefaultServerIP, TargetPort, Msg, true);
        } catch (Exception e) {
            Log.e(TAG, "[Exception] sendNwaitMSG(Msg)");
            e.printStackTrace();
            return false;
        }
    }

    public boolean sendNwaitMSG(String TargetIP, int TargetPort, String Msg) {
        // Send a message to given target address and wait a reply
        try {
//            LOG_DEBUG("[sendNwaitMSG] TargetIP [" + TargetIP + "], TargetPort [" + TargetPort + "]");
//            LOG_DEBUG("[sendNwaitMSG] ===== Msg =====\n" + Msg);
            return procSendMGS(TargetIP, TargetPort, Msg, true);
        } catch (Exception e) {
            Log.e(TAG, "[Exception] sendNwaitMSG(TargetIP, TargetPort, Msg)");
            e.printStackTrace();
            return false;
        }
    }

    public boolean procSendMGS(String TargetIP, int TargetPort, String Msg, boolean bReply) {
        try {
//            LOG_DEBUG("[procSendMGS] TargetIP [" + TargetIP + "], TargetPort [" + TargetPort + "], bReply [" + bReply + "]");
//            LOG_DEBUG("[procSendMGS] ===== Msg =====\n" + Msg);


            SendNWaitMSGThread SendNWaitSock = new SendNWaitMSGThread(TargetIP, TargetPort, Msg, bReply, SendNWaitHandler);
            SendNWaitSock.start();
            return true;
        } catch (Exception e) {
            Log.e(TAG, "[Exception] procSendMGS(TargetIP, TargetPort, Msg, bReply)");
            e.printStackTrace();
            return false;
        }
    }

    protected void onProcessDoing() {
    }

    protected void onProcessDone(iMAPResult eResult) {
    }

    private void deliveryReplyMsg(String msg) {
        try {
            LOG_DEBUG("[deliveryReplyMsg] msg : \r\n" + msg);
            DocumentBuilderFactory DocBuilderFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder DocBuilder = DocBuilderFactory.newDocumentBuilder();
            strRcvMSG = msg;
            InputStream InStm = new ByteArrayInputStream(strRcvMSG.getBytes());
            DocMsg = DocBuilder.parse(InStm);

        } catch (Exception e) {
            Log.e(TAG, "[Exception] deliveryReplyMsg(msg)");
            e.printStackTrace();
        }
    }

    public int setItem(String Name) {
        try {
            if (DocMsg == null) return 0;
            Items = DocMsg.getElementsByTagName(Name);
            return Items.getLength();
        } catch (Exception e) {
            Log.e(TAG, "[Exception] setItem(Name)");
            e.printStackTrace();
            return 0;
        }
    }

    public String getItemValue(String strItem) {
        try {
            NodeList mNodeList = DocMsg.getElementsByTagName(strItem);
            Element mElement = (Element) mNodeList.item(0);
            if (mElement != null) return trimQuotation(mElement.getTextContent());
            else return "null";
        } catch (Exception e) {
            Log.e(TAG, "[Exception] setItem(Name)");
            e.printStackTrace();
            return "error";
        }
    }

    public String getAttributeValue(int nIndex, String strAttName) {
        try {
            if (Items == null) return null;

            int nCnt = Items.getLength();
            if (nCnt < 1 || nIndex - 1 > nCnt) return null;

            Node Item = Items.item(nIndex);
            NamedNodeMap Attrs = Item.getAttributes();

            for (int i = 0; i < Attrs.getLength(); i++) {
                Node Attr = Attrs.item(i);
                if (Attr.getNodeName().equalsIgnoreCase(strAttName)) return Attr.getNodeValue();
            }

            return null;
        } catch (Exception e) {
            Log.e(TAG, "[Exception] getAttributeValue(nIndex, strAttName)");
            e.printStackTrace();
            return null;
        }
    }

    public String getItemValue(int nIndex) {
        try {
            int nCnt = Items.getLength();
            Log.d(TAG, "[getItemValue] nCnt = " + nCnt);
            if (nCnt < 1 || nIndex - 1 > nCnt) return null;
            Node Item = Items.item(nIndex);
            LOG_DEBUG("Node info name:" + Item.getNodeType() + " Value : " + Item.getNodeValue() + "Text Contents :" + Item.getTextContent());

            return trimQuotation(Item.getTextContent());
        } catch (Exception e) {
            Log.e(TAG, "[Exception] getItemValue(nIndex)");
            e.printStackTrace();
            return null;
        }
    }

    protected String trimQuotation(String strText) {
        try {
            strText = strText.replaceAll("\"", "");
            return strText.trim();
        } catch (Exception e) {
            Log.e(TAG, "[Exception] trimQuotation(strText)");
            e.printStackTrace();
            return null;
        }
    }

    public boolean setDefaultServerInfo(String SvrIP, int SvrPort) {
        try {
            strDefaultServerIP = SvrIP;
            nDefaultServerPort = SvrPort;
            return true;
        } catch (Exception e) {
            Log.e(TAG, "[Exception] setDefaultServerInfo(msg)");
            e.printStackTrace();
            return false;
        }
    }

    public String getDefaultServerInfo() {
        try {
            return strDefaultServerIP + ":" + nDefaultServerPort;
        } catch (Exception e) {
            Log.e(TAG, "[Exception] getDefaultServerInfo()");
            e.printStackTrace();
            return null;
        }
    }

    public class SendNWaitMSGThread extends Thread {
        String strTargetIP;
        int nTargetPort;
        String strSendMsg;
        Handler SWHandler;
        int nSockTimeOut = 5000;

        public SendNWaitMSGThread(String Msg, boolean Reply, Handler Handle) {
            try {
                LOG_DEBUG("[SendNWaitMSGThread] Reply [" + Reply + "]");
                LOG_DEBUG("[SendNWaitMSGThread] ===== Msg =====\n" + Msg);
                LOG_DEBUG("[SendNWaitMSGThread] Handler : " + Handle);
                strTargetIP = strDefaultServerIP;
                nTargetPort = nDefaultServerPort;
                strSendMsg = Msg;
                bReply = Reply;
                SWHandler = Handle;
            } catch (Exception e) {
                Log.e(TAG, "[Exception] SendNWaitMSGThread(Msg, Reply, Handle)");
                e.printStackTrace();
            }
        }

        public SendNWaitMSGThread(String TargetIP, int TargetPort, String Msg, boolean Reply, Handler Handle) {
            try {
                LOG_DEBUG("[SendNWaitMSGThread] TargetIP [" + TargetIP + "], TargetPort [" + TargetPort + "], Reply [" + Reply + "]");
                LOG_DEBUG("[SendNWaitMSGThread] ===== Msg =====\n" + Msg);
                LOG_DEBUG("[SendNWaitMSGThread] Handler : " + Handle);
                strTargetIP = TargetIP;
                nTargetPort = TargetPort;
                strSendMsg = Msg;
                bReply = Reply;
                SWHandler = Handle;
            } catch (Exception e) {
                Log.e(TAG, "[Exception] SendNWaitMSGThread(TargetIP, TargetPort, Msg, Reply, Handle)");
                e.printStackTrace();
            }
        }

        public void setReplyTimeOut(int TimeOut) {
            nSockTimeOut = TimeOut;
        }

        private void sendHandlerMsg(int msg) {
            try {
//                LOG_DEBUG("[sendHandlerMsg] msg : " + msg);
                Message HandleMsg = SWHandler.obtainMessage();
                HandleMsg.what = msg;
                SWHandler.sendMessage(HandleMsg);
            } catch (Exception e) {
                Log.e(TAG, "[Exception] sendHandlerMsg(msg)");
                e.printStackTrace();
            }
        }

        public void run() {
            try {
                if (ConfigValue.eMainServerType == SetValue.Server2dot0_Integrated && nTargetPort == 11000) {

                    //Log.d(TAG, "SendNWaitSock KSJ 2222");
                    // 암호화
                    try {
                        SSLClient mSSLClientSocket = new SSLClient(strTargetIP, nTargetPort);
                        SendNWaitSock = mSSLClientSocket.getSocket();
                    } catch (Exception e) {
                        Log.e(TAG, "[Exception] (SendNWaitMSGThread.run()) SSLClient init error!!");
                        e.printStackTrace();
                        if (SendNWaitSock != null) SendNWaitSock.close();
                        sendHandlerMsg(HANDLE_MSG_UNREACHABLE);
                        return;
                    }

                    SendNWaitSock.setSoLinger(true, 3000);
                    SendNWaitSock.setSendBufferSize(SOCKET_BUFFER_SIZE_RECEIVE * 1024);
                    SendNWaitSock.setReceiveBufferSize(SOCKET_BUFFER_SIZE_RECEIVE * 1024);
                    SendNWaitSock.setSoTimeout(nSockTimeOut);
                }
                else if (ConfigValue.eMainServerType == SetValue.ServerMixed_Integrated && nTargetPort == 11000) {
                    //Log.d(TAG, "SendNWaitSock KSJ 2222");
                    // 암호화
                    try {
                        SSLClient mSSLClientSocket = new SSLClient(strTargetIP, nTargetPort);
                        SendNWaitSock = mSSLClientSocket.getSocket();
                    } catch (Exception e) {
                        Log.e(TAG, "[Exception] (SendNWaitMSGThread.run()) SSLClient init error!!");
                        e.printStackTrace();
                        if (SendNWaitSock != null) SendNWaitSock.close();
                        sendHandlerMsg(HANDLE_MSG_UNREACHABLE);
                        return;
                    }

                    SendNWaitSock.setSoLinger(true, 3000);
                    SendNWaitSock.setSendBufferSize(SOCKET_BUFFER_SIZE_RECEIVE * 1024);
                    SendNWaitSock.setReceiveBufferSize(SOCKET_BUFFER_SIZE_RECEIVE * 1024);
                    SendNWaitSock.setSoTimeout(nSockTimeOut);
                }
                else {

                    //Log.d(TAG, "SendNWaitSock KSJ 1111");
                    // 비암호화
                    SendNWaitSock = new Socket();

                    SendNWaitSock.setSoLinger(true, 3000);
                    SendNWaitSock.setSendBufferSize(SOCKET_BUFFER_SIZE_RECEIVE * 1024);
                    SendNWaitSock.setReceiveBufferSize(SOCKET_BUFFER_SIZE_RECEIVE * 1024);
                    SendNWaitSock.setSoTimeout(nSockTimeOut);

                    InetSocketAddress SWSockAddr = new InetSocketAddress(strTargetIP, nTargetPort);

                    try {
                        SendNWaitSock.connect(SWSockAddr, TIMEOUT_SOCKET_CONNECT);
                    } catch (Exception e) {
                        Log.e(TAG, "[Exception] (SendNWaitMSGThread.run()) ===== 1 =====");
                        e.printStackTrace();
                        if (SendNWaitSock != null) SendNWaitSock.close();
                        sendHandlerMsg(HANDLE_MSG_UNREACHABLE);
                        return;
                    }
                }

                InputStream InStm = SendNWaitSock.getInputStream();
                BufferedReader MsgReader = new BufferedReader(new InputStreamReader(InStm, CHARSET_EUC_KR));
                DataOutputStream OutStm = new DataOutputStream(SendNWaitSock.getOutputStream());

                byte[] OutBuffer = strSendMsg.getBytes(CHARSET_EUC_KR);
                try {
                    OutStm.write(OutBuffer);
                    OutStm.flush();
                } catch (Exception e) {
                    Log.e(TAG, "[Exception] (SendNWaitMSGThread.run()) ===== 2 =====");
                    e.printStackTrace();
                    if (SendNWaitSock != null) SendNWaitSock.close();
                    sendHandlerMsg(HANDLE_MSG_ERROR_SEND);
                    return;
                }

                if (!bReply) {
                    // If the reply is not required, close the socket.
                    if (SendNWaitSock != null) SendNWaitSock.close();
                    sendHandlerMsg(HANDLE_MSG_DONE_SEND);
                    return;
                }

                strRcvMSG = "";
                String InBuffer;
                lSocketConnectionPeriod = System.currentTimeMillis() + TIMEOUT_iMAP_WAIT;   // 타임아웃시간을 설정한다.

                if (ConfigValue.eMainServerType == SetValue.Server2dot0_Integrated) {
                    // 암호화
                    // * ready() 가 정상동작 되지않아 아래 와 같이 수신부를 수정함.

                    while (System.currentTimeMillis() < lSocketConnectionPeriod) {
                        try {
                            try {
                                char[] ReadData = new char[SOCKET_BUFFER_SIZE_READ * 1024];
                                InBuffer = "";
                                long charCnt = MsgReader.read(ReadData);
                                if (charCnt > 0) {
                                    InBuffer += new String(ReadData, 0, (int) charCnt);
                                    if (InBuffer != null) strRcvMSG += InBuffer;
                                    if (0 < InBuffer.indexOf("</imap>")) {
                                        sendHandlerMsg(HANDLE_MSG_DONE_REPLY);
                                        if (SendNWaitSock != null) SendNWaitSock.close();
                                        return;
                                    }
                                } else {
                                    Thread.sleep(100);
                                }
                            } catch (Exception e) {
                                Log.e(TAG, "[Exception] (SendNWaitMSGThread.run()) ===== 3 ===== (Encrypted)");
                                if (SendNWaitSock != null) SendNWaitSock.close();
                                e.printStackTrace();
                                sendHandlerMsg(HANDLE_MSG_ERROR_XML);
                                return;
                            }
                        } catch (Exception e) {
                            Log.e(TAG, "[Exception] (SendNWaitMSGThread.run()) ===== 4 ===== (Encrypted)");
                            if (SendNWaitSock != null) SendNWaitSock.close();
                            e.printStackTrace();
                            sendHandlerMsg(HANDLE_MSG_ERROR_READ);
                            return;
                        }
                    }
                } else {
                    // 비암호화
                    while (System.currentTimeMillis() < lSocketConnectionPeriod) {
                        try {
                            if (MsgReader.ready()) {
                                try {
                                    char[] ReadData = new char[SOCKET_BUFFER_SIZE_READ * 1024];
                                    InBuffer = "";
                                    while (MsgReader.ready()) {
                                        long charCnt = MsgReader.read(ReadData);
                                        InBuffer += new String(ReadData, 0, (int) charCnt);
                                    }
                                    if (InBuffer != null) strRcvMSG += InBuffer;
                                    if (0 < InBuffer.indexOf("</imap>")) {
                                        sendHandlerMsg(HANDLE_MSG_DONE_REPLY);
                                        if (SendNWaitSock != null) SendNWaitSock.close();
                                        return;
                                    }
                                } catch (Exception e) {
                                    Log.e(TAG, "[Exception] (SendNWaitMSGThread.run()) ===== 3 ===== (NonEncrypted)");
                                    if (SendNWaitSock != null) SendNWaitSock.close();
                                    e.printStackTrace();
                                    sendHandlerMsg(HANDLE_MSG_ERROR_XML);
                                    return;
                                }
                            } else {
                                Thread.sleep(100);
                            }
                        } catch (Exception e) {
                            Log.e(TAG, "[Exception] (SendNWaitMSGThread.run()) ===== 4 ===== (NonEncrypted)");
                            if (SendNWaitSock != null) SendNWaitSock.close();
                            e.printStackTrace();
                            sendHandlerMsg(HANDLE_MSG_ERROR_READ);
                            return;
                        }
                    }
                }

                if (SendNWaitSock != null) SendNWaitSock.close();
                sendHandlerMsg(HANDLE_MSG_TIMEOUT);
                return;
            } catch (Exception e) {
                try {
                    Log.e(TAG, "[Exception] (SendNWaitMSGThread.run()) ===== 5 =====");
                    e.printStackTrace();
                    if (SendNWaitSock != null) SendNWaitSock.close();
                    sendHandlerMsg(HANDLE_MSG_ERROR_OTHER);
                } catch (IOException e1) {
                    Log.e(TAG, "[Exception] (SendNWaitMSGThread.run()) ===== 6 =====");
                    e1.printStackTrace();
                    sendHandlerMsg(HANDLE_MSG_ERROR_OTHER);
                }
            }
        }
    }
}
