using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml;
using JWH;

namespace JWH.TIB
{

    public class MessageParser
    {

        public string Subject { get; set; } = string.Empty;

        public string SendSubject { get; set; } = string.Empty;

        public string XmlData { get; set; } = string.Empty;

        public bool SendRequest { get; set; } = false;

        public string PathMessageReply { get; set; } = string.Empty;

        public List<MessageValue> MessageValues { get; set; } = new List<MessageValue>();


        public MessageParser()
        {
        }

        public MessageParser(string xmlData, bool sendRequest = false)
        {
            this.XmlData = xmlData;
            this.SendRequest = sendRequest;
        }

        public MessageParser(string subject, string xmlData, bool sendRequest = false)
        {
            this.Subject = subject;
            this.XmlData = xmlData;
            this.SendRequest = sendRequest;
        }


        #region [ CreateMessage ] ----------------------------------------------

        /// <summary>
        /// 문자열로 XmlMessage를 생성한 후, 각 항목에 값을 설정한다
        /// </summary>
        /// <param name="xmlData"></param>
        /// <returns></returns>
        public XmlMessage CreateSendMessage(string xmlData, bool isValueSetting = true)
        {
            try
            {
                if (string.IsNullOrEmpty(xmlData)) return null;

                XmlMessage xmlMessage = new XmlMessage(xmlData);
                if (isValueSetting) 
                {
                    this.SetMessageValue(xmlMessage.XmlData.Header);
                    this.SetMessageValue(xmlMessage.XmlData.Body);
                }

                return xmlMessage;
            }
            catch (Exception ex)
            {
                XLogger.Instance.Fatal(ex);
                return null;
            }
        }

        /// <summary>
        /// 응답메시지를 반환한다 (MessageName_*.xml)
        /// </summary>
        /// <param name="xmlRequest">Request Message</param>
        /// <returns>Reply Message</returns>
        public XmlMessage[] CreateSendReplyMessages(XmlMessage xmlRequest)
        {
            try
            {
                List<XmlMessage> lstReply = new List<XmlMessage>();
                XmlMessage xmlReply = null;
                string strReply = string.Empty;

                // ReplyFile Processing...
                DirectoryInfo directoryInfo = new DirectoryInfo(this.PathMessageReply);
                foreach (FileInfo fileInfo in directoryInfo.GetFiles($"{xmlRequest.XmlData.MessageName}_*.xml"))
                {
                    StreamReader reader = new StreamReader(fileInfo.FullName, Encoding.UTF8, true);
                    strReply = reader.ReadToEnd();
                    xmlReply = new XmlMessage(strReply);
                    xmlReply.SetRequstMessage(xmlRequest);
                    this.SetMessageValue(xmlReply.XmlData.Body, xmlRequest);

                    lstReply.Add(xmlReply);
                }
                if (lstReply.Count > 0) return lstReply.ToArray();

                // Dynamic Processing
                if (string.Compare(xmlRequest.XmlData.MessageName, "AreYouThereRequest", true) == 0)
                    xmlReply = this.CreateReply_AreYouThereRequest(xmlRequest);
                else
                    xmlReply = this.CreateReply_Common(xmlRequest);
                lstReply.Add(xmlReply);

                return lstReply.ToArray();
            }
            catch (Exception ex)
            {
                XLogger.Instance.Fatal(ex);
                return null;
            }
        }

        #endregion

        #region [ CreateReply ] ------------------------------------------------

        /// <summary>
        /// CreateReply AreYouThereRequest
        /// </summary>
        /// <param name="xmlRequest"></param>
        /// <returns></returns>
        private XmlMessage CreateReply_AreYouThereRequest(XmlMessage xmlRequest)
        {
            if (xmlRequest == null) return null;

            XmlMessage xmlReply = new XmlMessage(xmlRequest.XmlData.GetText());
            xmlReply.XmlData.MessageName += "Reply";
            xmlReply.XmlData.ReplySubjectName = xmlRequest.XmlData.SendSubjectName;
            xmlReply.XmlData.SendSubjectName = xmlRequest.XmlData.ReplySubjectName;

            xmlReply.RequestMessage = xmlRequest;
            if (xmlRequest.SendRequestFlag) xmlReply.IsReply = true;

            return xmlReply;
        }

        /// <summary>
        /// CreateReply Common
        /// </summary>
        /// <param name="xmlRequest"></param>
        /// <returns></returns>
        private XmlMessage CreateReply_Common(XmlMessage xmlRequest)
        {
            if (xmlRequest == null) return null;

            XmlMessage xmlReply = new XmlMessage(xmlRequest.XmlData.GetText());
            xmlReply.XmlData.SendRequestFlag = false;
            xmlReply.XmlData.MessageName += "Reply";
            xmlReply.XmlData.ReplySubjectName = xmlRequest.XmlData.SendSubjectName;
            xmlReply.XmlData.SendSubjectName = xmlRequest.XmlData.ReplySubjectName;

            XmlNode nodeReturn = xmlReply.XmlData.Message.CreateChildNode("return");
            nodeReturn.CreateChildNode("returncode", "0");
            nodeReturn.CreateChildNode("returnmessage", "TibSimulator");

            xmlReply.RequestMessage = xmlRequest;
            if (xmlRequest.SendRequestFlag) xmlReply.IsReply = true;

            return xmlReply;
        }

