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

namespace TEngine.Localization
{
	using TranslationDictionary = Dictionary<string, TranslationQuery>;

    public struct TranslationQuery
    {
        public string OrigText;
        public string Text;           // Text without Tags
        public string LanguageCode;
        public string[] TargetLanguagesCode;
        public string[] Results;			// This is filled google returns the translations
        public string[] Tags;               // This are the sections of the orignal text that shoudn't be translated
    }

    public static partial class GoogleTranslation
	{
        public static void CreateQueries(string text, string LanguageCodeFrom, string LanguageCodeTo, TranslationDictionary dict)
        {
            if (!text.Contains("[i2s_"))
            {
                CreateQueries_Plurals(text, LanguageCodeFrom, LanguageCodeTo, dict);
                return;
            }

            var variants = SpecializationManager.GetSpecializations(text);
            foreach (var kvp in variants)
            {
                CreateQueries_Plurals(kvp.Value, LanguageCodeFrom, LanguageCodeTo, dict);
            }
        }

        static void CreateQueries_Plurals(string text, string LanguageCodeFrom, string LanguageCodeTo, TranslationDictionary dict)
        {
            bool hasPluralParams = text.Contains("{[#");
            bool hasPluralTypes = text.Contains("[i2p_");
            if (!HasParameters(text) || !hasPluralParams && !hasPluralTypes)
            {
                AddQuery(text, LanguageCodeFrom, LanguageCodeTo, dict);
                return;
            }

            bool forcePluralParam = hasPluralParams;

            for (var i = (ePluralType)0; i <= ePluralType.Plural; ++i)
            {
                var pluralType = i.ToString();
                if (!GoogleLanguages.LanguageHasPluralType(LanguageCodeTo, pluralType))
                    continue;

                var newText = GetPluralText(text, pluralType);
                int testNumber = GoogleLanguages.GetPluralTestNumber(LanguageCodeTo, i);

                var parameter = GetPluralParameter(newText, forcePluralParam);
                if (!string.IsNullOrEmpty(parameter))
                    newText = newText.Replace(parameter, testNumber.ToString());

                //Debug.Log("Translate: " + newText);

                AddQuery(newText, LanguageCodeFrom, LanguageCodeTo, dict);
            }
        }

        public static void AddQuery(string text, string LanguageCodeFrom, string LanguageCodeTo, TranslationDictionary dict)
        {
            if (string.IsNullOrEmpty(text))
                return;


            if (!dict.ContainsKey(text))
            {
                var query = new TranslationQuery { OrigText = text, LanguageCode = LanguageCodeFrom, TargetLanguagesCode = new[] { LanguageCodeTo } };
                query.Text = text;
                ParseNonTranslatableElements(ref query);
                dict[text] = query;
            }
            else
            {
                var query = dict[text];
                if (Array.IndexOf(query.TargetLanguagesCode, LanguageCodeTo) < 0)
                {
                    query.TargetLanguagesCode = query.TargetLanguagesCode.Concat(new[] { LanguageCodeTo }).Distinct().ToArray();
                }
                dict[text] = query;
            }
        }

        static string GetTranslation(string text, string LanguageCodeTo, TranslationDictionary dict)
        {
            if (!dict.ContainsKey(text))
                return null;
            var query = dict[text];

            int langIdx = Array.IndexOf(query.TargetLanguagesCode, LanguageCodeTo);
            if (langIdx < 0)
                return "";

            if (query.Results == null)
                return "";
            return query.Results[langIdx];
        }

        static TranslationQuery FindQueryFromOrigText(string origText, TranslationDictionary dict)
        {
            foreach (var kvp in dict)
            {
                if (kvp.Value.OrigText == origText)
                    return kvp.Value;
            }
            return default(TranslationQuery);
        }

        public static bool HasParameters( string text )
        {
            int idx = text.IndexOf("{[", StringComparison.Ordinal);
            if (idx < 0) return false;
            return text.IndexOf("]}", idx, StringComparison.Ordinal) > 0;
        }

        public static string GetPluralParameter(string text, bool forceTag)  // force tag requires that the parameter has the form {[#param]}
        {
            // Try finding the "plural parameter" that has the form {[#name]}
            // this allows using the second parameter as plural:  "Player {[name1]} has {[#value]} points"
            int idx0 = text.IndexOf("{[#", StringComparison.Ordinal);
            if (idx0 < 0)
            {
                if (forceTag) return null;
                idx0 = text.IndexOf("{[", StringComparison.Ordinal); // fallback to the first paremeter if no one has the # tag
            }
            if (idx0 < 0)
                return null;

            int idx1 = text.IndexOf("]}", idx0+2, StringComparison.Ordinal);
            if (idx1 < 0)
                return null;     // no closing parameter tag

            return text.Substring(idx0, idx1 - idx0 + 2);
        }

