using System;
using System.Diagnostics;
using UnityEngine.InputSystem.LowLevel;
#if UNITY_EDITOR
using UnityEditor;
#endif
////REVIEW: this *really* should be renamed to TouchPolling or something like that
////REVIEW: Should this auto-enable itself when the API is used? Problem with this is that it means the first touch inputs will get missed
////        as by the time the API is polled, we're already into the first frame.
////TODO: gesture support
////TODO: high-frequency touch support
////REVIEW: have TouchTap, TouchSwipe, etc. wrapper MonoBehaviours like LeanTouch?
////TODO: as soon as we can break the API, remove the EnhancedTouchSupport class altogether and rename UnityEngine.InputSystem.EnhancedTouch to TouchPolling
////FIXME: does not survive domain reloads
namespace UnityEngine.InputSystem.EnhancedTouch
{
    /// 
    /// API to control enhanced touch facilities like  that are not
    /// enabled by default.
    /// 
    /// 
    /// Enhanced touch support provides automatic finger tracking and touch history recording.
    /// It is an API designed for polling, i.e. for querying touch state directly in methods
    /// such as MonoBehaviour.Update. Enhanced touch support cannot be used in combination
    /// with s though both can be used side-by-side.
    ///
    /// 
    /// 
    /// public class MyBehavior : MonoBehaviour
    /// {
    ///     protected void OnEnable()
    ///     {
    ///         EnhancedTouchSupport.Enable();
    ///     }
    ///
    ///     protected void OnDisable()
    ///     {
    ///         EnhancedTouchSupport.Disable();
    ///     }
    ///
    ///     protected void Update()
    ///     {
    ///         var activeTouches = Touch.activeTouches;
    ///         for (var i = 0; i < activeTouches.Count; ++i)
    ///             Debug.Log("Active touch: " + activeTouches[i]);
    ///     }
    /// }
    /// 
    /// 
    /// 
    /// 
    /// 
    public static class EnhancedTouchSupport
    {
        /// 
        /// Whether enhanced touch support is currently enabled.
        /// 
        /// True if EnhancedTouch support has been enabled.
        public static bool enabled => s_Enabled > 0;
        private static int s_Enabled;
        private static InputSettings.UpdateMode s_UpdateMode;
        /// 
        /// Enable enhanced touch support.
        /// 
        /// 
        /// Calling this method is necessary to enable the functionality provided
        /// by  and . These APIs add extra
        /// processing to touches and are thus disabled by default.
        ///
        /// Calls to Enable and  balance each other out.
        /// If Enable is called repeatedly, it will take as many calls to
        ///  to disable the system again.
        /// 
        public static void Enable()
        {
            ++s_Enabled;
            if (s_Enabled > 1)
                return;
            InputSystem.onDeviceChange += OnDeviceChange;
            InputSystem.onBeforeUpdate += Touch.BeginUpdate;
            InputSystem.onSettingsChange += OnSettingsChange;
            #if UNITY_EDITOR
            AssemblyReloadEvents.beforeAssemblyReload += OnBeforeDomainReload;
            #endif
            SetUpState();
        }
        /// 
        /// Disable enhanced touch support.
        /// 
        /// 
        /// This method only undoes a single call to .
        /// 
        public static void Disable()
        {
            if (!enabled)
                return;
            --s_Enabled;
            if (s_Enabled > 0)
                return;
            InputSystem.onDeviceChange -= OnDeviceChange;
            InputSystem.onBeforeUpdate -= Touch.BeginUpdate;
            InputSystem.onSettingsChange -= OnSettingsChange;
            #if UNITY_EDITOR
            AssemblyReloadEvents.beforeAssemblyReload -= OnBeforeDomainReload;
            #endif
            TearDownState();
        }
        internal static void Reset()
        {
            Touch.s_GlobalState.touchscreens = default;
            Touch.s_GlobalState.playerState.Destroy();
            Touch.s_GlobalState.playerState = default;
            #if UNITY_EDITOR
            Touch.s_GlobalState.editorState.Destroy();
            Touch.s_GlobalState.editorState = default;
            #endif
            s_Enabled = 0;
        }
        private static void SetUpState()
        {
            Touch.s_GlobalState.playerState.updateMask = InputUpdateType.Dynamic | InputUpdateType.Manual | InputUpdateType.Fixed;
            #if UNITY_EDITOR
            Touch.s_GlobalState.editorState.updateMask = InputUpdateType.Editor;
            #endif
            s_UpdateMode = InputSystem.settings.updateMode;
            foreach (var device in InputSystem.devices)
                OnDeviceChange(device, InputDeviceChange.Added);
        }
        internal static void TearDownState()
        {
            foreach (var device in InputSystem.devices)
                OnDeviceChange(device, InputDeviceChange.Removed);
            Touch.s_GlobalState.playerState.Destroy();
            #if UNITY_EDITOR
            Touch.s_GlobalState.editorState.Destroy();
            #endif
            Touch.s_GlobalState.playerState = default;
            #if UNITY_EDITOR
            Touch.s_GlobalState.editorState = default;
            #endif
        }
        private static void OnDeviceChange(InputDevice device, InputDeviceChange change)
        {
            switch (change)
            {
                case InputDeviceChange.Added:
                {
                    if (device is Touchscreen touchscreen)
                        Touch.AddTouchscreen(touchscreen);
                    break;
                }
                case InputDeviceChange.Removed:
                {
                    if (device is Touchscreen touchscreen)
                        Touch.RemoveTouchscreen(touchscreen);
                    break;
                }
            }
        }
        private static void OnSettingsChange()
        {
            var currentUpdateMode = InputSystem.settings.updateMode;
            if (s_UpdateMode == currentUpdateMode)
                return;
            TearDownState();
            SetUpState();
        }
        #if UNITY_EDITOR
        private static void OnBeforeDomainReload()
        {
            // We need to release NativeArrays we're holding before losing track of them during domain reloads.
            Touch.s_GlobalState.playerState.Destroy();
            Touch.s_GlobalState.editorState.Destroy();
        }
        #endif
        [Conditional("DEVELOPMENT_BUILD")]
        [Conditional("UNITY_EDITOR")]
        internal static void CheckEnabled()
        {
            if (!enabled)
                throw new InvalidOperationException("EnhancedTouch API is not enabled; call EnhancedTouchSupport.Enable()");
        }
    }
}