using System;
using System.Collections.Generic;
using UnityEngine.Events;
using UnityEngine.XR.Interaction.Toolkit;
namespace UnityEngine.XR.Content.Walkthrough
{
    /// 
    /// Contains information needed to process one step of a walkthrough.
    /// 
    public class WalkthroughStep : MonoBehaviour
    {
        /// Local method use only -- created here to reduce garbage collection. Collections must be cleared before use
        static readonly List s_TriggersToRemove = new List();
        [SerializeField]
        [Tooltip("Camera target to reposition user.")]
        GameObject m_CameraTarget;
        [SerializeField]
        [Tooltip("The Teleportation Provider used to reposition the user. Usually a component on the XR Origin.")]
        TeleportationProvider m_TeleportationProvider;
        [SerializeField]
        [Tooltip("Optional audio source for voiceover")]
        AudioSource m_AudioSource;
        [SerializeField]
        [Tooltip("Objects to enable when this step is active.")]
        List m_Visuals = new List();
#pragma warning disable 649
        [SerializeField]
        [Tooltip("Actions to call when the step starts.")]
        UnityEvent m_OnStepBegin;
        [SerializeField]
        [Tooltip("Actions to call when the step completes.")]
        UnityEvent m_OnStepComplete;
        [SerializeField]
        [Tooltip("The purpose of this step.")]
        string m_Description;
#pragma warning restore 649
        [SerializeField]
        [Tooltip("If true, this step cannot be skipped until completed at least once.")]
        bool m_BlockUntilComplete;
        [SerializeField]
        [Tooltip("If true, this step will automatically progress when complete - unless explicitly skipped to.")]
        bool m_AutoProgressOnComplete = true;
        bool m_Started;
        bool m_AutoProgressEnabled = true;
        bool m_StepInvoked;
        Action m_OnComplete;
        GameObject m_Waypoint;
        GameObject m_Link;
        List m_Triggers = new List();
        List m_RemainingTriggers = new List();
        /// 
        /// The purpose of this step. Appends a (Complete) if complete and normally has triggers.
        /// 
        public string description => $"{m_Description}{(completed && m_Triggers.Count > 0 ? " (Complete)" : "") }";
        public GameObject waypoint
        {
            get => m_Waypoint;
            set => m_Waypoint = value;
        }
        public GameObject link
        {
            get => m_Link;
            set => m_Link = value;
        }
        /// 
        /// Ensures the step visuals are hidden until active and that all triggers are accounted for
        /// 
        public void Initialize()
        {
            if (!m_Started)
                SetVisualsState(false);
            GetComponents(m_Triggers);
        }
        /// 
        /// Returns true if this step does not currently have any triggers remaining to fire
        /// 
        public bool canProgress => (!m_BlockUntilComplete || (m_RemainingTriggers.Count == 0));
        /// 
        /// Returns true if this step does not block, or has been completed at least once.
        /// 
        public bool canSkip => (!m_BlockUntilComplete || completed);
        /// 
        /// True if this step's triggers have been activated at least once
        /// 
        public bool completed { get; private set; }
        /// 
        /// Makes this step and its triggers the active focus of a walkthrough
        /// 
        /// Callback to fire when this step's triggers are complete
        /// If this step is allow to auto-progress during this activation
        public void StartStep(Action onComplete, bool allowAutoProgress = true)
        {
            if (m_Started)
                return;
            // Autoprogression is enabled only if the step AND walkthrough allow it
            m_AutoProgressEnabled = allowAutoProgress && m_AutoProgressOnComplete;
            SetVisualsState(true);
            SetAudioSource(true);
            if (m_Waypoint != null)
            {
                m_Waypoint.SetActive(false);
            }
            if (m_CameraTarget != null && m_TeleportationProvider != null)
            {
                SetCameraPosition();
            }
            m_OnComplete = onComplete;
            m_Started = true;
            if (m_Triggers.Count == 0 && m_AutoProgressEnabled)
            {
                CompleteStep();
                return;
            }
            foreach (var currentTrigger in m_Triggers)
            {
                if (currentTrigger.ResetTrigger())
                    m_RemainingTriggers.Add(currentTrigger);
            }
            if (m_RemainingTriggers.Count == 0)
            {
                CompleteStep();
                return;
            }
            if (m_RemainingTriggers.Count > 0)
                m_AutoProgressEnabled = m_AutoProgressOnComplete;
        }
        /// 
        /// Ends this step being the focus of the current walkthrough
        /// 
        public void CancelStep()
        {
            SetVisualsState(false);
            SetAudioSource(false);
            if (m_Waypoint != null)
            {
                m_Waypoint.SetActive(true);
            }
            if (!m_Started)
                return;
            m_OnComplete = null;
            m_Started = false;
            m_RemainingTriggers.Clear();
        }
        void CompleteStep()
        {
            if (!m_Started)
                return;
            completed = true;
            m_OnComplete?.Invoke(m_AutoProgressEnabled);
            m_OnComplete = null;
            m_Started = false;
            // We disable visuals if the the next step is being activated
            if (m_AutoProgressEnabled)
            {
                SetVisualsState(false);
                SetAudioSource(false);
                if (m_Waypoint != null)
                {
                    m_Waypoint.SetActive(true);
                }
            }
            m_RemainingTriggers.Clear();
        }
        void Update()
        {
            // If this step is running, check remaining triggers.  Any triggers that are now met get removed.
            // If there are no triggers left, then the step is complete.
            if (!m_Started)
                return;
            if (m_RemainingTriggers.Count == 0)
                return;
            s_TriggersToRemove.Clear();
            foreach (var currentTrigger in m_RemainingTriggers)
            {
                if (currentTrigger.Check())
                    s_TriggersToRemove.Add(currentTrigger);
            }
            foreach (var toRemove in s_TriggersToRemove)
            {
                m_RemainingTriggers.Remove(toRemove);
            }
            s_TriggersToRemove.Clear();
            if (m_RemainingTriggers.Count == 0)
            {
                CompleteStep();
                return;
            }
        }
        void SetVisualsState(bool enabled)
        {
            if (m_Visuals != null)
            {
                foreach (var currentVisual in m_Visuals)
                {
                    if (currentVisual != null)
                        currentVisual.SetActive(enabled);
                }
            }
            if (m_StepInvoked == enabled)
                return;
            m_StepInvoked = enabled;
            if (enabled && m_OnStepBegin != null)
                m_OnStepBegin.Invoke();
            if (!enabled && m_OnStepComplete != null)
                m_OnStepComplete.Invoke();
        }
        void SetAudioSource(bool enabled)
        {
            if (m_AudioSource != null)
            {
                if (enabled)
                {
                    m_AudioSource.Play();
                }
                else
                {
                    m_AudioSource.Stop();
                }
            }
        }
        void SetCameraPosition()
        {
            TeleportRequest request = new TeleportRequest()
            {
                requestTime = Time.time,
                matchOrientation = MatchOrientation.TargetUpAndForward,
                destinationPosition = m_CameraTarget.transform.position,
                destinationRotation = m_CameraTarget.transform.rotation
            };
            m_TeleportationProvider.QueueTeleportRequest(request);
        }
    }
}