// Created with MonoDevelop
//
//    Copyright (C) 2016 Hiperscan GmbH Dresden, Germany
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections;
using System.Reflection;
using System.Threading;


namespace Hiperscan.Common
{

    public class StringValueAttribute : System.Attribute
    {
        public StringValueAttribute(string value)
        {
            this.Value = value;
        }

        public string Value { get; }
    }

    public class StringEnum
    {

        private static Hashtable string_value = Hashtable.Synchronized(new Hashtable());
        private static readonly Mutex mutex = new Mutex();

        public StringEnum(Type enum_type)
        {
            if (enum_type.IsEnum == false)
                throw new ArgumentException(string.Format("Type must be an Enum. Type was {0}", enum_type.ToString()));

            this.EnumType = enum_type;
        }

        public string GetStringValue(string value_name)
        {
            try
            {
                return GetStringValue((Enum)Enum.Parse(this.EnumType, value_name));
            }
            catch 
            {
                return null;
            }
        }

        public Array GetStringValues()
        {
            ArrayList values = new ArrayList();

            foreach (FieldInfo fi in this.EnumType.GetFields())
            {
                StringValueAttribute[] attrs = fi.GetCustomAttributes(typeof (StringValueAttribute), false) as StringValueAttribute[];
                if (attrs.Length > 0)
                    values.Add(attrs[0].Value);
            }

            return values.ToArray();
        }

        public IList GetListValues()
        {
            Type underlyingType = Enum.GetUnderlyingType(this.EnumType);
            ArrayList values = new ArrayList();

            foreach (FieldInfo fi in this.EnumType.GetFields())
            {
                StringValueAttribute[] attrs = fi.GetCustomAttributes(typeof (StringValueAttribute), false) as StringValueAttribute[];
                if (attrs.Length > 0)
                    values.Add(new DictionaryEntry(Convert.ChangeType(Enum.Parse(this.EnumType, fi.Name), underlyingType), attrs[0].Value));
                
            }

            return values;
        }

        public bool IsStringDefined(string string_value)
        {
            return Parse(this.EnumType, string_value) != null;
        }

        public bool IsStringDefined(string string_value, bool ignore_case)
        {
            return Parse(this.EnumType, string_value, ignore_case) != null;
        }

        public Type EnumType { get; }

        public static string GetStringValue(Enum value)
        {
            lock (mutex)
            {
                string output = null;
                Type type = value.GetType();

                if (StringEnum.string_value.ContainsKey(value))
                    output = (StringEnum.string_value[value] as StringValueAttribute).Value;
                else 
                {
                    FieldInfo fi = type.GetField(value.ToString());
                    StringValueAttribute[] attrs = fi.GetCustomAttributes(typeof (StringValueAttribute), false) as StringValueAttribute[];
                    if (attrs.Length > 0)
                    {
                        StringEnum.string_value.Add(value, attrs[0]);
                        output = attrs[0].Value;
                    }

                }
                return output;
            }
        }
 
        public static object Parse(Type type, string string_value)
        {
            return Parse(type, string_value, false);
        }

        public static object Parse(Type type, string string_value, bool ignore_case)
        {
            object output = null;
            string enum_string_value = null;

            if (type.IsEnum == false) 
                throw new ArgumentException(string.Format("Supplied type must be an Enum. Type was {0}", type.ToString()));
            
            foreach (FieldInfo fi in type.GetFields())
            {
                StringValueAttribute[] attrs = fi.GetCustomAttributes(typeof (StringValueAttribute), false) as StringValueAttribute[];
                if (attrs.Length > 0)
                    enum_string_value = attrs[0].Value;
                
                if (string.Compare(enum_string_value, string_value, ignore_case) == 0)
                {
                    output = Enum.Parse(type, fi.Name);
                    break;
                }
            }

            return output;
        }

        public static bool IsStringDefined(Type enum_type, string string_value)
        {
            return Parse(enum_type, string_value) != null;
        }

        public static bool IsStringDefined(Type enum_type, string string_value, bool ignore_case)
        {
            return Parse(enum_type, string_value, ignore_case) != null;
        }
    }
}
