using System;
using System.IO;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.ComponentModel;

namespace Ease.Core
{
    public sealed class EnumHelper
    {
        private EnumHelper()
        {
        }

        /// <summary>
        /// Use EnumFormatter.ToString() instead.
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        [Obsolete("Use EnumFormatter.ToString()")]
        public static string GetDescription(Enum value)
        {
            return EnumFormatter.ToString(value);
        }
    }

    /// <summary>
    /// Provides formatting functions to convert enumeration values 
    /// to user-friendly strings by breaking proper-case enumeration
    /// name into seperate words. Use the ToString() method to get 
    /// user-friendly text. Use the Parse method to parse a user-firendly
    /// text to an enumeration value.
    /// </summary>
    public sealed class EnumFormatter
    {
        private static Dictionary<string, EnumItemCollection> _hashEnums;

        static EnumFormatter()
        {
            _hashEnums = new Dictionary<string, EnumItemCollection>();
        }

        /// <summary>
        /// convert enumeration values to user-friendly string
        /// by breaking proper-case enumeration
        /// name into seperate words. RecceivedFromOtherDepot = "Recceived From Other Depot".
        /// </summary>
        /// <param name="value">The value to be converted</param>
        /// <returns>Formatted string</returns>
        public static string ToString(Enum value)
        {
            string s = value.ToString();

            StringBuilder desc = new StringBuilder(s.Length);
            for (int i = 0; i < s.Length; i++)
            {
                if (!char.IsLower(s[i]) && i > 0 && char.IsLower(s[i - 1]))
                {
                    desc.Append(" ");
                }

                desc.Append(s[i]);
            }

            return desc.ToString();
        }

        /// <summary>
        /// Parse a formatted enumeration name into the value.
        /// "Recceived From Other Depot" --> RecceivedFromOtherDepot.
        /// Throws FormattingException if fails to convert to
        /// an enumeration value for the specified type.
        /// </summary>
        /// <param name="enumType">The type to convert to.</param>
        /// <param name="text">Text to parse</param>
        /// <returns></returns>
        public static Enum Parse(Type enumType, string text)
        {
            text = text.Replace(" ", "");

            return (Enum)Enum.Parse(enumType, text);
        }

        /// <summary>
        /// Return the actual value in integer.
        /// </summary>
        /// <param name="enumType">From based on which enum.</param>
        /// <param name="value">Value to be converted.</param>
        /// <returns>Integer value.</returns>
        public static int GetValue(Type enumType, Enum value)
        {
            return (Convert.ToInt32(Enum.Format(enumType, value, "d")));
        }

        /// <summary>
        /// Return the integer value.
        /// </summary>
        /// <param name="enumType">From based on which enum.</param>
        /// <param name="text">Friendly name or actual name.</param>
        /// <returns>Return an integer value.</returns>
        public static int GetValue(Type enumType, string text)
        {
            text = text.Replace(" ", "");

            return Convert.ToInt32(Enum.Parse(enumType, text));
        }

        /// <summary>
        /// Return the nteger value index zero based.
        /// </summary>
        /// <param name="enumType">From based on which enum.</param>
        /// <param name="value">Enum value.</param>
        /// <returns>Return an integer index.</returns>
        public static int GetIndex(Type enumType, Enum value)
        {
            string s = value.ToString();
            return GetIndex(enumType, s);
        }

        /// <summary>
        /// Return the nteger value index zero based.
        /// </summary>
        /// <param name="enumType">From based on which enum.</param>
        /// <param name="text">Friendly name or actual name.</param>
        /// <returns>Return an integer index.</returns>
        public static int GetIndex(Type enumType, string text)
        {
            if (_hashEnums.ContainsKey(enumType.FullName))
            {
                EnumItemCollection items = _hashEnums[enumType.FullName];
                if (items != null)
                {
                    text = text.Replace(" ", "");

                    EnumItem item = items[text];
                    if (item != null)
                        return item.Index;
                }
            }

            return -1;
        }

        /// <summary>
        /// Return string array of enum.
        /// </summary>
        /// <param name="enumType">From based on which enum.</param>
        /// <returns>Array of string.</returns>
        public static string[] GetNames(Type enumType)
        {
            EnumItemCollection items = EnumFormatter.GetObjects(enumType);

            if (items != null)
                return items.GetNames();

            return new string[] { };
        }

