using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

namespace SC.XR.Unity.Module_Keyboard
{
    public class SCKeyboard3DMono : SCKeyboardMono
    {
        #region Initialize
        public override void Initialize(SCKeyboardType keyboardType = SCKeyboardType.Default)
        {
            base.Initialize(keyboardType);

            ActiveKeyboard(KeyboardEnum.ABC,KeyboardState.Cn);
        }
        #endregion

        #region Keyboards
        protected SCKeyboard3DPrompt Keyboard3DPrompt
        {
            get
            {
                return keyboard_prompt as SCKeyboard3DPrompt;
            }
        }

        public GameObject keyboard_number;

        #endregion

        #region Key Clicks
        public override void OnPinyinKeyClick(string value)
        {
            preInputStringBuilder.Append(value);

            ActiveKeyboard(KeyboardEnum.Prompt);

            Keyboard3DPrompt.SetEnteredText(preInputStringBuilder.ToString());

            PinyinKeyHandler(preInputStringBuilder.ToString());
        }

        public override void OnPromptKeyClick(string value)
        {
            base.OnPromptKeyClick(value);
            HideKeyboardPrompt(false);
        }
        #endregion

        #region SpecialKeyClickEvents
        public override void OnABCClick()
        {
            ActiveKeyboard(KeyboardEnum.ABC, presentKeyboardState);
        }

        public override void OnDeleteClick()
        {
            OnWebKeyClick?.Invoke("Backspace");
            if (keyboard_prompt.isActiveAndEnabled)
            {
                preInputStringBuilder.Remove(preInputStringBuilder.Length - 1, 1);
                if (preInputStringBuilder.Length == 0)
                {
                    Keyboard3DPrompt.gameObject.SetActive(false);

                    preInputStringBuilder.Clear();
                    return;
                }
                Keyboard3DPrompt.SetEnteredText(preInputStringBuilder.ToString());
                PinyinKeyHandler(preInputStringBuilder.ToString());
            }
            else
            {
                OnDeleteKeyClick?.Invoke();
            }
        }

        public override void OnLanguageClick() {  }

        private string[] cnSymbols = new string[40] { ",", "。", "?", "”" ,
        "!", "@", "#", "¥", "%", "&", "(", ")", "-", "_", "=", "+", "\\", ";", ":", "、", "*", "/",
        "“", "”", "...", "·", "μ", "Σ", "《", "》", "【", "】", "{", "}", "|", "¢", "η", "°", "~", "……"};

        private string[] enSymbols = new string[40] { ",", ".", "?", "'" ,
        "!","@","#","$","%","&","(",")","-","_","=","+","\\",";",":","\"","*","/",
        "·", "¢", "∈", "£", "μ", "Σ", "<", ">", "[", "]", "{", "}", "|", "", "η", "°", "~", "^"};

        public override void OnCnClick()
        {
            HideKeyboardPrompt();

            //presentKeyboardState = KeyboardState.En;

            for (int i = 0; i < 4; i++)
            {
                if (symbolKeysDic.ContainsKey(cnSymbols[i]))
                {
                    for (int j = 0; j < symbolKeysDic[cnSymbols[i]].Count; j++)
                    {
                        symbolKeysDic[cnSymbols[i]][j].Value = enSymbols[i];
                        symbolKeysDic[cnSymbols[i]][j].GetComponentInChildren<TextMesh>().text = enSymbols[i];
                    }                            
                }
            }

            for (int i = 4; i < 22; i++)
            {
                if (symbolKeysDic.ContainsKey(cnSymbols[i]))
                {
                    for (int j = 0; j < symbolKeysDic[cnSymbols[i]].Count; j++)
                    {
                        symbolKeysDic[cnSymbols[i]][j].Value = enSymbols[i];
                        symbolKeysDic[cnSymbols[i]][j].GetComponentInChildren<TextMesh>().text = enSymbols[i];
                    }
                }
            }

        }
        public override void OnEnClick()
        {
            //presentKeyboardState = KeyboardState.Cn;
            
            for (int i = 0; i < 4; i++)
            {
                if (symbolKeysDic.ContainsKey(cnSymbols[i]))
                {
                    for (int j = 0; j < symbolKeysDic[cnSymbols[i]].Count; j++)
                    {
                        symbolKeysDic[cnSymbols[i]][j].Value = cnSymbols[i];
                        symbolKeysDic[cnSymbols[i]][j].GetComponentInChildren<TextMesh>().text = cnSymbols[i];
                    }
                }
            }

            for (int i = 4; i < 22; i++)
            {
                if (symbolKeysDic.ContainsKey(cnSymbols[i]))
                {
                    for (int j = 0; j < symbolKeysDic[cnSymbols[i]].Count; j++)
                    {
                        symbolKeysDic[cnSymbols[i]][j].Value = cnSymbols[i];
                        symbolKeysDic[cnSymbols[i]][j].GetComponentInChildren<TextMesh>().text = cnSymbols[i];
                    }
                }
            }
            
        }

