#if USING_MRTK3

using System.Collections;
using System.Collections.Generic;
using System;
using UnityEngine;
using UnityEngine.Events;
using Microsoft.MixedReality.Toolkit;
using UnityEngine.XR.Interaction.Toolkit;


namespace SimplifyXR
{
    public enum MRTKEvent
    {
        GazeHover,
        GazePinchHover,
        RayHover,
        GrabHover,
        PokeHover,
        ActiveHover,
        GrabSelect,
        RaySelect,
        GazePinchSelect,
        PokeSelect
    }

    public enum MRTKEventState
    {
        Entered,
        Exited
    }

    [Serializable]
    public class MRTKInteractablePair
    {
        [Required, Tooltip("The interactable object"), SerializeField]
        public MRTKBaseInteractable interactable;

        [Conditional("interactable", null, ComparisonType.NotEqual),
            Tooltip("The interactable event to listen to"), SerializeField]
        public MRTKEvent interactableEvent;

        [Conditional("interactable", null, ComparisonType.NotEqual),
           Tooltip("The interactable event to listen to"), SerializeField]
        public MRTKEventState interactableEventState;
    }

    /// <summary>
    /// Initiates a sequence based on an interactable event trigger
    /// </summary>
    [DirectiveCategory(DirectiveCategories.Initiator, DirectiveSubCategory.Experimental, prettyName = "Interactable Event", directiveLibrary = DirectiveLibrary.MRTK3, directiveInfo = "This initiator starts a sequence when a <b>MRTK Interactable Event</b> is <b>detected.</b>")]

    public class MRTK3InteractableInitiator : EventInitiator
    {
        [Tooltip("Listen to multiple interactions")]
        public bool listenToMultiple;

        [Required, Tooltip("The interactable object"),
            Conditional("listenToMultiple", false, ComparisonType.Equals)]
        public MRTKBaseInteractable interactable;

        [Conditional("interactable", null, ComparisonType.NotEqual),
            Conditional("listenToMultiple", true, ComparisonType.NotEqual),
            Tooltip("The interactable event to listen to")]
        public MRTKEvent interactableEvent;

        [Conditional("interactable", null, ComparisonType.NotEqual),
           Conditional("listenToMultiple", true, ComparisonType.NotEqual),
           Tooltip("The interactable event to listen to")]
        public MRTKEventState interactableEventState;

        [Conditional("listenToMultiple", true, ComparisonType.Equals),
            Tooltip("The interactable event to listen to")]
        public List<MRTKInteractablePair> interactables;

        [HideInInspector]
        public string eventFieldInfoName;


        //Used to store base xr interactable and interactor args on any mrtk event
        private BaseInteractionEventArgs baseInteractionEventArgs = null;

        public override List<KnobKeywords> ReceiveKeywords()
        {
            return new List<KnobKeywords>();
        }

        public override List<KnobKeywords> SendKeywords()
        {
            return new List<KnobKeywords>() { new KnobKeywords("BaseInteractionEvent", typeof(BaseInteractionEventArgs)), new KnobKeywords("TimeStamp", typeof(float)) };
        }

        #region interactable event handlers