        #endregion

        #region [ Method ] ====================================================

        /// <summary>
        /// 메시지의 각 항목에 값을 할당한다
        /// <para>Step-1. Value of srcMessage</para>
        /// <para>Step-2. Value of MessageValue</para>
        /// </summary>
        /// <param name="node"></param>
        /// <param name="srcMessage">RequestMessage</param>
        private void SetMessageValue(XmlNode node, XmlMessage srcMessage = null)
        {
            // MessageValue(UI)
            if (node.GetText() == string.Empty)
            {
                string name = node.Name;
                XmlAttribute attribute = node.GetAttribute("Name");
                if (attribute != null)
                {
                    name = attribute.Value;
                    node.Attributes.Remove(attribute);
                }

                string srcValue = srcMessage?.XmlData.GetValue(name);
                MessageValue msgValue = this.GetMessageValue(name);
                if (string.IsNullOrEmpty(srcValue))
                {
                    if (msgValue != null)
                    {
                        object value = string.Empty;
                        if (msgValue.Format.ToUpper() == "FUNCTION") value = this.CallFunction(node, msgValue, srcMessage);
                        else if (msgValue.Format.ToUpper() == "MACRO") value = this.CallMacro(node, msgValue, srcMessage);
                        else value = msgValue.Value;
                    
                        if (value != null)
                        {
                            if (value.GetType() == typeof(string)) node.SetText((string)value);
                            else if (value.GetType() == typeof(XmlNode[]))
                            {
                                foreach (XmlNode child in value as XmlNode[])
                                    node.AppendChild(child);
                            }
                        }
                    }
                }
                else
                {
                    node.SetText(srcValue);
                }
            }

            foreach (XmlNode child in node.ChildNodes)
            {
                if (child.GetType() == typeof(XmlText)) continue;
                this.SetMessageValue(child, srcMessage);
            }
        }

        /// <summary>
        /// Call Function
        /// </summary>
        /// <param name="node"></param>
        /// <param name="srcValue"></param>
        /// <param name="srcMessage"></param>
        /// <returns></returns>
        private object CallFunction(XmlNode node, MessageValue srcValue, XmlMessage srcMessage = null)
        {
            try
            {
                string source = srcValue.Value;
                string name = string.Empty;
                string parameters = string.Empty;
                List<object> lstParameter = new List<object>();

                int length = source.Length - 1;
                int indexStart = source.IndexOf('(');
                int indexEnd = source.IndexOf(')');
                if (indexStart >= 0) length = indexStart;

                name = source.Substring(0, length);
                parameters = source.Substring(indexStart + 1, indexEnd - indexStart - 1);

                MethodInfo methodInfo = this.GetType().GetMethod(name);
                if (methodInfo == null) return string.Empty;
                lstParameter.AddRange(this.GetMethodParameters(methodInfo, node, parameters, srcMessage));

                object result = methodInfo.Invoke(this, lstParameter.ToArray());
                if (methodInfo.Name == "GetIncrement" && srcValue != null) srcValue.Value = $"{name}({(int)result + 1})";

                return result;
            }
            catch (Exception ex)
            {
                XLogger.Instance.Fatal(ex);
                return string.Empty;
            }
        }

        /// <summary>
        /// Call Macro
        /// </summary>
        /// <param name="node"></param>
        /// <param name="srcValue"></param>
        /// <param name="srcMessage"></param>
        /// <returns></returns>
        private object CallMacro(XmlNode node, MessageValue srcValue, XmlMessage srcMessage = null)
        {
            string source = srcValue.Value;
            try
            {
                string macro = source;
                string name = string.Empty;
                string value = string.Empty;
                int indexStart = -1;
                int indexEnd = -1;

                indexStart = macro.IndexOf('%', 0);
                while (indexStart >= 0)
                {
                    indexStart = macro.IndexOf('%', indexStart);
                    indexEnd = macro.IndexOf('%', indexStart + 1);
                    if (indexStart < 0) break;
                    name = macro.Substring(indexStart + 1, indexEnd - indexStart - 1);

                    value = string.Empty;
                    if (srcMessage != null) value = srcMessage.XmlData.GetValue(name);
                    if (string.IsNullOrEmpty(value))
                    {
                        MessageValue msgValue = this.GetMessageValue(name);
                        if (msgValue != null) value = msgValue.Value;
                    }

                    macro = $"{macro.Substring(0, indexStart)}{value}{macro.Substring(indexEnd + 1)}";
                    indexStart = 0;
                }

                return macro;
            }
            catch (Exception ex)
            {
                XLogger.Instance.Fatal(ex);
                return source;
            }
        }