        /// <summary>
        /// Return EnumItemCollection object.
        /// </summary>
        /// <param name="enumType">From based on which enum.</param>
        /// <returns>Return EnumItemCollection object.</returns>
        public static EnumItemCollection GetObjects(Type enumType)
        {
            EnumItemCollection items;
            if (_hashEnums.ContainsKey(enumType.FullName))
            {
                items = (EnumItemCollection)_hashEnums[enumType.FullName];

                if (items != null)
                    return items;
            }

            int index = 0;
            string[] names = Enum.GetNames(enumType);
            items = new EnumItemCollection();

            foreach (string name in names)
            {
                string s = name;

                StringBuilder desc = new StringBuilder(s.Length);
                for (int i = 0; i < s.Length; i++)
                {
                    if (!char.IsLower(s[i]) && i > 0 && char.IsLower(s[i - 1]))
                    {
                        desc.Append(" ");
                    }

                    desc.Append(s[i]);
                }

                s = desc.ToString();

                EnumItem item = new EnumItem(index, name, s, EnumFormatter.GetValue(enumType, name));

                items.Add(item);
                index++;
            }

            _hashEnums.Add(enumType.FullName, items);

            return items;
        }

        /// <summary>
        /// Return EnumItemCollection object.
        /// </summary>
        /// <param name="enumType">From based on which enum.</param>
        /// <param name="values">Array of enums value.</param>
        /// <returns>Return EnumItemCollection object.</returns>
        public static EnumItemCollection GetObjects(Type enumType, Array values)
        {
            EnumItemCollection items;

            #region From Cached Items

            if (_hashEnums.ContainsKey(enumType.FullName))
            {
                EnumItemCollection cachedItems = (EnumItemCollection)_hashEnums[enumType.FullName];

                if (cachedItems != null)
                {
                    items = new EnumItemCollection();
                    foreach (EnumItem cachedItem in cachedItems)
                    {
                        foreach (Enum value in values)
                        {
                            int enumValue = Convert.ToInt32(Enum.Format(enumType, value, "d"));
                            if (cachedItem.Value == enumValue)
                            {
                                EnumItem item = new EnumItem(cachedItem.Index, cachedItem.Name, cachedItem.FriendlyName,
                                    cachedItem.Value);
                                items.Add(item);

                                break;
                            }
                        }
                    }

                    return items;
                }
            }

            #endregion

            #region Items agains Passed enums array

            string[] names = Enum.GetNames(enumType);
            items = new EnumItemCollection();

            foreach (string name in names)
            {
                string s = name;

                StringBuilder desc = new StringBuilder(s.Length);
                for (int i = 0; i < s.Length; i++)
                {
                    if (!char.IsLower(s[i]) && i > 0 && char.IsLower(s[i - 1]))
                    {
                        desc.Append(" ");
                    }

                    desc.Append(s[i]);
                }

                s = desc.ToString();

                int enumValue = Convert.ToInt32(Enum.Parse(enumType, name, true));

                foreach (Enum value in values)
                {
                    int enumParamValue = Convert.ToInt32(Enum.Format(enumType, value, "d"));
                    if (enumValue == enumParamValue)
                    {
                        EnumItem item = new EnumItem(items.Count, name, s, enumValue);
                        items.Add(item);
                        break;
                    }
                }
            }

            #endregion

            return items;
        }
    }

    #region Helper Class

    public sealed class EnumItem
    {
        #region Constructor

        internal EnumItem(int index, string name, string friendlyName, int value)
        {
            _name = name;
            _index = index;
            _value = value;
            _friendlyName = friendlyName;
        }

        #endregion

        #region Properies

        #region Property Index : int

        private readonly int _index;

        public int Index
        {
            get { return _index; }
        }

        #endregion

        #region Property Name : string

        private readonly string _name;

        public string Name
        {
            get { return _name; }
        }

        #endregion

        #region Property FriendlyName  : string

        private readonly string _friendlyName;

        public string FriendlyName
        {
            get { return _friendlyName; }
        }

        #endregion

        #region Property Value : int

        private readonly int _value;

        public int Value
        {
            get { return _value; }
        }

        #endregion

        #endregion

        #region Functions

        /// <summary>
        /// Use to create an EnumItem object
        /// </summary>
        /// <param name="enumType">Type of Enum i.e TypeOf(UserTypeEnum) </param>
        /// <param name="value">Value of enum i.e UserTypeEnum.Admin</param>
        /// <returns>Returns an instatnce of EnumItem</returns>
        public static EnumItem FromValue(Type enumType, Enum value)
        {
            int enumValue = Convert.ToInt32(Enum.Format(enumType, value, "d"));
            return EnumItem.FromValue(enumType, enumValue);
        }

