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

[Serializable]
[CreateAssetMenu(fileName = "New Dialogue", menuName = "Dialogue/New Dialogue")]
public class Dialogue : ScriptableObject, IDialogue
{
    [SerializeField]
    public string SpeakerName;

    [SerializeField]
    public int CurrentStep = 0;

    [SerializeField]
    public List<DialogueStepModel> DialogueSteps;

    public void SetSpeakerName(string speakerName)
    {
        SpeakerName = speakerName;

        DialogueSteps.ForEach(step => step.Header = speakerName);
    }

    #region finish action api
    public void SetActionAfterEachDialogueStep(UnityEvent _endOfDialogueStepAction)
    {
        foreach(var dialogueStep in DialogueSteps)
        {
            dialogueStep.SetActionAfterDialogueStep(_endOfDialogueStepAction);
        }
    }

    public void SetActionAfterEachDialogueStep(Action _finishDialogueAction)
    {
        foreach (var dialogueStep in DialogueSteps)
        {
            dialogueStep.SetActionAfterDialogueStep(_finishDialogueAction);
        }
    }

    public void SetActionAfterDialogueStep(int dialogueStepNo, UnityEvent _endOfDialogueStepAction)
    {
        if(DialogueSteps.Count >= dialogueStepNo)
            DialogueSteps[dialogueStepNo].SetActionAfterDialogueStep(_endOfDialogueStepAction);
    }
    public void SetActionAfterDialogueStep(int dialogueStepNo, Action _finishDialogueAction)
    {
/*        Debug.Log(dialogueStepNo); */        
        if (DialogueSteps.Count >= dialogueStepNo)
            DialogueSteps[dialogueStepNo].SetActionAfterDialogueStep(_finishDialogueAction);
    }
    #endregion

    #region dialogue displaying api
    public void StartDialogue()
    {
        // 1. Build
        BuildDialogue(DialogueSteps);

        // 2. Show first step
        ShowDialogueStepPanel();
    }

    public void BreakDialogueStep()
    {
        foreach (var DialogueStep in DialogueSteps)
        {
            if (!DialogueStep.WasDisplayed && DialogueStep.DialogueController.CurrentPanel != null)
            {
                DialogueStep.DialogueController.CloseCurrentPanel(); // close panel 

                break;
            }
        }
    }

    /// <summary>
    /// Function to 
    /// - close panel
    /// - INVOKE FINAL DIALOGUE ACTION
    /// - mark as dispalyed
    /// - increasing step number
    /// 
    /// Dont invoke final action!!!
    /// </summary>
    public void FinishDialogue()
    {
        foreach (var DialogueStep in DialogueSteps)
        {
            if (!DialogueStep.WasDisplayed)
            {
                // status must be changes before invoked final action - example - updating state
                DialogueStep.WasDisplayed = true;

                CurrentStep++;

                DialogueStep.DialogueController.FinishCurrentDialogue(DialogueStep.DialogueController);

                break;
            }
        }
    }

    /// <summary>
    /// Function to 
    /// - close panel
    /// - mark as dispalyed
    /// - increasing step number
    /// 
    /// Dont invoke final action!!!
    /// </summary>
    public void CloseDialogue()
    {
        foreach (var DialogueStep in DialogueSteps)
        {
            if (!DialogueStep.WasDisplayed)
            {
                DialogueStep.WasDisplayed = true;

                CurrentStep++;

                DialogueStep.DialogueController.CloseCurrentPanel(DialogueStep.DialogueController);

                break;
            }
        }
    }



    /// <summary>
    /// Dialogue API
    /// 
    /// MAIN bunction to begin dialogue 
    /// Create new panel instance on scene by force with sentence from queue
    /// 
    /// The best way is to invoked its after player reaction from other script (by event - collision) :)
    /// </summary>
    public void ShowDialogueStepPanel()
    {
        foreach (var DialogueStep in DialogueSteps)
        {
            if (!DialogueStep.WasDisplayed && DialogueStep.DialogueController.listOfDialogue.Count != 0)
            {
                DialogueStep.DialogueController.Show(DialogueStep.DialogueController.listOfDialogue.Dequeue());  // create panel 

                break;
            }
        }
    }