        private object[] GetMethodParameters(MethodInfo method, XmlNode node, string arguments, XmlMessage srcMessage = null)
        {
            string[] parameters = arguments.Split(',');
            List<object> lstParameter = new List<object>();

            string name = string.Empty;
            object value = string.Empty;
            for (int i = 0; i < method.GetParameters().Length; i++)
            {
                if (parameters[i].StartsWith("%") && parameters[i].EndsWith("%")) name = parameters[i].Replace("%", "");
                if (name.ToUpper() == "XMLNODE") { lstParameter.Add(node); continue; }
                if (name.ToUpper() == "XMLMESSAGE") { lstParameter.Add(srcMessage); continue; }
                if (srcMessage != null) value = srcMessage.XmlData.GetValue(name);

                if (value == null || value.GetType() == typeof(string) ? string.IsNullOrEmpty((string)value) : false)
                {
                    MessageValue msgValue = this.GetMessageValue(name);
                    if (msgValue != null)
                    {
                        if (msgValue.Format.ToUpper() == "FUNCTION") value = this.CallFunction(node, msgValue, srcMessage);
                        else if (msgValue.Format.ToUpper() == "MACRO") value = this.CallMacro(node, msgValue, srcMessage);
                        else value = msgValue.Value;
                    }
                }
                if (value == null) value = parameters[i];

                var obj = Convert.ChangeType(value, method.GetParameters()[i].ParameterType);

                lstParameter.Add(obj);
            }

            return lstParameter.ToArray();
        }

        private MessageValue GetMessageValue(string name)
        {
            return this.MessageValues.Where(item => item.Name.ToUpper() == name.ToUpper()).FirstOrDefault();
        }

        #endregion

        #region [ Function Method ] ============================================

        public string GetDateTime()
        {
            return DateTime.Now.ToString("yyyyMMddHHmmss");
        }

        public string GetTransactionID()
        {
            return Guid.NewGuid().ToString();
        }

        public string GetIPAddress()
        {
            return "127.0.0.1";
        }

        public object[] GetPanelList(XmlNode node, string lotID, int qty)
        {
            List<XmlNode> lstPanel = new List<XmlNode>();
            for (int i = qty; i > 0; i--)
            {
                XmlNode panelInfo = node.OwnerDocument.CreateNode("element", "PANELINFO", "");
                XmlNode panelID = node.OwnerDocument.CreateNode("element", "PANELID", "");
                panelID.SetText($"{lotID}_{i.ToString("00")}");
                XmlNode panelSeq = node.OwnerDocument.CreateNode("element", "PANELSEQ", "");
                panelSeq.SetText($"{i}");
                panelInfo.AppendChild(panelID);
                panelInfo.AppendChild(panelSeq);
                lstPanel.Add(panelInfo);
            }

            return lstPanel.ToArray();
        }

        public object[] GetChilds(XmlNode node, XmlMessage srcMessage)
        {
            if (srcMessage == null) return null;

            List<XmlNode> lstChilid = new List<XmlNode>();
            XmlNode xmlNode = srcMessage.XmlData.GetNode(node.Name);
            foreach (XmlNode child in xmlNode.ChildNodes)
            {
                XmlNode importNode = node.OwnerDocument.ImportNode(child, true);
                node.AppendChild(importNode);
            }

            return null;
        }

        public int GetIncrement(Int32 value)
        {
            return value;
        }

        public string ChangeLotID()
        {
            MessageValue msgValueText = this.GetMessageValue("LotText");
            MessageValue msgValueSeq = this.GetMessageValue("LotSeq");
            MessageValue msgValueID = this.GetMessageValue("LotID");
            if (msgValueText == null || msgValueSeq == null || msgValueID == null) return String.Empty;

            int seq = 0;
            int.TryParse(msgValueSeq.Value, out seq);
            string value = $"{msgValueText.Value}{seq.ToString("00")}";

            msgValueID.Value = value;
            msgValueSeq.Value = (seq + 1).ToString();

            return value;
        }

        public string ChangeCarrierID()
        {
            MessageValue msgValueText = this.GetMessageValue("CarrierText");
            MessageValue msgValueSeq = this.GetMessageValue("CarrierSeq");
            MessageValue msgValueID = this.GetMessageValue("CarrierID");
            if (msgValueText == null || msgValueSeq == null || msgValueID == null) return String.Empty;

            int seq = 0;
            int.TryParse(msgValueSeq.Value, out seq);
            string value = $"{msgValueText.Value}{seq.ToString("00")}";

            msgValueID.Value = value;
            msgValueSeq.Value = (seq + 1).ToString();

            return value;
        }

        #endregion

    }

}
