﻿#if USING_MRTK

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using System.Reflection;
using System.Linq;
using System;
using Microsoft.MixedReality.Toolkit.Utilities.Solvers;
using Microsoft.MixedReality.Toolkit.UI;

namespace SimplifyXR
{
    public enum InteractableEvent
    {
        OnClick,
        OnTouch,
        OnTouchEnd,
        OnGrab,
        OnRelease,
        OnPress,
        OnPressRelease,
        OnHold,
        OnSelect,
        OnDeselect,
        OnFocusOn,
        OnFocusOff
    }

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

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


    /// <summary>
    /// Initiates a sequence based on an interactable event trigger
    /// </summary>
    [DirectiveCategory(DirectiveCategories.Initiator, DirectiveSubCategory.UserInteraction, DirectiveLibrary.MRTK, 
        "Interactable Event",
        directiveInfo ="This initiator starts a sequence when a <b>MRTK Interactable Event</b> is <b>detected.</b>")]
    public class MRTKInteractableInitiator : EventInitiator
    {

        [Tooltip("Listen to multiple interactions")]
        public bool listenToMultiple;

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

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

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

        [HideInInspector]
        public string eventFieldInfoName;

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

        public override List<KnobKeywords> SendKeywords()
        {
            if (listenToMultiple)
            {
                return new List<KnobKeywords>(){
                new KnobKeywords("Interactables", typeof(List<UnityEngine.Object>)),
                 };
            }
            else
            {
                return new List<KnobKeywords>(){
                new KnobKeywords("Interactable", typeof(Interactable)),
                };
            }           
        }

        #region interactable event handlers