        /// <summary>
        /// Use to create an EnumItem object
        /// </summary>
        /// <param name="enumType">Type of Enum i.e TypeOf(UserTypeEnum) </param>
        /// <param name="value">Valid enum value in int</param>
        /// <returns>Returns an instatnce of EnumItem</returns>
        public static EnumItem FromValue(Type enumType, int value)
        {
            int idx = -1;
            EnumItem item = null;

            string[] names = Enum.GetNames(enumType);
            foreach (string name in names)
            {
                idx += 1;
                int val = Convert.ToInt32(Enum.Parse(enumType, name, true));
                if (val != value)
                    continue;

                string s = name;
                StringBuilder desc = new StringBuilder(s.Length);
                for (int i = 0; i < s.Length; i++)
                {
                    if (!char.IsLower(s[i]) && i > 0 && char.IsLower(s[i - 1]))
                        desc.Append(" ");

                    desc.Append(s[i]);
                }

                s = desc.ToString();

                item = new EnumItem(idx, name, s, value);
                break;
            }

            return item;
        }

        /// <summary>
        /// Use to create an EnumItem object
        /// </summary>
        /// <param name="enumType">Type of Enum i.e TypeOf(UserTypeEnum) </param>
        /// <param name="value">Valid value of enum in string i.e Admin</param>
        /// <returns>Returns an instatnce of EnumItem</returns>
        public static EnumItem FromValue(Type enumType, string value)
        {
            int enumValue = 0;
            if (!int.TryParse(value, out enumValue))
                enumValue = -99999;

            if (enumValue == -99999)
                throw new Exception(string.Format("Invalid use of value: {0}", value));

            return EnumItem.FromValue(enumType, enumValue);
        }

        /// <summary>
        /// Use to create an EnumItem object
        /// </summary>
        /// <param name="enumType">Type of Enum i.e TypeOf(UserTypeEnum)</param>
        /// <param name="values">Custom arry of enum i.e new UserTypeEnum[]{UserTypeEnum.SuperUser, UserTypeEnum.Admin}</param>
        /// <param name="value">Value of enum i.e UserTypeEnum.Admin</param>
        /// <returns>Returns an instatnce of EnumItem</returns>
        public static EnumItem FromValue(Type enumType, Array values, Enum value)
        {
            int idx = -1;
            EnumItem item = null;
            int enumValue = Convert.ToInt32(Enum.Format(enumType, value, "d"));
            foreach (Enum arValue in values)
            {
                idx += 1;
                string name = arValue.ToString();
                int val = Convert.ToInt32(Enum.Parse(enumType, name, true));
                if (val != enumValue)
                    continue;

                string s = name;
                StringBuilder desc = new StringBuilder(s.Length);
                for (int i = 0; i < s.Length; i++)
                {
                    if (!char.IsLower(s[i]) && i > 0 && char.IsLower(s[i - 1]))
                        desc.Append(" ");

                    desc.Append(s[i]);
                }

                s = desc.ToString();

                item = new EnumItem(idx, name, s, enumValue);
                break;
            }

            if (item == null)
                return new EnumItem(-1, "Unknown", "Unknown", enumValue);

            return item;
        }

        #endregion
    }

    public sealed class EnumItemCollection : CollectionBase
    {
        #region Constructor

        private Hashtable _hashTable;

        internal EnumItemCollection()
        {
            _hashTable = new Hashtable();
            InnerList.Clear();
        }

        #endregion

        #region Functions

        internal void Add(EnumItem item)
        {
            if (!_hashTable.ContainsKey(item.Name))
            {
                _hashTable.Add(item.Name, item);
                InnerList.Add(item);
            }
        }

        public EnumItem this[Enum value]
        {
            get
            {
                string s = value.ToString();
                return this[s];
            }
        }

        public EnumItem this[string valueMember]
        {
            get
            {
                valueMember = valueMember.Replace(" ", "");

                if (_hashTable.ContainsKey(valueMember))
                    return (EnumItem)_hashTable[valueMember];

                return null;
            }
        }

        public EnumItem this[int index]
        {
            get { return (EnumItem)InnerList[index]; }
        }

        internal string[] GetNames()
        {
            string[] names = null;
            if (this.Count > 0)
            {
                names = new string[this.Count];
                for (int index = 0; index < this.Count; index++)
                {
                    names[index] = this[index].FriendlyName;
                }
            }

            return names;
        }

        #endregion
    }

    public class EnumDescription
    {
        public static string GetEnumDescription(Enum value)
        {
            FieldInfo fi = value.GetType().GetField(value.ToString());
            DescriptionAttribute[] attributes =
              (DescriptionAttribute[])fi.GetCustomAttributes
              (typeof(DescriptionAttribute), false);

            return (attributes.Length > 0) ? attributes[0].Description : value.ToString();
        }
    }

    #endregion
}