        void RegisterEventHandler(MRTKBaseInteractable interactable, MRTKEvent interactableEvent, MRTKEventState interactableEventState, bool register = true)
        {
            if (interactable != null)
            {
                switch (interactableEvent)
                {
                    case MRTKEvent.ActiveHover:
                        RegisterTimedFlagEvent(interactable.IsActiveHovered, interactableEventState, register);
                        RegisterHoverEvent(interactable, interactableEventState, register);
                        break;
                    case MRTKEvent.GazeHover:
                        RegisterTimedFlagEvent(interactable.IsGazeHovered, interactableEventState, register);
                        RegisterHoverEvent(interactable, interactableEventState, register);
                        break;
                    case MRTKEvent.GazePinchHover:
                        RegisterTimedFlagEvent(interactable.IsGazePinchHovered, interactableEventState, register);
                        RegisterHoverEvent(interactable, interactableEventState, register);
                        break;
                    case MRTKEvent.GazePinchSelect:
                        RegisterTimedFlagEvent(interactable.IsGazePinchSelected, interactableEventState, register);
                        RegisterSelectEvent(interactable, interactableEventState, register);
                        break;
                    case MRTKEvent.GrabHover:
                        RegisterTimedFlagEvent(interactable.IsGrabHovered, interactableEventState, register);
                        RegisterHoverEvent(interactable, interactableEventState, register);
                        break;
                    case MRTKEvent.GrabSelect:
                        RegisterTimedFlagEvent(interactable.IsGrabSelected, interactableEventState, register);
                        RegisterSelectEvent(interactable, interactableEventState, register);
                        break;
                    case MRTKEvent.PokeHover:
                        RegisterTimedFlagEvent(interactable.IsPokeHovered, interactableEventState, register);
                        RegisterHoverEvent(interactable, interactableEventState, register);
                        break;
                    case MRTKEvent.PokeSelect:
                        RegisterTimedFlagEvent(interactable.IsPokeSelected, interactableEventState, register);
                        RegisterSelectEvent(interactable, interactableEventState, register);
                        break;
                    case MRTKEvent.RayHover:
                        RegisterTimedFlagEvent(interactable.IsRayHovered, interactableEventState, register);
                        RegisterHoverEvent(interactable, interactableEventState, register);
                        break;
                    case MRTKEvent.RaySelect:
                        RegisterTimedFlagEvent(interactable.IsRaySelected, interactableEventState, register);
                        RegisterSelectEvent(interactable, interactableEventState, register);
                        break;
                }
            }
        }


        void RegisterTimedFlagEvent(TimedFlag timedFlag, MRTKEventState interactableEventState, bool register)
        {
            if (interactableEventState == MRTKEventState.Entered)
            {
                if (register) timedFlag.OnEntered.AddListener(SingleTypeInitiate);
                if (!register) timedFlag.OnEntered.RemoveListener(SingleTypeInitiate);
            }
            else
            {
                if (register) timedFlag.OnExited.AddListener(SingleTypeInitiate);
                if (!register) timedFlag.OnExited.RemoveListener(SingleTypeInitiate);
            }
        }

        void RegisterHoverEvent(MRTKBaseInteractable interactable, MRTKEventState interactableEventState, bool register)
        {
            if (interactableEventState == MRTKEventState.Entered)
            {
                if (register) interactable.hoverEntered.AddListener(SetBaseInteractionArgs);
                if (!register) interactable.hoverEntered.RemoveListener(SetBaseInteractionArgs);
            }
            else
            {
                if (register) interactable.hoverExited.AddListener(SetBaseInteractionArgs);
                if (!register) interactable.hoverExited.RemoveListener(SetBaseInteractionArgs);
            }
        }

        void RegisterSelectEvent(MRTKBaseInteractable interactable, MRTKEventState interactableEventState, bool register)
        {
            if (interactableEventState == MRTKEventState.Entered)
            {
                if (register) interactable.selectEntered.AddListener(SetBaseInteractionArgs);
                if (!register) interactable.selectEntered.RemoveListener(SetBaseInteractionArgs);
            }
            else
            {
                if (register) interactable.selectExited.AddListener(SetBaseInteractionArgs);
                if (!register) interactable.selectExited.RemoveListener(SetBaseInteractionArgs);
            }
        }

        #endregion

        protected new void Awake()
        {
            base.Awake();
            if (enabled) RegisterEvents(true);
        }

        protected void RegisterEvents(bool register)
        {
            if (!listenToMultiple)
            {
                RegisterEventHandler(interactable, interactableEvent, interactableEventState, register);
            }
            else
            {
                foreach (MRTKInteractablePair interactablePair in interactables)
                {
                    RegisterEventHandler(interactablePair.interactable, interactablePair.interactableEvent, interactablePair.interactableEventState, register);
                }
            }
        }

        protected new void OnDestroy()
        {
            base.OnDestroy();
            RegisterEvents(false);
        }
        public void SingleTypeInitiate(float timeStamp) //single float argument, representing the timestamp the event (entered, exited) occurred.
        {
            SendData(timeStamp);
            base.Initiate();
        }

        public void SetBaseInteractionArgs(BaseInteractionEventArgs args)
        {
            baseInteractionEventArgs = args;
        }

        void SendData(float timeStamp)
        {
            if (baseInteractionEventArgs != null)
            {
                AddPassableData(new List<string> { "TimeStamp", "BaseInteractionEvent" }, new List<object> { timeStamp, baseInteractionEventArgs });
            }
        }
    }
}
#endif