        public static string GetPluralText( string text, string pluralType )
        {
            pluralType = "[i2p_" + pluralType + "]";
            int idx0 = text.IndexOf(pluralType, StringComparison.Ordinal);
            if (idx0>=0)
            {
                idx0 += pluralType.Length;
                int idx1 = text.IndexOf("[i2p_",idx0, StringComparison.Ordinal);
                if (idx1 < 0) idx1 = text.Length;

                return text.Substring(idx0, idx1 - idx0);
            }

            // PluralType not found, fallback to the first one
            idx0 = text.IndexOf("[i2p_", StringComparison.Ordinal);
            if (idx0 < 0)
                return text;                      // No plural tags:   "my text"

            if (idx0>0)
                return text.Substring(0, idx0);   // Case: "my text[i2p_zero]hello"

            // Case: "[i2p_plural]my text[i2p_zero]hello"
            idx0 = text.IndexOf("]", StringComparison.Ordinal);
            if (idx0 < 0) return text;  // starts like a plural, but has none

            idx0 += 1;
            int idx2 = text.IndexOf("[i2p_", idx0, StringComparison.Ordinal);
            if (idx2 < 0) idx2 = text.Length;
            return text.Substring(idx0, idx2 - idx0);
        }
         
        static int FindClosingTag(string tag, MatchCollection matches, int startIndex)
        {
            for (int i = startIndex, imax = matches.Count; i < imax; ++i)
            {
                var newTag = I2Utils.GetCaptureMatch(matches[i]);
                if (newTag[0]=='/' && tag.StartsWith(newTag.Substring(1), StringComparison.Ordinal))
                    return i;
            }
            return -1;
        }

	    static string GetGoogleNoTranslateTag(int tagNumber)
	    {
	        //return " I2NT" + tagNumber;
	        if (tagNumber < 70)
	            return "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++".Substring(0, tagNumber+1);

	        string s = "";
	        for (int i = -1; i < tagNumber; ++i)
	            s += "+";
	        return s;
	    }


        static void ParseNonTranslatableElements( ref TranslationQuery query )
        {
            //\[i2nt].*\[\/i2nt]
            var matches = Regex.Matches(  query.Text, @"\{\[(.*?)]}|\[(.*?)]|\<(.*?)>");
            if (matches == null || matches.Count == 0)
                return;

            string finalText = query.Text;
            List<string> finalTags = new List<string>();
            for (int i=0, imax=matches.Count; i<imax; ++i)
            {
                var tag = I2Utils.GetCaptureMatch( matches[i] );
                int iClosingTag = FindClosingTag(tag, matches, i); //  find [/tag] or </tag>

                if (iClosingTag < 0)
                {
                    // Its not a tag, its a parameter
                    var fulltag = matches[i].ToString();
                    if (fulltag.StartsWith("{[", StringComparison.Ordinal) && fulltag.EndsWith("]}", StringComparison.Ordinal))
                    {
                        finalText = finalText.Replace(fulltag, GetGoogleNoTranslateTag(finalTags.Count)+" ");  //  0x2600 is the start of the UNICODE Miscellaneous Symbols table, so they are not going to be translated by google
                        //finalText = finalText.Replace(fulltag, /*"{[" + finalTags.Count + "]}"*/ ((char)(0x2600 + finalTags.Count)).ToString());  //  0x2600 is the start of the UNICODE Miscellaneous Symbols table, so they are not going to be translated by google
                        finalTags.Add(fulltag);
                    }
                    continue;
                }

                if (tag == "i2nt")
                {
                    var tag1 = query.Text.Substring(matches[i].Index, matches[iClosingTag].Index-matches[i].Index + matches[iClosingTag].Length);
                    finalText = finalText.Replace(tag1, GetGoogleNoTranslateTag(finalTags.Count)+" ");
                    //finalText = finalText.Replace(tag1, /*"{[" + finalTags.Count + "]}"*/ ((char)(0x2600 + finalTags.Count)).ToString());
                    
                    finalTags.Add(tag1);
                }
                else
                {
                    var tag1 = matches[i].ToString();
                    finalText = finalText.Replace(tag1, GetGoogleNoTranslateTag(finalTags.Count)+" ");
                    //finalText = finalText.Replace(tag1, /*"{[" + finalTags.Count + "]}"*/ ((char)(0x2600 + finalTags.Count)).ToString());
                    finalTags.Add(tag1);

                    var tag2 = matches[iClosingTag].ToString();
                    finalText = finalText.Replace(tag2, GetGoogleNoTranslateTag(finalTags.Count)+" ");
                    //finalText = finalText.Replace(tag2, /*"{[" + finalTags.Count + "]}"*/ ((char)(0x2600 + finalTags.Count)).ToString());
                    finalTags.Add(tag2);
                }
            }

            query.Text = finalText;
            query.Tags = finalTags.ToArray();
        }