        public override void OnPageDownClick()
        {
            if (presentKeyboard == keyboard_number)
            {
                switch (presentKeyboardState)
                {
                    case KeyboardState.Cn:
                        for (int i = 4; i < 22; i++)
                        {
                            if (symbolKeysDic.ContainsKey(cnSymbols[i]))
                            {
                                for (int j = 0; j < symbolKeysDic[cnSymbols[i]].Count; j++)
                                {
                                    symbolKeysDic[cnSymbols[i]][j].Value = cnSymbols[i + 18];
                                    symbolKeysDic[cnSymbols[i]][j].GetComponentInChildren<TextMesh>().text = cnSymbols[i + 18];
                                }
                            }
                        }
                        break;
                    case KeyboardState.En:
                        for (int i = 4; i < 22; i++)
                        {
                            if (symbolKeysDic.ContainsKey(cnSymbols[i]))
                            {
                                for (int j = 0; j < symbolKeysDic[cnSymbols[i]].Count; j++)
                                {
                                    symbolKeysDic[cnSymbols[i]][j].Value = enSymbols[i];
                                    symbolKeysDic[cnSymbols[i]][j].GetComponentInChildren<TextMesh>().text = enSymbols[i + 18];
                                }
                            }
                        }
                        break;
                }
            }
        }

        public override void OnPageUpClick()
        {
            if (presentKeyboard == keyboard_number)
            {                
                switch (presentKeyboardState)
                {
                    case KeyboardState.Cn:
                        for (int i = 4; i < 22; i++)
                        {
                            if (symbolKeysDic.ContainsKey(cnSymbols[i]))
                            {
                                for (int j = 0; j < symbolKeysDic[cnSymbols[i]].Count; j++)
                                {
                                    symbolKeysDic[cnSymbols[i]][j].Value = cnSymbols[i];
                                    symbolKeysDic[cnSymbols[i]][j].GetComponentInChildren<TextMesh>().text = cnSymbols[i];
                                }
                            }
                        }
                        break;
                    case KeyboardState.En:
                        for (int i = 4; i < 22; i++)
                        {
                            if (symbolKeysDic.ContainsKey(cnSymbols[i]))
                            {
                                for (int j = 0; j < symbolKeysDic[cnSymbols[i]].Count; j++)
                                {
                                    symbolKeysDic[cnSymbols[i]][j].Value = enSymbols[i];
                                    symbolKeysDic[cnSymbols[i]][j].GetComponentInChildren<TextMesh>().text = enSymbols[i];
                                }
                            }
                        }
                        break;
                }
            }
        }
        #endregion

        #region Language Key Click Events
        public override void OnChineseClick()
        {
            
        }

        public override void OnEnglishClick()
        {
            ActiveKeyboard(KeyboardEnum.ABC,KeyboardState.En);
        }

        public override void OnMoreClick()
        {

        }
        #endregion