    /// <summary>
    /// Show next sentence ONLY in the same step
    /// 
    /// Return next panel status ponly for first faounded not displayed dialogue step
    /// 
    /// TODO change name this function and one below - but this break buttons with this action assigned
    /// 
    /// It would be much safer if function were return int not void bu unity event dont allow to bind methods in that scenario
    /// </summary>
    /// <returns></returns>
    public void ShowNextPanel()
    {
        // we dont use foreach and "was displayed" condition because ther is DATA LEAK
        // MISLEADING - last sentence mark step ad "wasDisplayed" but not increase "CurrentPanel" index 

        var currentStep = GetCurrentStep();

        var nextSentenceIndex = currentStep.DialogueController.ShowNextPanel(currentStep.DialogueController);

        if (!nextSentenceIndex)
        {
            CurrentStep++;
            Debug.Log($"Increase - {CurrentStep}");
        }

        //return nextSentenceIndex;
    }

    /// <summary>
    /// Dialogue API
    /// 
    /// Function to automaticly go to next dialgue sentence (in this or next step eg after pressing button on panel
    /// It provide that dialogue not braking after finishing current step
    /// 
    /// The dialogue follows the order of the lists ! !
    /// 
    /// It's default function to get next sentence if dialogue is currently started
    /// It is responsible for detecting 
    ///   1) if there is any dialogue panel already created before displaying next sentence
    ///   2) if its last panel sentence and invoiking 'end dialogue' action
    ///   
    /// Whats more, if there was no more sentences in 'step', althroughr closing panel go to next step automatiiccally 
    /// </summary>
    public void GoToNextSentence()
    {
        // wskazujemy na inny krok kolejno niz nieoznaczony
        // MISLEADING - last sentence mark step ad "wasDisplayed" but not increase "CurrentPanel" index 

        foreach (var DialogueStep in DialogueSteps)
        {
            if (!DialogueStep.WasDisplayed)
            {
                var nextSentence = DialogueStep.DialogueController.ShowNextPanel(DialogueStep.DialogueController);

                if (!nextSentence)
                {
                    DialogueStep.WasDisplayed = true;

                    CurrentStep++;

                    ShowDialogueStepPanel();
                }

                break;
            }
        }
    }



    #endregion

    public void InvokeFinalAction()
    {
        foreach (var DialogueStep in DialogueSteps)
        {
            if (!DialogueStep.WasDisplayed)
            {
                DialogueStep.DialogueController.FinishDialoguStep();

                break;
            }
        }
    }

    public void BuildDialogue()
    {
        BuildDialogue(DialogueSteps);
    }

    /// <summary>
    /// Function to build each step of dialogue
    /// </summary>
    public void BuildDialogue(List<DialogueStepModel> Dialogue)
    {
        foreach (var dialogueStep in Dialogue)
        {
            dialogueStep.Header = SpeakerName;

            dialogueStep.Build();

            // synchronize current step counter
            // cause ERROR - multi counting ....
        }
    }

    #region Dialogue Status API
    /// <summary>
    /// Function to reset rememebered dialogue status
    /// 
    /// (Scriptable objects clones are overwritten during clone modification)
    /// </summary>
    public void ResetDialogue()
    {
        CurrentStep = 0;

        DialogueSteps.ForEach(step => step.WasDisplayed = false);
    }

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


        CurrentStep = dialogueData.CurrentStep;

        for(int i=0; i < dialogueData.DialogueStepModelDataList.Count; i++)
        {
            DialogueSteps[i].WasDisplayed = dialogueData.DialogueStepModelDataList[i].WasDisplayed;
        }
    }
    #endregion

    public DialogueStepModel? GetCurrentStep()
    {// TODO - argument out of colection :/

        var currentStep = DialogueSteps.ElementAtOrDefault(CurrentStep);

        if (currentStep != null && currentStep.WasDisplayed && CurrentStep < DialogueSteps.Count)
            CurrentStep++;

        return DialogueSteps.Count > CurrentStep ? DialogueSteps[CurrentStep] : null;
    }



/*    public DialogueStepModel GetCurrentStep()
    {

        foreach (var DialogueStep in DialogueSteps)
        {
            Debug.Log("-" + DialogueStep.DialogueController.listOfDialogue.Count);

            if (!DialogueStep.WasDisplayed && DialogueStep.DialogueController.listOfDialogue.Count != 0)
            {
                return DialogueStep;
            }
        }

        return null;
    }*/
}