using Edge.Core.Parser.BinaryParser.Attributes;
using Edge.Core.Parser.BinaryParser.Util;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Edge.Core.Parser.BinaryParser.MessageEntity
{
    public abstract class MessageTemplateBase : MessageBase
    {
        protected MessageTemplateBase() { }
        /// <summary>
        /// Validate the element object against on some high level validation rules.
        /// </summary>
        /// <returns>If an exception returned by overriden element class, the validation will be indicated as fail, the exception will be re-throw out</returns>
        public virtual Exception Validate() { return null; }

        /// <summary>
        /// Simply Xml-serialize the object to get the log string.
        /// </summary>
        /// <returns>xml format string</returns>
        public override string ToLogString()
        {
            var unformattedLogs = new List<string>();
            try
            {
                this.ToLogStringHelper(this, unformattedLogs, "   ");
            }
            catch (Exception ex)
            {
                unformattedLogs.Add("Exception occured when generating the LogString for current message, exception detail: " + ex);
            }

            if (unformattedLogs.Any())
                return unformattedLogs.Aggregate((p, n) => p + System.Environment.NewLine + n) + System.Environment.NewLine + "------------";
            return "";
        }

        private List<string> ToLogStringHelper(object elementInstance, List<string> propertyNameValueString, string prefix)
        {
            int originalCount = propertyNameValueString.Count;
            // Only processing WayneAttribute marked properties, and order by its Index in DESC
            var targetPropertyList = elementInstance.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
                                        .Where(p => p.GetCustomAttributes(typeof(AttributeBase), true).Length > 0)
                                        .OrderBy(info => ((AttributeBase)(info.GetCustomAttributes(typeof(AttributeBase), true)[0])).Index);
            var plainPropertyLogStrAccumulator = string.Empty;
            foreach (var propertyInfo in targetPropertyList)
            {
                // normal plain Property
                if (propertyInfo.GetCustomAttributes(typeof(EnumerableFormatAttribute), true).Length == 0)
                {
                    var value = propertyInfo.GetValue(elementInstance, null);
                    string hexStr = "";
                    if (value is int)
                    {
                        hexStr = "0x" + ((int)value).ToHexLogString();// ToString("X").PadLeft(2, '0');
                    }
                    else if (value is byte)
                    {
                        hexStr = "0x"
                            + BitConverter.ToInt32(new byte[] { (byte)value, 0, 0, 0 }, 0).ToString("X").PadLeft(2, '0');
                    }

                    plainPropertyLogStrAccumulator += propertyInfo.Name + ": " +
                                                  value.ToString() + "(" + hexStr + "), ";
                }
                // the IList Property
                else
                {
                    var listFormat =
                        (EnumerableFormatAttribute)propertyInfo.GetCustomAttributes(typeof(EnumerableFormatAttribute), true)[0];
                    var list = (IList)propertyInfo.GetValue(elementInstance, null);

                    if (list != null)
                    {
                        var genericArg = list.GetType().GetGenericArguments()[0];
                        propertyNameValueString.Add(prefix + "*(List)" + propertyInfo.Name + "-->");
                        string primitivePropertyStr = string.Empty;
                        for (int i = 0; i <= list.Count - 1; i++)
                        {
                            if (genericArg.IsPrimitive)
                            {
                                primitivePropertyStr += "0x" + int.Parse(list[i].ToString()).ToString("X").PadLeft(2, '0') + " ";

                            }
                            else
                            {
                                this.ToLogStringHelper(list[i], propertyNameValueString, prefix + "   [" + i + "]");
                                if (list[i] is IFormattable) propertyNameValueString.Add(prefix + ((list[i] as IFormattable)).ToString(null, null));
                            }
                        }

                        propertyNameValueString.Add(primitivePropertyStr);
                        propertyNameValueString.Add(prefix + "*(List)" + propertyInfo.Name + "<--");
                    }
                }
            }

            if (!string.IsNullOrEmpty(plainPropertyLogStrAccumulator))
            {
                // Add prefix and remove the last ", "
                plainPropertyLogStrAccumulator = prefix + plainPropertyLogStrAccumulator.Remove(plainPropertyLogStrAccumulator.Length - 2);
                propertyNameValueString.Insert(originalCount, plainPropertyLogStrAccumulator);
            }

            return propertyNameValueString;
        }
    }
}