using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Events;

[RequireComponent(typeof(NPC))]
public class NpcDialogueManager : DialogueManager
{
    public override void Start()
    {
        //base.Start();

        CanBeOpened = false;
        OpenInDefaultWay = false;

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

            Dialogue.BuildCurrentDialogue();
        }

        // UPDATE DIALOGUE 
        // search in scene manager list
        if (Dialogue != null)
        {
            var machedDialogueData = ((MultiDialogueDataManager)MultiDialogueDataManager.Instance).GetDialogue(Dialogue);

            if (machedDialogueData != null)
            {
                UpdateDialogueState(machedDialogueData);
            }
            else
            {
                UpdateProggres();
            }
        }
    }

    public override void Update()
    {
        if (Input.GetKeyDown(keyToOpen) && OpenPanelCondition())
        {
            // Refrsh dialogue language and proggres after each opening trial 
            if (languageDetector.InstanceTemplates.Count() > 0)
            {
                // create and config dailogue 
                CreateInstanceBasedOnLanguage();

                Dialogue.BuildCurrentDialogue();
            }

            // UPDATE DIALOGUE 
            // search in scene manager list
            if (Dialogue != null)
            {
                var machedDialogueData = ((MultiDialogueDataManager)MultiDialogueDataManager.Instance).GetDialogue(Dialogue);

                if (machedDialogueData != null)
                {
                    UpdateDialogueState(machedDialogueData);
                }
                else
                {
                    UpdateProggres();
                }
            }

            OpenDialoguePanel();
        }
    }

    #region Open Panel API
    public override bool OpenPanelCondition()
    {
        /*
         * Conditions:
         * - npc bust me in Talking mode
         * - player is in collision range
         * - panel assigned to next sentence in dialogue must be closed 
         */
        return gameObject.GetComponent<NPC>().State == NPCStateEnum.Talking && base.OpenPanelCondition();
    }

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

    public override 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(gameObject.GetComponent<NPC>().Name);

        // Bind setted actions
        BindEndActionsToDialogues();

        // update dial state - sync in order to prepare to save
        Dialogue.Dialogues.ForEach(dial => dial.Value.SetActionAfterEachDialogueStep(UpdateProggres));  
    }

    public void UpdateDialogueState(MultiDialogueData dialogueData)
    {
        // 1 Rule - speaker name shouldnt change !!! its our 'key'

        Dialogue.CurrentDialogue = dialogueData.CurrentDialogue;

        foreach(var singleDialogue in dialogueData.DialogueStepModelDataList)
        {
            Dialogue.Dialogues
                .Where(dial => dial.Key == singleDialogue.Key)
                .Select(dial => dial.Value)
                .ToList()
                .ForEach(dial => dial.UpdateDialogueState(singleDialogue.Value) );
        }
    }

    /// <summary>
    /// Function to sync local dialogue state with remote one
    /// Handled in Scene Manager
    /// </summary>
    public void UpdateProggres()
    {
        ((MultiDialogueDataManager)MultiDialogueDataManager.Instance).RegisterDialogue(Dialogue);
    }

    #region Dialogue actions API
    /// <summary>
    /// Function to invoke actions declared in manager on object from scene not onto asset
    /// 
    /// Used in Dialogue ScriptableObject Template
    /// </summary>
    /// <param name="dialogueIndex"></param>
    public void DialogueEndAction(int dialogueIndex)
    {
        GameObject.FindObjectsOfType<NpcDialogueManager>().Where(obj => obj.name == gameObject.name).First().GetComponent<NpcDialogueManager>().InvokeDialogueEndAction(dialogueIndex);
    }

    /// <summary>
    /// Function to invoke actions declared in manager (on current object)
    /// 
    /// Use this to invoke actions which we want to occured after specific Dialogue
    /// </summary>
    /// <param name="dialogueIndex"></param>
    protected void InvokeDialogueEndAction(int dialogueIndex)
    {
        EndactionEventList.Where(el => el.Key.Key == dialogueIndex).ToList().ForEach(el => el.Value.Invoke());
    }

    /// <summary>
    /// Function to invoke actions declared in manager on object from scene not onto asset
    /// 
    /// Used in Dialogue ScriptableObject Template
    /// </summary>
    public void StepEndAction()
    {
        GameObject.FindObjectsOfType<NpcDialogueManager>().Where(obj => obj.name == gameObject.name).First().GetComponent<NpcDialogueManager>().InvokeStepEndAction();
    }

    /// <summary>
    /// Function to invoke actions declared in manager (on current object)
    /// 
    /// Use this to invoke actions which we want to occured after specific Dialogue Step
    /// </summary>
    protected void InvokeStepEndAction()
    {
        var dialogueProgress = Dialogue.DialogueStepStatus();

        EndactionEventList.Where(el => el.Key.Key == dialogueProgress.Item1 & el.Key.Value == dialogueProgress.Item2).ToList().ForEach(el => el.Value.Invoke());
    }
    #endregion
}