        public static string GetQueryResult(string text, string LanguageCodeTo, TranslationDictionary dict)
        {
            if (!dict.ContainsKey(text))
                return null;

            var query = dict[text];
            if (query.Results == null || query.Results.Length < 0)
                return null;

            if (string.IsNullOrEmpty(LanguageCodeTo))
                return query.Results[0];

            int idx = Array.IndexOf(query.TargetLanguagesCode, LanguageCodeTo);
            if (idx < 0)
                return null;

            return query.Results[idx];
        }

        public static string RebuildTranslation(string text, TranslationDictionary dict, string LanguageCodeTo)
        {
            if (!text.Contains("[i2s_"))
            {
                return RebuildTranslation_Plural(text, dict, LanguageCodeTo);
            }

            var variants = SpecializationManager.GetSpecializations(text);
            var results = new Dictionary<string,string>(StringComparer.Ordinal);

            foreach (var kvp in variants)
            {
                results[kvp.Key] = RebuildTranslation_Plural(kvp.Value, dict, LanguageCodeTo);
            }
            return SpecializationManager.SetSpecializedText(results);
        }

        static string RebuildTranslation_Plural( string text, TranslationDictionary dict, string LanguageCodeTo )
		{
            bool hasPluralParams = text.Contains("{[#");
            bool hasPluralTypes = text.Contains("[i2p_");
            if (!HasParameters(text) || !hasPluralParams && !hasPluralTypes)
            {
				return GetTranslation (text, LanguageCodeTo, dict);
			}

            var sb = new StringBuilder();

            string pluralTranslation = null;
            bool forcePluralParam = hasPluralParams;


            for (var i = ePluralType.Plural; i >= 0; --i)
            {
                var pluralType = i.ToString();
                if (!GoogleLanguages.LanguageHasPluralType(LanguageCodeTo, pluralType))
                    continue;

                var newText = GetPluralText(text, pluralType);
                int testNumber = GoogleLanguages.GetPluralTestNumber(LanguageCodeTo, i);

                var parameter = GetPluralParameter(newText, forcePluralParam);
                if (!string.IsNullOrEmpty(parameter))
                    newText = newText.Replace(parameter, testNumber.ToString());

                var translation = GetTranslation(newText, LanguageCodeTo, dict);
                //Debug.LogFormat("from: {0}  ->  {1}", newText, translation);
                if (!string.IsNullOrEmpty(parameter))
                    translation = translation.Replace(testNumber.ToString(), parameter);

                if (i==ePluralType.Plural)
                {
                    pluralTranslation = translation;
                }
                else
                {
                    if (translation == pluralTranslation)
                        continue;
                    sb.AppendFormat("[i2p_{0}]", pluralType);
                }
                sb.Append(translation);
            }

 			return sb.ToString ();
		}



		public static string UppercaseFirst(string s)
		{
			if (string.IsNullOrEmpty(s))
			{
				return string.Empty;
			}
			char[] a = s.ToLower().ToCharArray();
			a[0] = char.ToUpper(a[0]);
			return new string(a);
		}
		public static string TitleCase(string s)
		{
			if (string.IsNullOrEmpty(s))
			{
				return string.Empty;
			}

#if NETFX_CORE
			var sb = new StringBuilder(s);
			sb[0] = char.ToUpper(sb[0]);
			for (int i = 1, imax=s.Length; i<imax; ++i)
			{
				if (char.IsWhiteSpace(sb[i - 1]))
					sb[i] = char.ToUpper(sb[i]);
				else
					sb[i] = char.ToLower(sb[i]);
			}
			return sb.ToString();
#else
            return CultureInfo.CurrentCulture.TextInfo.ToTitleCase(s);
#endif
		}
	}
}

