using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

namespace CommonLang
{
    public static class Parser
    {
        private static HashMap<Type, ParserAdapter> mParsers = new HashMap<Type, ParserAdapter>();
        private static string FloatFormat = "F";

        static Parser()
        {
            RegistParser(new ListParser<sbyte>());
            RegistParser(new ListParser<byte>());
            RegistParser(new ListParser<short>());
            RegistParser(new ListParser<ushort>());
            RegistParser(new ListParser<int>());
            RegistParser(new ListParser<uint>());
            RegistParser(new ListParser<long>());
            RegistParser(new ListParser<ulong>());
            RegistParser(new ListParser<float>());
            RegistParser(new ListParser<double>());
            RegistParser(new ListParser<decimal>());
            RegistParser(new ListParser<char>());
            RegistParser(new ListParser<bool>());
            RegistParser(new ListParser<string>());
            RegistParser(new ListParser<object>());
        }

        public static void RegistParser(ParserAdapter parser)
        {
            mParsers.Put(parser.ParserType, parser);
        }
        public static void SetFloatFormat(string format)
        {
            FloatFormat = format;
        }


        public static bool TryConvertTo(object src, Type targetType)
        {
            object target;
            return TryConvertTo(src, targetType, out target);
        }
        public static bool TryConvertTo(object src, Type targetType, out object target)
        {
            if (targetType.IsInstanceOfType(src))
            {
                target = src;
                return true;
            }
            if (targetType.IsPrimitive && src.GetType().IsPrimitive)
            {
                try
                {
                    target = Convert.ChangeType(src, targetType);
                    if (targetType.IsInstanceOfType(target))
                    {
                        return true;
                    }
                }
                catch (Exception err)
				{
					Console.WriteLine("TryConvertTo : " + src + " -> " + targetType + ", catch: " + err);
				}
            }
            target = null;
            return false;
        }


        public static bool StringToObject<T>(string text, out T ret)
        {
            Type type = typeof(T);
            try
            {
                ret = (T)StringToObject(text, type);
                return true;
            }
            catch (Exception err)
            {
				Console.WriteLine("StringToObject 1: " + text + ", catch: " + err);
				ret = default(T);
                return false;
            }
        }

        public static object StringToObject(string text, Type type)
        {
            if (String.IsNullOrEmpty(text))
            {
                return null;
            }
            ParserAdapter parser = mParsers.Get(type);
            if (parser != null)
            {
                object ret = parser.StringToObject(text);
                if (ret != null)
                {
                    return ret;
                }
            }
            if (type.IsArray)
            {
                int rank = type.GetArrayRank();
                if (rank == 1)
                {
                    Type subtype = type.GetElementType();
                    string[] kvs = text.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                    Array array = Array.CreateInstance(subtype, kvs.Length);
                    for (int i = 0; i < kvs.Length; i++)
                    {
                        object obj = StringToObject(kvs[i], subtype);
                        array.SetValue(obj, i);
                    }
                    return array;
                }
                else if (rank == 2)
                {
                    int a0 = text.IndexOf('{');
                    int a1 = text.LastIndexOf('}');
                    if (a0 >= 0 && a1 > a0)
                    {
                        Type subtype = type.GetElementType();
                        Array array = null;
                        string stext = text.Substring(a0 + 1, a1 - a0 - 1);
                        string[] range0 = Regex.Split(stext, @"\}\s*,\s*\{");
                        for (int i = 0; i < range0.Length; i++)
                        {
                            string[] range1 = range0[i].Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                            if (array == null)
                            {
                                array = Array.CreateInstance(subtype, range0.Length, range1.Length);
                            }
                            for (int j = 0; j < range1.Length; j++)
                            {
                                object obj = StringToObject(range1[j], subtype);
                                array.SetValue(obj, i, j);
                            }
                        }
                        return array;
                    }
                }
            }
            if (type.IsEnum)
            {
                try
                {
                    return Enum.Parse(type, text, true);
                }
                catch (Exception err)
                {
					Console.WriteLine("StringToObject2 : " + text + ", catch: " + err);
					return null;
                }
            }

