using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Events;

[RequireComponent(typeof(NPC))]
public class DialogueManager : MonoBehaviour
{
    [SerializeField]
    public string SpeakerName;

    [SerializeField]
    public LanguageDetector<MultiDialogue> languageDetector;

    /* We user object CLONED TO PREVENT overwritting changes in main object inassets */
    [SerializeField]
    public MultiDialogue Dialogue;

    // List<Key<dialogue No, dialogue step No>, Value : UnityEvent>
    public List<IndexValuePair<IndexValuePair<int, int>, UnityEvent>> EndactionEventList;

    [SerializeField] public KeyCode keyToOpen = KeyCode.E;

    public bool CanBeOpened = true;

    /// <summary>
    /// This flag tell that manager will open dialogue automatically after
    /// - beeing in collision range
    /// - when dialogue is closed
    /// - when Player press KeyToOpen button
    /// 
    /// Change var status in other sctipt if you want to use other triggers to open dialogue
    /// </summary>
    public bool OpenInDefaultWay = true;

    public virtual void Start() 
    {
        if (languageDetector.InstanceTemplates.Count() > 0)
        {
            // create and config dailogue 
            CreateInstanceBasedOnLanguage();

            Dialogue.BuildCurrentDialogue();
        }
    }


    public virtual void Update()
    {
        if (OpenInDefaultWay && Input.GetKeyDown(keyToOpen) && OpenPanelCondition())
        {
            if (languageDetector.InstanceTemplates.Count() > 0)
            {
                // create and config dailogue 
                CreateInstanceBasedOnLanguage();

                Dialogue.BuildCurrentDialogue();
            }

            OpenDialoguePanel();
        }
    }


    public virtual void OnTriggerEnter2D(Collider2D collision)
    {
        // don't listen when component is disabled
        if (ComponentEnabledCondition())
            return;

        if (collision.gameObject.tag == "Player")
        {
            CanBeOpened = true;
        }
    }

    public virtual void OnTriggerExit2D(Collider2D collision)
    {
        // don't listen when component is disabled
        if (ComponentEnabledCondition())
            return;

        if (collision.gameObject.tag == "Player")
        {
            CanBeOpened = false;

            // Shop dont have dialoge in this script but in its own one
            // TODO change - pass eachd ialogue to this script and use conditions from child class
            if (Dialogue != null && GetCurrentDialoguePanelStatus())
            {
                Dialogue.BreakDialogueStep();
            }
        }
    }

    public virtual void CreateInstanceBasedOnLanguage()
    {
        // DONT CLONE TO HAVE CONSTANT DATA - init - deep copy - to prevent overwritting chil;dern :D
        Dialogue = languageDetector.DetectInstanceBasedOnLanguage();

        Dialogue.ResetDialogue();
/*        var cloneDialogues = new List<IndexValuePair<int, Dialogue>>(Dialogue.Dialogues);

        Dialogue.Dialogues.Clear();
        foreach (var dial in cloneDialogues)
        {
            Dialogue.Dialogues.Add(new IndexValuePair<int, Dialogue>(dial.Key, Instantiate(dial.Value)));
        }*/


        Dialogue.SetSpeakerName(PlayerPrefs.GetString("name"));

        // Bind setted actions
        BindEndActionsToDialogues();

        // Add one more action - to reset proggress
        Dialogue.Dialogues.ForEach(dial => dial.Value.SetActionAfterEachDialogueStep(dial.Value.ResetDialogue));  // reset dial 
    }

    #region Open Panel API
    public void OpenDialoguePanel()
    {
        if(OpenPanelCondition())
        {
            gameObject.GetComponent<DialogueManager>().Dialogue.StartDialogue();
        }
    }
    public virtual bool OpenPanelCondition()
    {
        /*
         * Conditions:
         * - player is in collision range
         * - panel assigned to next sentence in dialogue must be closed 
         */
        return IsInRangeCondition() && !GetCurrentDialoguePanelStatus();
    }

    public bool IsInRangeCondition()
    {
        return CanBeOpened;
    }

    protected virtual bool ComponentEnabledCondition()
    {
        return !gameObject.GetComponent<DialogueManager>().enabled;
    }
    #endregion

    /// <summary>
    /// Opened = 1
    /// Closed = 0
    /// </summary>
    /// <returns></returns>
    public virtual bool GetCurrentDialoguePanelStatus()
    {
        var multiDialStatius = Dialogue.DialogueStepStatus();

        // parse result (dialoge no, step no)
        // dialogue not exist - finished 
        // dialoge exists & step no > steps sum - finished
        // dialoge no exists && step no exists - not finished - check panel status

        if(!Dialogue.Dialogues.Where(dial => dial.Key == multiDialStatius.Item1).Any())
            return false;

        var fixedDialogue = Dialogue.Dialogues.Where(dial => dial.Key == multiDialStatius.Item1).First().Value;

        if (fixedDialogue.CurrentStep >= fixedDialogue.DialogueSteps.Count())
            return false;

        // CONTINUE dont increment counter - only mark checkboxes
        if (fixedDialogue
            .DialogueSteps
            .Where(step => step.WasDisplayed == false)
            .ToArray()
            .Count() == 0)
            return false;

        // when we are here we can be sure given points are correct
        return Dialogue
            .Dialogues
            .Where(dialogue => dialogue.Key == multiDialStatius.Item1)
            .First()
            .Value
            .DialogueSteps
            .ElementAtOrDefault(multiDialStatius.Item2)
            .DialogueController.CurrentPanel != null;
    }

    public void BindEndActionsToDialogues()
    {
        foreach(var finalAction in EndactionEventList)
        {
            // finalAction.Key.Key | .Value/
            Dialogue
                 .Dialogues
                 .Where(dial => dial.Key == finalAction.Key.Key)
                 .FirstOrDefault()
                 .Value
                 .DialogueSteps
                 .ElementAtOrDefault(finalAction.Key.Value)
                 .SetActionAfterDialogueStep(finalAction.Value)
            ;
        }
    }
}