        #region Key Handlers
        private Text m_font;
        private RectTransform m_PromptPage;
        private RectTransform m_MorePromptPage;
        public Text Font
        {
            get
            {
                if (m_font == null)
                {
                    m_font = Resources.Load<SCKeyboard2DKey>(keyPath).GetComponentInChildren<Text>();
                }
                return m_font;
            }
        }
        public RectTransform PromptPage
        {
            get
            {
                if (m_PromptPage == null)
                {
                    m_PromptPage = Keyboard3DPrompt.promptInput;
                }
                return m_PromptPage;
            }
        }

        private int maxChineseWordCount;
        private static int currentChineseWordIndex = 0;
        public virtual void GenerateChinesePages(int page)
        {
            if (chinesePagesDic.ContainsKey(page))
            {
                HideAllPromptPages();
                foreach (int row in chinesePagesDic[page].Keys)
                {
                    foreach (int word in chinesePagesDic[page][row].Keys)
                    {
                        SCKeyboardBaseKey key = promptPagesDic[0][row][word];
                        string text = chinesePagesDic[page][row][word];

                        key.Value = text;
                        key.gameObject.SetActive(true);
                    }
                }

                StartCoroutine(RebuildLayout(PromptPage));
            }
            else
            {
                GenerateChineseKeys();
            }
        }
        private void GenerateChineseKeys()
        {
            HideAllPromptPages();

            maxChineseWordCount = Keyboard3DPrompt.GetChinese(preInputStringBuilder.ToString());

            currentChineseWordIndex = 0;

            GenerateChineseKeys(0, PromptPage, new List<string>());

            StartCoroutine(RebuildLayout(PromptPage));
        }
        private void GenerateChineseKeys(int keyIndex, RectTransform promptRow, List<string> messages)
        {
            if (currentChineseWordIndex > maxChineseWordCount - 1)
            {
                return;
            }

            if (!promptPagesDic.ContainsKey(0)) promptPagesDic[0] = new Dictionary<int, Dictionary<int, SCKeyboardBaseKey>>();
            if (!promptPagesDic[0].ContainsKey(0)) promptPagesDic[0][0] = new Dictionary<int, SCKeyboardBaseKey>();
            if (!chinesePagesDic.ContainsKey(0)) chinesePagesDic[0] = new Dictionary<int, Dictionary<int, string>>();
            if (!chinesePagesDic[0].ContainsKey(0)) chinesePagesDic[0][0] = new Dictionary<int, string>();

            SCKeyboard2DKey promptKey;

            string text = Keyboard3DPrompt.GetChinese(currentChineseWordIndex);

            if (promptPagesDic[0][0].ContainsKey(keyIndex))
            {
                promptKey = promptPagesDic[0][0][keyIndex] as SCKeyboard2DKey;

                promptKey.Value = text;

                messages.Add(promptKey.Value);

                if (!WithinTheLimits(messages, promptRow.GetComponent<HorizontalLayoutGroup>(), promptRow.sizeDelta.x))
                {
                    promptKey.gameObject.SetActive(false);
                    return;
                }

                promptKey.gameObject.SetActive(true);

                chinesePagesDic[0][0][keyIndex] = text;
            }
            else
            {
                promptKey = InstantiateKey(keyPath, promptRow, text);
                messages.Add(promptKey.Value);

                promptPagesDic[0][0][keyIndex] = promptKey;

                if (!WithinTheLimits(messages, promptRow.GetComponent<HorizontalLayoutGroup>(), promptRow.sizeDelta.x))
                {
                    promptKey.gameObject.SetActive(false);
                    return;
                }

                promptKey.gameObject.SetActive(true);
                chinesePagesDic[0][0][keyIndex] = text;
            }

            //Debug.Log("promptKey [ " + promptKey.Value + " ] :" + currentChineseWordIndex);

            currentChineseWordIndex++;

            GenerateChineseKeys(keyIndex + 1, promptRow, messages);
        }
        
