using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Project.Device
{
    public class Socket
    {
        public enum eType
        {
            REQUEST,
            REPLY,
        }
        public enum eSType
        {
            STATUS,
            GEN,
            SPL
        }

        public struct Message
        {
            public Boolean isError;
            public string ErrorMessage;
            public string Asset;
            public eType Type;
            public eSType SType;
            public string Body;
            public string timeStamp;
        }

        private Winsock_Orcas.Winsock wsListen;
        private Winsock_Orcas.Winsock wsData;
        private char sepMsg = (char)0x07;
        private char sepBody = (char)0x09;

        public int ListenPort { get; set; }
        public string errorMessage { get; set; }
        public event EventHandler<SocketMessageEventArgs> RecvMessage;
        public event EventHandler<SocketMessageEventArgs> SendMessage;
        public event EventHandler<MesasgeEventArgs> GetMessage;

        private void RaiseMessage(string msg, Boolean isErr = false)
        {
            if (GetMessage != null) GetMessage(this, new MesasgeEventArgs(msg, isErr));
        }
        private void RaiseSendMessage(Message msg, string raw)
        {
            if (SendMessage != null)
            {
                try
                {
                    SendMessage(this, new SocketMessageEventArgs(msg, raw));
                }
                catch (Exception ex)
                {
                    this.errorMessage = ex.Message;
                }
            }
        }
        private void RaiseRecvMessage(Message msg, string raw)
        {
            if (RecvMessage != null)
            {
                try
                {
                    RecvMessage(this, new SocketMessageEventArgs(msg, raw));
                }
                catch (Exception ex)
                {
                    this.errorMessage = ex.Message;
                }
            }
        }
        public Socket()
        {
            wsListen = new Winsock_Orcas.Winsock();
            wsData = new Winsock_Orcas.Winsock();
            wsListen.LegacySupport = true;
            wsData.LegacySupport = true;
            wsData.Connected += wsData_Connected;
            wsData.Disconnected += wsData_Disconnected;
            wsData.DataArrival += wsData_DataArrival;
            wsData.ErrorReceived += wsData_ErrorReceived;
            wsListen.ConnectionRequest += wsListen_ConnectionRequest;
            wsListen.ErrorReceived += wsListen_ErrorReceived;
        }

        void wsListen_ErrorReceived(object sender, Winsock_Orcas.WinsockErrorReceivedEventArgs e)
        {
            RaiseMessage("LTERR:" + e.Details, true);
        }

        void wsData_ErrorReceived(object sender, Winsock_Orcas.WinsockErrorReceivedEventArgs e)
        {
            RaiseMessage("RXERR:"+e.Details, true);
        }

        void wsData_Disconnected(object sender, EventArgs e)
        {
            RaiseMessage("Disconnected");
        }

        void wsData_Connected(object sender, Winsock_Orcas.WinsockConnectedEventArgs e)
        {
            RaiseMessage("Conncted");
        }

        void wsListen_ConnectionRequest(object sender, Winsock_Orcas.WinsockConnectionRequestEventArgs e)
        {
            if (wsData.State != Winsock_Orcas.WinsockStates.Closed)
            {
                wsData.Close();
                System.Threading.Thread.Sleep(500);
            }
            RaiseMessage("Connection Request " + e.ClientIP);
            try
            {
                wsData.Accept(e.Client);
            }catch (Exception ex)
            {
                RaiseMessage("Connection Request\n"+ex.Message, true);
            }
            
            
        }

        ~Socket()
        {
            if (wsData != null)
            {
                wsData.DataArrival -= wsData_DataArrival;
                wsData.Connected += wsData_Connected;
                wsData.Disconnected += wsData_Disconnected;
            }
            if (wsListen != null)
            {
                wsListen.ConnectionRequest += wsListen_ConnectionRequest;
            }



            if (wsListen != null && wsListen.State != Winsock_Orcas.WinsockStates.Closed)
                wsListen.Close();
            if (wsData != null && wsData.State != Winsock_Orcas.WinsockStates.Closed)
                wsData.Close();
            wsData.Dispose();
            wsListen.Dispose();
        }

        public Boolean Listen(int port)
        {
            try
            {
                ListenPort = port;
                wsListen.Listen(this.ListenPort);
                this.errorMessage = string.Empty;
                return true;
            }
            catch (Exception ex)
            {
                this.errorMessage = ex.Message;
                return false;
            }
        }
        public Boolean isListen
        {
            get
            {
                return wsListen.State == Winsock_Orcas.WinsockStates.Listening;
            }
        }
        public Boolean isConn
        {
            get
            {
                return wsData.State == Winsock_Orcas.WinsockStates.Connected;
            }
        }

        public Message LastSendMessage = new Message();
        private Message makeMessage(string Asset, eType type, eSType subtype, string body)
        {
            Message retval = new Message();
            retval.Asset = Asset;
            retval.Type = type;
            retval.SType = subtype;
            retval.Body = body;
            retval.isError = true;
            retval.ErrorMessage = string.Empty;
            retval.timeStamp = DateTime.Now.ToString("yyyyMMddHHmmss");
            return retval;
        }

        /// <summary>
        /// Body문자열을 구분자를 기준으로 한 배열로 반환 함
        /// </summary>
        /// <param name="bodyMessage"></param>
        /// <returns></returns>
        public string[] getBodyArray(string bodyMessage)
        {
            return bodyMessage.Split(sepBody);
        }

        /// <summary>
        /// 패킷의 내용을 메세지로 분리한다.
        /// </summary>
        /// <param name="PacketMessage"></param>
        /// <returns></returns>
        private Message ParseMessage(string PacketMessage)
        {
            Message retval = new Message();
            retval.isError = false;
            retval.ErrorMessage = string.Empty;

            var buffer = PacketMessage.Split(sepMsg);
            if (buffer.Length != 5)
            {
                retval.isError = true;
                retval.ErrorMessage = "Packet Size Error : Expect 5 Array";
                return retval;
            }
            else
            {
                retval.Asset = buffer[0];
                if (!getType(buffer[1], out retval.Type))
                {
                    retval.isError = true;
                    retval.ErrorMessage = "지정된 Type 이 아닙니다(" + buffer[1] + ")";
                    return retval;
                }
                if (!getSType(buffer[2], out retval.SType))
                {
                    retval.isError = true;
                    retval.ErrorMessage = "지정된 SubType 이 아닙니다(" + buffer[2] + ")";
                    return retval;
                }
                retval.timeStamp = buffer[3];
                //DateTime timeStamp;
                //if (!DateTime.TryParse(retval.timeStamp, out timeStamp))
                //{
                //    retval.isError = true;
                //    retval.ErrorMessage = "timeStamp 해석 오류(" + retval.timeStamp + ")";
                //    return retval;
                //}
                retval.Body = buffer[4];
            }
            return retval;
        }

        private Boolean getType(string strType, out eType type)
        {
            type = eType.REPLY;
            var list = Enum.GetNames(typeof(eType));
            int value = -1;
            for (int i = 0; i < list.Length; i++)
            {
                if (list[i].ToUpper() == strType.ToUpper())
                {
                    value = i;
                    break;
                }
            }
            if (value == -1) return false;
            type = (eType)value;
            return true;
        }
        private Boolean getSType(string strSType, out eSType stype)
        {
            stype = eSType.GEN;
            var list = Enum.GetNames(typeof(eSType));
            int value = -1;
            for (int i = 0; i < list.Length; i++)
            {
                if (list[i].ToUpper() == strSType.ToUpper())
                {
                    value = i;
                    break;
                }
            }
            if (value == -1) return false;
            stype = (eSType)value;
            return true;
        }

        public string makePacket(Message msg)
        {
            //프레임구조 asset chr(7) + type + chr(7) + stype + char(7) + timeStamp + char(7) + body"
            //timestamp = yyyymmddhhmmss
            //sepChar = (char)0x07;

            System.Text.StringBuilder buffer = new StringBuilder();
            buffer.Append(msg.Asset);
            buffer.Append(sepMsg);
            buffer.Append(msg.Type.ToString());
            buffer.Append(sepMsg);
            buffer.Append(msg.SType.ToString());
            buffer.Append(sepMsg);
            buffer.Append(msg.timeStamp);
            buffer.Append(sepMsg);
            buffer.Append(msg.Body);
            return buffer.ToString();
        }

        public string makeLotSplitPacket( string fcst, string tcst, string slot, string wafer)
        {
            System.Text.StringBuilder buffer = new StringBuilder();
            buffer.Append(fcst);
            buffer.Append(sepBody);
            buffer.Append(tcst);
            buffer.Append(sepBody);
            buffer.Append(slot);
            buffer.Append(sepBody);
            buffer.Append(wafer);
            return buffer.ToString();
        }
        public string makeLotGenPacket(string Lot, string cst, string slot, string wafer)
        {
            System.Text.StringBuilder buffer = new StringBuilder();
            buffer.Append(Lot);
            buffer.Append(sepBody);
            buffer.Append(cst);
            buffer.Append(sepBody);
            buffer.Append(slot);
            buffer.Append(sepBody);
            buffer.Append(wafer);
            return buffer.ToString();
        }
        public enum eStatus
        {
            RUN  =0,
            IDLE ,
            ALARM,
        }
        public string makeReplyStatus(eStatus Status)
        {
            System.Text.StringBuilder buffer = new StringBuilder();
            buffer.Append(((int)Status).ToString());
            return buffer.ToString();
        }
        public Boolean Send(string Asset, eType type, eSType subtype, string body)
        {
            var msg = makeMessage(Asset, type, subtype, body);
            var isStatus = subtype == eSType.STATUS;
            return Send(msg, isStatus);
        }

        public Boolean isReplyRecv = false;
        public Boolean Send(Message msg,Boolean isStatus)
        {

            var packet = makePacket(msg);
            if (wsData.State != Winsock_Orcas.WinsockStates.Connected)
            {
                errorMessage = "Not Connected";
                return false;
            }
            try
            {
                if(isStatus==false) isReplyRecv = false;    //190129
                wsData.Send(packet + "\r\n");
                errorMessage = string.Empty;
                LastSendMessage = msg;
                RaiseSendMessage(msg, packet);
                return true;
            }
            catch (Exception ex)
            {
                errorMessage = ex.Message;
                return false;
            }
        }

        public Message LastReplyMessage = new Message();
        public Message LastRecvMessage = new Message();
        void wsData_DataArrival(object sender, Winsock_Orcas.WinsockDataArrivalEventArgs e)
        {
            var sock = sender as Winsock_Orcas.Winsock;
            var data = sock.Get<String>();
           if(PUB.setting.LOg_SocketRecv)
           {
               if (data.IndexOf("REQUEST") != -1 && data.IndexOf("STATUS") != -1)
               {
                   //핑 명령
               }
               else PUB.log.AddE("Socket Recv : " + data); //190129
           }
               
            LastRecvMessage = ParseMessage(data);
            if (LastRecvMessage.isError) PUB.log.AddE("Socket Message error" + LastRecvMessage.ErrorMessage);
            else
            {
                //190129 - reply만 별도 처리함
                if (LastRecvMessage.Type == eType.REPLY)
                {
                    LastReplyMessage = ParseMessage(data);
                    isReplyRecv = true;
                }
            }
            RaiseRecvMessage(LastRecvMessage, data);
        }


        public class SocketMessageEventArgs : EventArgs
        {
            public string rawData { get; set; }
            public Device.Socket.Message Message { get; set; }
            public SocketMessageEventArgs(Device.Socket.Message msg, string raw)
            {
                this.Message = msg;
                this.rawData = raw;
            }
        }
        public class MesasgeEventArgs : EventArgs
        {
            public string Message { get; set; }
            public Boolean isError { get; set; }
            public MesasgeEventArgs(string msg, Boolean iserr)
            {
                this.Message = msg;
                this.isError = iserr;
            }
        }
    }

}
