using System;
using System.Reflection;
using nft.util;
using System.Globalization;
using System.Collections.Generic;
using System.Diagnostics;

namespace nft.framework
{
	/// <summary>
	/// Internationalization, the utility class for multi language support.
    /// 多言語対応
    /// (I18nとは I-18文字略-n という意味でプログラミング言語の世界でよく使われる略称です。)
	/// </summary>
	public class I18n
	{
        static readonly string COMMON_GROUP = "common";
        static List<NftProperties> properties;
        static private CultureInfo invariant;

        static I18n() {
            invariant = CultureInfo.InvariantCulture;
            properties = new List<NftProperties>();
        }

        public static void LoadFile(string path) {
            NftProperties pps = NftProperties.LoadFromFile(path, true);
            properties.Insert(0,pps);
        }

        /// <summary>
        /// Get localized text by name, pick caller automatically from stack.
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public static string T(string name) {
            return _T1(name, ToGroupCandidates(1), true);
        }

        /// <summary>
        /// Get localized text by name, suppose caller type as group name.
        /// </summary>
        /// <param name="caller">caller object or type</param>
        /// <param name="name"></param>
        /// <returns></returns>
        public static string T(object caller, string name) {
            return _T2(name, ToGroupCandidates(caller), true);
        }

        /// <summary>
        /// Get localized text by name, pick caller automatically from stack.
        /// 'args' are inserted using String.Format method.
        /// </summary>
        /// <param name="name"></param>
        /// <param name="args">insert parameters</param>
        /// <returns></returns>
        public static string F(string name, params object[] args) {
            string ret = _T1(name, ToGroupCandidates(1), true);
            return String.Format(ret,args);
        }

        /// <summary>
        /// Get localized text by name, pick caller automatically from stack.
        /// placeholders of parameters like a part '#{hoge}' are replaced by dictionary value.
        /// </summary>
        /// <param name="name"></param>
        /// <param name="_params">insert parameters</param>
        /// <returns></returns>
        public static string F(string name, Dictionary<string, object> _params) {
            string ret = _T1(name, ToGroupCandidates(1), true);
            foreach (KeyValuePair<string, object> pair in _params) {
                ret = ret.Replace("#{" + pair.Key + "}", ToString(pair.Value));
            }
            return ret;
        }

        /// <summary>
        /// Get localized text by name, suppose caller type as group name.
        /// 'args' are inserted using String.Format method.
        /// </summary>
        /// <param name="caller">caller object or type</param>
        /// <param name="name"></param>
        /// <param name="args">insert parameters</param>
        /// <returns></returns>
        public static string F2(object caller, string name, params object[] args) {
            string ret = _T2(name, ToGroupCandidates(caller), true);
            return String.Format(ret, ToString(args));
        }

        /// <summary>
        /// Get localized text by name, suppose caller type as group name.
        /// placeholders of parameters like a part '#{hoge}' are replaced by dictionary value.
        /// </summary>
        /// <param name="caller">caller object or type</param>
        /// <param name="name"></param>
        /// <param name="_params">insert parameters</param>
        /// <returns></returns>
        public static string F2(object caller, string name, Dictionary<string, object> _params) {
            string ret = _T2(name, ToGroupCandidates(caller), true);
            foreach (KeyValuePair<string, object> pair in _params) {
                ret = ret.Replace("#{" + pair.Key + "}", ToString(pair.Value));
            }
            return ret;
        }

        protected static string _T1(string name, ICollection<string> groupCandidates, bool strict) {
            string ret = null;
            foreach (string group in groupCandidates) {
                ret = GetText(group, name);
                if (ret != null) return ret;
            }
            if (strict) {
                string tname = name.Trim();
                if (!tname.Equals(name)) {
                    ret = _T1(tname, groupCandidates, false);
                    if (ret != null) return '?' + ret;
                }
                TextInfo textInfo = invariant.TextInfo;
                tname = textInfo.ToTitleCase(name);
                if(!tname.Equals(name)) {
                    ret = _T1(tname, groupCandidates, false);
                    if (ret != null) return '?' + ret;
                }
                tname = textInfo.ToLower(name);
                if(!tname.Equals(name)) {
                    ret = _T1(tname, groupCandidates, false);
                    if (ret != null) return '?' + ret;
                }
            }
            return name;
        }

        protected static string _T2(string name, ICollection<string> groupCandidates, bool strict) {
            string ret = null;
            int n = 0;
            foreach (string group in groupCandidates) {
                ret = GetText(group, name);
                if (ret != null) {
                    return (n == 0) ? ret : '?' + ret;
                }
                n++;
            }
            if (strict) {
                string tname = name.Trim();
                if (!tname.Equals(name)) {
                    ret = _T2(tname, groupCandidates, false);
                    if (ret != null) return '?' + ret;
                }
                TextInfo textInfo = invariant.TextInfo;
                tname = textInfo.ToTitleCase(name);
                if (!tname.Equals(name)) {
                    ret = _T2(tname, groupCandidates, false);
                    if (ret != null) return '?' + ret;
                }
                tname = textInfo.ToLower(name);
                if (!tname.Equals(name)) {
                    ret = _T2(tname, groupCandidates, false);
                    if (ret != null) return '?' + ret;
                }
            }
            return name;
        }

        protected static string GetText(string group, string name) {
            foreach(NftProperties pps in properties){
                Property p1 = pps[group + Property.SEPARATOR + name, false];
                if (p1 != null) return p1.stringValue;
            }
            return null;
        }

        protected static string ToString(object param) {
            return param.ToString();
        }

        protected static object[] ToString(ref object[] _params) {
            int n = _params.Length;
            for (int i = 0; i < n; i++) {
                _params[i] = ToString(_params[i]);
            }
            return _params;
        }

        protected static ICollection<String> ToGroupCandidates(int stackcount) {
            StackFrame callerFrame = new StackFrame(++stackcount);
            Type callerType = callerFrame.GetMethod().ReflectedType;
            return TypeToGroupCandidates(callerType);
        }

        protected static ICollection<String> ToGroupCandidates(object caller) {
            if (caller is string || caller.GetType().IsPrimitive) {
                List<String> l1 = new List<string>(1);
                string name = caller.ToString();
                l1.Add(name);
                string tname = name.ToLower();
                if (!tname.Equals(name)) {
                    l1.Add(tname);
                }
                tname = name.ToUpper();
                if (!tname.Equals(name)) {
                    l1.Add(tname);
                }
                l1.Add(COMMON_GROUP);
                return l1;
            } else {
                Type callerType = (Type)((caller is Type) ? caller : caller.GetType());
                return TypeToGroupCandidates(callerType);
            }
        }

        protected static ICollection<String> TypeToGroupCandidates(Type callerType) {            
            List<Type> lstTypes = new List<Type>();
            lstTypes.Add(callerType);
            lstTypes.AddRange(callerType.GetInterfaces());
            Type btype = callerType.BaseType;
            while(btype!=null && !btype.Assembly.GlobalAssemblyCache){
                lstTypes.Add(btype);
                btype = btype.BaseType;
            }
            List<String> l1 = new List<string>(lstTypes.Count*2);
            List<String> l2 = new List<string>(lstTypes.Count);
            foreach (Type t in lstTypes) {
                l1.Add(t.FullName);
                l1.Add(t.Name);
                string ns = t.Namespace;
                if (!l2.Contains(ns)) l2.Add(ns);
            }
            l1.AddRange(l2);
            l1.Add(COMMON_GROUP);
            return l1;
        }
        
	}
}