            if (type.IsAssignableFrom(typeof(string)))
            {
                return text;
            }
            if (type.IsAssignableFrom(typeof(int)))
            {
                return int.Parse(text);
            }
            if (type.IsAssignableFrom(typeof(uint)))
            {
                return uint.Parse(text);
            }
            if (type.IsAssignableFrom(typeof(short)))
            {
                return short.Parse(text);
            }
            if (type.IsAssignableFrom(typeof(ushort)))
            {
                return ushort.Parse(text);
            }
            if (type.IsAssignableFrom(typeof(byte)))
            {
                return byte.Parse(text);
            }
            if (type.IsAssignableFrom(typeof(sbyte)))
            {
                return sbyte.Parse(text);
            }

            if (type.IsAssignableFrom(typeof(bool)))
            {
                return bool.Parse(text);
            }

            if (type.IsAssignableFrom(typeof(float)))
            {
                if (text == float.NaN.ToString())
                {
                    return float.NaN;
                }
                return float.Parse(text, System.Globalization.CultureInfo.InvariantCulture);
            }
            if (type.IsAssignableFrom(typeof(double)))
            {
                return float.Parse(text, System.Globalization.CultureInfo.InvariantCulture);
            }
            return null;
        }

        public static string ObjectToString(object obj)
        {
            if (obj == null)
            {
                return "";
            }
            Type type = obj.GetType();
            ParserAdapter parser = mParsers.Get(type);
            if (parser != null)
            {
                string ret = parser.ObjectToString(obj);
                if (ret != null)
                {
                    return ret;
                }
            }
            if (type.IsArray)
            {
                int rank = type.GetArrayRank();
                if (rank == 1)
                {
                    Array array = (Array)obj;
                    StringBuilder sb = new StringBuilder();
                    Type subtype = type.GetElementType();
                    for (int i = 0; i < array.Length; i++)
                    {
                        sb.Append(ObjectToString(array.GetValue(i)));
                        if (i < array.Length - 1)
                        {
                            sb.Append(",");
                        }
                    }
                    return sb.ToString();
                }
                else if (rank == 2)
                {
                    Array array = (Array)obj;
                    StringBuilder sb = new StringBuilder();
                    for (int i = 0; i < array.GetLength(0); i++)
                    {
                        sb.Append("{");
                        for (int j = 0; j < array.GetLength(1); j++)
                        {
                            sb.Append(ObjectToString(array.GetValue(i, j)));
                            if (j < array.GetLength(1) - 1)
                            {
                                sb.Append(",");
                            }
                        }
                        sb.Append("}");
                        if (i < array.GetLength(0) - 1)
                        {
                            sb.Append(",");
                        }
                    }
                    return sb.ToString();
                }
            }
            if (type.IsEnum)
            {
                return Enum.GetName(type, obj);
            }
            if (obj is float)
            {
                return ((float)obj).ToString(FloatFormat);
            }
            if (obj is double)
            {
                return ((double)obj).ToString(FloatFormat);
            }
            return obj + "";
        }

        public abstract class ParserAdapter
        {
            readonly public Type ParserType;

            public ParserAdapter(Type type)
            {
                this.ParserType = type;
            }

            public abstract object StringToObject(string text);
            public abstract string ObjectToString(object obj);
        }


        public class ListParser<T> : ParserAdapter
        {
            public ListParser()
                : base(typeof(List<T>))
            {

            }

            public override object StringToObject(string text)
            {
                string[] ss = text.Split(',');
                List<T> ret = null;
                if (ss.Length > 0)
                {
                    ret = new List<T>();
                    foreach (string s in ss)
                    {
                        T d = (T)Parser.StringToObject(s, typeof(T));
                        ret.Add(d);
                    }
                }
                return ret;
            }

            public override string ObjectToString(object obj)
            {
                List<T> list = (List<T>)obj;
                StringBuilder sb = new StringBuilder();
                int i = 0;
                foreach (T d in list)
                {
                    sb.Append(ObjectToString(d));
                    if (i < list.Count - 1)
                    {
                        sb.Append(",");
                    }
                    i++;
                }
                return sb.ToString();
            }
        }
    }
}