        private void HidePromptPage(int page)
        {
            if (promptPagesDic.ContainsKey(page))
            {
                foreach (var rowIndex in promptPagesDic[page].Keys)
                {
                    foreach (var keyIndex in promptPagesDic[page][rowIndex].Keys)
                    {
                        promptPagesDic[page][rowIndex][keyIndex].gameObject.SetActive(false);
                    }
                }
            }
        }
        private void HideAllPromptPages()
        {
            HidePromptPage(0);
        }
        IEnumerator RebuildLayout(RectTransform rect)
        {
            yield return new WaitForEndOfFrame();
            LayoutRebuilder.ForceRebuildLayoutImmediate(rect);
        }

        public override void PinyinKeyHandler(string preInput)
        {
            chinesePagesDic = new Dictionary<int, Dictionary<int, Dictionary<int, string>>>();
            GenerateChinesePages(0);
        }

        private SCKeyboard2DKey InstantiateKey(string path, Transform parent, string value)
        {
            SCKeyboard2DKey prefab = Resources.Load<SCKeyboard2DKey>(path);
            SCKeyboard2DKey promptKey = GameObject.Instantiate(prefab);
            promptKey.transform.SetParent(parent);
            promptKey.transform.localPosition = Vector3.zero;
            promptKey.transform.localRotation = Quaternion.identity;
            promptKey.transform.localScale = Vector3.one;
            promptKey.Value = value;

            return promptKey;
        }
        private bool WithinTheLimits(List<string> messages, HorizontalLayoutGroup group, float LimitWidth)
        {
            float rectWidth = LimitWidth;
            float totalWidth = 0;

            float left = group.padding.left;
            float right = group.padding.right;
            float space = group.spacing;

            float width = 0;

            int wordCount = messages.Count;

            string message = "";
            for (int i = 0; i < messages.Count; i++)
            {
                message += messages[i];
                width += 12;
            }

            width += KeyboardUtils.CaculateTextLength(message, Font) / 10;

            totalWidth = (wordCount - 1) * space + left + width;

            return totalWidth <= rectWidth;
        }
        #endregion

        #region Active Keyboards
        protected override void ActiveKeyboard(KeyboardEnum keyboardEnum,KeyboardState keyboardState = KeyboardState.En)
        {
            base.ActiveKeyboard(keyboardEnum, keyboardState);
            switch (keyboardEnum)
            {
                case KeyboardEnum.ABC:
                    OnActiveABCKeyboard(keyboardState);
                    break;
                case KeyboardEnum.Number:
                    OnActiveNumberKeyboard();
                    break;
                case KeyboardEnum.Prompt:
                    OnActivePromptKeyboard();
                    break;
            }
        }

        public void OnActiveABCKeyboard(KeyboardState keyboardState)
        {
            if (presentKeyboard != null)
                previousKeyboard = presentKeyboard;

            keyboard_abc.SetActive(true);

            HideKeyboardPrompt();

            presentKeyboard = keyboard_abc;

            if (previousKeyboard != null && previousKeyboard != presentKeyboard)
                previousKeyboard.SetActive(false);

            presentKeyboardState = keyboardState;
        }
        
        public void OnActiveNumberKeyboard()
        {
            if (presentKeyboard != null)
                previousKeyboard = presentKeyboard;

            keyboard_number.SetActive(true);

            HideKeyboardPrompt();

            presentKeyboard = keyboard_number;

            if (previousKeyboard != null && previousKeyboard != presentKeyboard)
                previousKeyboard.SetActive(false);
        }        

        public void OnActivePromptKeyboard()
        {
            Keyboard3DPrompt.gameObject.SetActive(true);
        }

        protected override void HideKeyboardPrompt(bool inputPreString = true)
        {
            if (keyboard_prompt.isActiveAndEnabled)
            {
                if (inputPreString) OnNormalKeyClick(preInputStringBuilder.ToString());
                preInputStringBuilder.Clear();
                Keyboard3DPrompt.gameObject.SetActive(false);
            }
        }
        #endregion               
    }
}