        void RegisterEventHandler(
            Interactable interactable,
            InteractableEvent interactableEvent,
            bool register = true)
        {

            if(interactable != null)
            {
                switch (interactableEvent)
                {
                    case InteractableEvent.OnClick:
                        if (register) interactable.OnClick.AddListener(SingleTypeInitiate);
                        if (!register) interactable.OnClick.RemoveListener(SingleTypeInitiate);
                        break;
                    case InteractableEvent.OnGrab:
                        if (interactable.GetReceiver<InteractableOnGrabReceiver>() == null) interactable.AddReceiver<InteractableOnGrabReceiver>();
                        if (register) interactable.GetReceiver<InteractableOnGrabReceiver>().OnGrab.AddListener(SingleTypeInitiate);
                        if (!register) interactable.GetReceiver<InteractableOnGrabReceiver>().OnGrab.RemoveListener(SingleTypeInitiate);
                        break;
                    case InteractableEvent.OnRelease:
                        if (interactable.GetReceiver<InteractableOnGrabReceiver>() == null) interactable.AddReceiver<InteractableOnGrabReceiver>();
                        if (register) interactable.GetReceiver<InteractableOnGrabReceiver>().OnRelease.AddListener(SingleTypeInitiate);
                        if (!register) interactable.GetReceiver<InteractableOnGrabReceiver>().OnRelease.RemoveListener(SingleTypeInitiate);
                        break;
                    case InteractableEvent.OnTouch:
                        if (interactable.GetReceiver<InteractableOnTouchReceiver>() == null) interactable.AddReceiver<InteractableOnTouchReceiver>();
                        if (register) interactable.GetReceiver<InteractableOnTouchReceiver>().OnTouchStart.AddListener(SingleTypeInitiate);
                        if (!register) interactable.GetReceiver<InteractableOnTouchReceiver>().OnTouchStart.RemoveListener(SingleTypeInitiate);
                        break;
                    case InteractableEvent.OnTouchEnd:
                        if (interactable.GetReceiver<InteractableOnTouchReceiver>() == null) interactable.AddReceiver<InteractableOnTouchReceiver>();
                        if (register) interactable.GetReceiver<InteractableOnTouchReceiver>().OnTouchEnd.AddListener(SingleTypeInitiate);
                        if (!register) interactable.GetReceiver<InteractableOnTouchReceiver>().OnTouchEnd.RemoveListener(SingleTypeInitiate);
                        break;
                    case InteractableEvent.OnPress:
                        if (interactable.GetReceiver<InteractableOnPressReceiver>() == null) interactable.AddReceiver<InteractableOnPressReceiver>();
                        if (register) interactable.GetReceiver<InteractableOnPressReceiver>().OnPress.AddListener(SingleTypeInitiate);
                        if (!register) interactable.GetReceiver<InteractableOnPressReceiver>().OnPress.RemoveListener(SingleTypeInitiate);
                        break;
                    case InteractableEvent.OnPressRelease:
                        if (interactable.GetReceiver<InteractableOnPressReceiver>() == null) interactable.AddReceiver<InteractableOnPressReceiver>();
                        if (register) interactable.GetReceiver<InteractableOnPressReceiver>().OnRelease.AddListener(SingleTypeInitiate);
                        if (!register) interactable.GetReceiver<InteractableOnPressReceiver>().OnRelease.RemoveListener(SingleTypeInitiate);
                        break;
                    case InteractableEvent.OnHold:
                        if (interactable.GetReceiver<InteractableOnHoldReceiver>() == null) interactable.AddReceiver<InteractableOnHoldReceiver>();
                        if (register) interactable.GetReceiver<InteractableOnHoldReceiver>().OnHold.AddListener(SingleTypeInitiate);
                        if (!register) interactable.GetReceiver<InteractableOnHoldReceiver>().OnHold.RemoveListener(SingleTypeInitiate);
                        break;
                    case InteractableEvent.OnSelect:
                        if (interactable.GetReceiver<InteractableOnToggleReceiver>() == null) interactable.AddReceiver<InteractableOnToggleReceiver>();
                        if (register) interactable.GetReceiver<InteractableOnToggleReceiver>().OnSelect.AddListener(SingleTypeInitiate);
                        if (!register) interactable.GetReceiver<InteractableOnToggleReceiver>().OnSelect.RemoveListener(SingleTypeInitiate);
                        break;
                    case InteractableEvent.OnDeselect:
                        if (interactable.GetReceiver<InteractableOnToggleReceiver>() == null) interactable.AddReceiver<InteractableOnToggleReceiver>();
                        if (register) interactable.GetReceiver<InteractableOnToggleReceiver>().OnDeselect.AddListener(SingleTypeInitiate);
                        if (!register) interactable.GetReceiver<InteractableOnToggleReceiver>().OnDeselect.RemoveListener(SingleTypeInitiate);
                        break;
                    case InteractableEvent.OnFocusOn:
                        if (interactable.GetReceiver<InteractableOnFocusReceiver>() == null) interactable.AddReceiver<InteractableOnFocusReceiver>();
                        if (register) interactable.GetReceiver<InteractableOnFocusReceiver>().OnFocusOn.AddListener(SingleTypeInitiate);
                        if (!register) interactable.GetReceiver<InteractableOnFocusReceiver>().OnFocusOn.RemoveListener(SingleTypeInitiate);
                        break;
                    case InteractableEvent.OnFocusOff:
                        if (interactable.GetReceiver<InteractableOnFocusReceiver>() == null) interactable.AddReceiver<InteractableOnFocusReceiver>();
                        if (register) interactable.GetReceiver<InteractableOnFocusReceiver>().OnFocusOff.AddListener(SingleTypeInitiate);
                        if (!register) interactable.GetReceiver<InteractableOnFocusReceiver>().OnFocusOff.RemoveListener(SingleTypeInitiate);
                        break;
                }
            }           
        }
        #endregion

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

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

        protected new void OnDestroy()
        {
            base.OnDestroy();
            RegisterEvents(false);
        }
        public void SingleTypeInitiate()
        {
            SendData();
            base.Initiate();
        }
        void SendData()
        {
            var thisData = !listenToMultiple ?  
                new List<object> { interactable } : new List<object> { interactables };
            var thisKeywords = !listenToMultiple ?
                new List<string> { "interactable" } : new List<string> { "interactables" };
            AddPassableData(thisKeywords, thisData);
        }


    }

}

#endif
