// Unity C# reference source
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using UnityEngine.Bindings;
using UnityEngine.Scripting;

namespace UnityEngine
{
    public enum TouchPhase
    {
        Began = 0,
        Moved = 1,
        Stationary = 2,
        Ended = 3,
        Canceled = 4
    }

    // Controls IME input
    public enum IMECompositionMode
    {
        Auto = 0,
        On = 1,
        Off = 2
    }

    public enum TouchType
    {
        Direct,
        Indirect,
        Stylus
    }

    [NativeHeader("Runtime/Input/InputBindings.h")]
    public struct Touch
    {
        private int m_FingerId;
        private Vector2 m_Position;
        private Vector2 m_RawPosition;
        private Vector2 m_PositionDelta;
        private float m_TimeDelta;
        private int m_TapCount;
        private TouchPhase m_Phase;
        private TouchType m_Type;
        private float m_Pressure;
        private float m_maximumPossiblePressure;
        private float m_Radius;
        private float m_RadiusVariance;
        private float m_AltitudeAngle;
        private float m_AzimuthAngle;

        public int fingerId { get { return m_FingerId; } set { m_FingerId = value; } }
        public Vector2 position { get { return m_Position; } set { m_Position = value; }  }
        public Vector2 rawPosition { get { return m_RawPosition; } set { m_RawPosition = value; }  }
        public Vector2 deltaPosition { get { return m_PositionDelta; } set { m_PositionDelta = value; }  }
        public float deltaTime { get { return m_TimeDelta; } set { m_TimeDelta = value; }  }
        public int tapCount { get { return m_TapCount; } set { m_TapCount = value; }  }
        public TouchPhase phase { get { return m_Phase; } set { m_Phase = value; }  }
        public float pressure { get { return m_Pressure; } set { m_Pressure = value; }  }
        public float maximumPossiblePressure { get { return m_maximumPossiblePressure; } set { m_maximumPossiblePressure = value; }  }

        public TouchType type { get { return m_Type; } set { m_Type = value; }  }
        public float altitudeAngle { get { return m_AltitudeAngle; } set { m_AltitudeAngle = value; }  }
        public float azimuthAngle { get { return m_AzimuthAngle; } set { m_AzimuthAngle = value; }  }
        public float radius { get { return m_Radius; } set { m_Radius = value; }  }
        public float radiusVariance { get { return m_RadiusVariance; } set { m_RadiusVariance = value; }  }
    }

    // Matches PenData::PenStatusEnum in native code
    [Flags]
    public enum PenStatus
    {
        None = 0x0,
        Contact = 0x1,
        Barrel = 0x2,
        Inverted = 0x4,
        Eraser = 0x8,
    }

    public enum PenEventType
    {
        NoContact,
        PenDown,
        PenUp
    }

    public struct PenData
    {
        public Vector2 position;
        public Vector2 tilt;
        public PenStatus penStatus;
        public float twist;
        public float pressure;
        public PenEventType contactType;
        public Vector2 deltaPos;
    }

    public enum DeviceOrientation
    {
        Unknown = 0,
        Portrait = 1,
        PortraitUpsideDown = 2,
        LandscapeLeft = 3,
        LandscapeRight = 4,
        FaceUp = 5,
        FaceDown = 6
    }

    public struct AccelerationEvent
    {
        internal float x, y, z;
        internal float m_TimeDelta;

        public Vector3 acceleration { get { return new Vector3(x, y, z); } }
        public float deltaTime { get { return m_TimeDelta; } }
    }

    [NativeHeader("Runtime/Input/GetInput.h")]
    public class Gyroscope
    {
        internal Gyroscope(int index)
        {
            m_GyroIndex = index;
        }

        private int m_GyroIndex;

        [FreeFunction("GetGyroRotationRate")]
        extern private static Vector3 rotationRate_Internal(int idx);
        [FreeFunction("GetGyroRotationRateUnbiased")]
        extern private static Vector3 rotationRateUnbiased_Internal(int idx);
        [FreeFunction("GetGravity")]
        extern private static Vector3 gravity_Internal(int idx);
        [FreeFunction("GetUserAcceleration")]
        extern private static Vector3 userAcceleration_Internal(int idx);
        [FreeFunction("GetAttitude")]
        extern private static Quaternion attitude_Internal(int idx);
        [FreeFunction("IsGyroEnabled")]
        extern private static bool getEnabled_Internal(int idx);
        [FreeFunction("SetGyroEnabled")]
        extern private static void setEnabled_Internal(int idx, bool enabled);
        [FreeFunction("GetGyroUpdateInterval")]
        extern private static float getUpdateInterval_Internal(int idx);
        [FreeFunction("SetGyroUpdateInterval")]
        extern private static void setUpdateInterval_Internal(int idx, float interval);

        public Vector3 rotationRate { get { return rotationRate_Internal(m_GyroIndex); } }
        public Vector3 rotationRateUnbiased { get { return rotationRateUnbiased_Internal(m_GyroIndex); } }
        public Vector3 gravity { get { return gravity_Internal(m_GyroIndex); } }
        public Vector3 userAcceleration { get { return userAcceleration_Internal(m_GyroIndex); } }
        public Quaternion attitude { get { return attitude_Internal(m_GyroIndex); } }
        public bool enabled { get { return getEnabled_Internal(m_GyroIndex); } set { setEnabled_Internal(m_GyroIndex, value); } }
        public float updateInterval { get { return getUpdateInterval_Internal(m_GyroIndex); } set { setUpdateInterval_Internal(m_GyroIndex, value); } }
    }

    public struct LocationInfo
    {
        internal double m_Timestamp;
        internal float m_Latitude;
        internal float m_Longitude;
        internal float m_Altitude;
        internal float m_HorizontalAccuracy;
        internal float m_VerticalAccuracy;

        public float latitude { get { return m_Latitude; } }
        public float longitude { get { return m_Longitude; } }
        public float altitude { get { return m_Altitude; } }
        public float horizontalAccuracy { get { return m_HorizontalAccuracy; } }
        public float verticalAccuracy { get { return m_VerticalAccuracy; } }
        public double timestamp { get { return m_Timestamp; } }
    }

    public enum LocationServiceStatus
    {
        Stopped = 0,
        Initializing = 1,
        Running = 2,
        Failed = 3
    }

    [NativeHeader("Runtime/Input/LocationService.h")]
    [NativeHeader("Runtime/Input/InputBindings.h")]
    public class LocationService
    {
        internal struct HeadingInfo
        {
            public float magneticHeading;
            public float trueHeading;
            public float headingAccuracy;
            public Vector3 raw;
            public double timestamp;
        }

        [FreeFunction("LocationService::IsServiceEnabledByUser")]
        internal extern static bool IsServiceEnabledByUser();
        [FreeFunction("LocationService::GetLocationStatus")]
        internal extern static LocationServiceStatus GetLocationStatus();
        [FreeFunction("LocationService::GetLastLocation")]
        internal extern static LocationInfo GetLastLocation();
        [FreeFunction("LocationService::SetDesiredAccuracy")]
        internal extern static void SetDesiredAccuracy(float value);
        [FreeFunction("LocationService::SetDistanceFilter")]
        internal extern static void SetDistanceFilter(float value);
        [FreeFunction("LocationService::StartUpdatingLocation")]
        internal extern static void StartUpdatingLocation();
        [FreeFunction("LocationService::StopUpdatingLocation")]
        internal extern static void StopUpdatingLocation();
        [FreeFunction("LocationService::GetLastHeading")]
        internal extern static HeadingInfo GetLastHeading();
        [FreeFunction("LocationService::IsHeadingUpdatesEnabled")]
        internal extern static bool IsHeadingUpdatesEnabled();
        [FreeFunction("LocationService::SetHeadingUpdatesEnabled")]
        internal extern static void SetHeadingUpdatesEnabled(bool value);

        public bool isEnabledByUser { get { return IsServiceEnabledByUser(); } }
        public LocationServiceStatus status { get { return GetLocationStatus(); } }
        public LocationInfo lastData
        {
            get
            {
                if (status != LocationServiceStatus.Running)
                    Debug.Log("Location service updates are not enabled. Check LocationService.status before querying last location.");

                return GetLastLocation();
            }
        }

        public void Start(float desiredAccuracyInMeters, float updateDistanceInMeters)
        {
            SetDesiredAccuracy(desiredAccuracyInMeters);
            SetDistanceFilter(updateDistanceInMeters);
            StartUpdatingLocation();
        }

        public void Start(float desiredAccuracyInMeters)
        {
            Start(desiredAccuracyInMeters, 10f);
        }

        public void Start()
        {
            Start(10f, 10f);
        }

        public void Stop()
        {
            StopUpdatingLocation();
        }
    }

    public class Compass
    {
        public float magneticHeading
        {
            get { return LocationService.GetLastHeading().magneticHeading; }
        }
        public float trueHeading
        {
            get { return LocationService.GetLastHeading().trueHeading; }
        }
        public float headingAccuracy
        {
            get { return LocationService.GetLastHeading().headingAccuracy; }
        }
        public Vector3 rawVector
        {
            get { return LocationService.GetLastHeading().raw; }
        }
        public double timestamp
        {
            get { return LocationService.GetLastHeading().timestamp; }
        }
        public bool enabled
        {
            get { return LocationService.IsHeadingUpdatesEnabled(); }
            set { LocationService.SetHeadingUpdatesEnabled(value); }
        }
    }

    [NativeHeader("Runtime/Camera/Camera.h")]
    // This is needed to internally call Physics/Physics2D using an interface if available, so we don't have a hard dependency on Physics.
    internal class CameraRaycastHelper
    {
        [FreeFunction("CameraScripting::RaycastTry")]   extern internal static GameObject RaycastTry(Camera cam, Ray ray, float distance, int layerMask);
        [FreeFunction("CameraScripting::RaycastTry2D")] extern internal static GameObject RaycastTry2D(Camera cam, Ray ray, float distance, int layerMask);
    }

    // Burst-compatible unmanaged string calls can not be in UnityEngine namespace (UnityEngine.Internal is okay)
    namespace Internal
    {
        [NativeHeader("Runtime/Input/InputBindings.h")]
        internal static class InputUnsafeUtility
        {
            [NativeThrows]
            internal extern static bool GetKeyString(string name);
            // Burst shadow
            [NativeThrows]
            // This will only be referenced from Burst-generated code, in place of the version without the
            // __Managed suffix. So we need to make sure it will not get stripped.
            [RequiredMember]
            internal extern static unsafe bool GetKeyString__Unmanaged(byte* name, int nameLen);
            [NativeThrows]
            internal extern static bool GetKeyUpString(string name);
            // Burst shadow
            [NativeThrows]
            // This will only be referenced from Burst-generated code, in place of the version without the
            // __Managed suffix. So we need to make sure it will not get stripped.
            [RequiredMember]
            internal extern static unsafe bool GetKeyUpString__Unmanaged(byte* name, int nameLen);
            [NativeThrows]
            internal extern static bool GetKeyDownString(string name);
            // Burst shadow
            [NativeThrows]
            // This will only be referenced from Burst-generated code, in place of the version without the
            // __Managed suffix. So we need to make sure it will not get stripped.
            [RequiredMember]
            internal extern static unsafe bool GetKeyDownString__Unmanaged(byte* name, int nameLen);
            [NativeThrows]
            internal extern static float GetAxis(string axisName);
            // Burst shadow
            [NativeThrows]
            // This will only be referenced from Burst-generated code, in place of the version without the
            // __Managed suffix. So we need to make sure it will not get stripped.
            [RequiredMember]
            internal extern static unsafe float GetAxis__Unmanaged(byte* axisName, int axisNameLen);
            [NativeThrows]
            internal extern static float GetAxisRaw(string axisName);
            // Burst shadow
            [NativeThrows]
            // This will only be referenced from Burst-generated code, in place of the version without the
            // __Managed suffix. So we need to make sure it will not get stripped.
            [RequiredMember]
            internal extern static unsafe float GetAxisRaw__Unmanaged(byte* axisName, int axisNameLen);
            [NativeThrows]
            internal extern static bool GetButton(string buttonName);
            // Burst shadow
            [NativeThrows]
            // This will only be referenced from Burst-generated code, in place of the version without the
            // __Managed suffix. So we need to make sure it will not get stripped.
            [RequiredMember]
            internal extern static unsafe bool GetButton__Unmanaged(byte* buttonName, int buttonNameLen);
            [NativeThrows]
            internal extern static bool GetButtonDown(string buttonName);
            // Burst shadow
            [NativeThrows]
            // This will only be referenced from Burst-generated code, in place of the version without the
            // __Managed suffix. So we need to make sure it will not get stripped.
            [RequiredMember]
            internal extern static unsafe byte GetButtonDown__Unmanaged(byte* buttonName, int buttonNameLen);
            [NativeThrows]
            internal extern static bool GetButtonUp(string buttonName);
            // Burst shadow
            [NativeThrows]
            // This will only be referenced from Burst-generated code, in place of the version without the
            // __Managed suffix. So we need to make sure it will not get stripped.
            [RequiredMember]
            internal extern static unsafe bool GetButtonUp__Unmanaged(byte* buttonName, int buttonNameLen);
            internal extern static bool IsJoystickPreconfigured(string joystickName);
            // This will only be referenced from Burst-generated code, in place of the version without the
            // __Managed suffix. So we need to make sure it will not get stripped.
            [RequiredMember]
            internal extern static unsafe bool IsJoystickPreconfigured__Unmanaged(byte* joystickName, int joystickNameLen);
        }
    }

    [NativeHeader("Runtime/Input/InputBindings.h")]
    public class Input
    {
        public static float GetAxis(string axisName) => Internal.InputUnsafeUtility.GetAxis(axisName);
        public static float GetAxisRaw(string axisName) => Internal.InputUnsafeUtility.GetAxisRaw(axisName);
        public static bool GetButton(string buttonName) => Internal.InputUnsafeUtility.GetButton(buttonName);
        public static bool GetButtonDown(string buttonName) => Internal.InputUnsafeUtility.GetButtonDown(buttonName);
        public static bool GetButtonUp(string buttonName) => Internal.InputUnsafeUtility.GetButtonUp(buttonName);

        [NativeThrows]
        private extern static bool GetKeyInt(KeyCode key);
        [NativeThrows]
        private extern static bool GetKeyUpInt(KeyCode key);
        [NativeThrows]
        private extern static bool GetKeyDownInt(KeyCode key);
        [NativeThrows]
        public extern static bool GetMouseButton(int button);
        [NativeThrows]
        public extern static bool GetMouseButtonDown(int button);
        [NativeThrows]
        public extern static bool GetMouseButtonUp(int button);
        [FreeFunction("ResetInput")]
        public extern static void ResetInputAxes();
        public static bool IsJoystickPreconfigured(string joystickName) => Internal.InputUnsafeUtility.IsJoystickPreconfigured(joystickName);
        [NativeThrows]
        public extern static string[] GetJoystickNames();
        [NativeThrows]
        public extern static Touch GetTouch(int index);
        [NativeThrows]
        public extern static PenData GetPenEvent(int index);
        [NativeThrows]
        public extern static PenData GetLastPenContactEvent();
        [NativeThrows]
        public extern static void ResetPenEvents();
        [NativeThrows]
        public extern static void ClearLastPenContactEvent();
        [NativeThrows]
        public extern static AccelerationEvent GetAccelerationEvent(int index);

        public static bool GetKey(KeyCode key) => GetKeyInt(key);
        public static bool GetKey(string name) => Internal.InputUnsafeUtility.GetKeyString(name);
        public static bool GetKeyUp(KeyCode key) => GetKeyUpInt(key);
        public static bool GetKeyUp(string name) => Internal.InputUnsafeUtility.GetKeyUpString(name);
        public static bool GetKeyDown(KeyCode key) => GetKeyDownInt(key);
        public static bool GetKeyDown(string name) => Internal.InputUnsafeUtility.GetKeyDownString(name);

        [Conditional("UNITY_EDITOR")]
        internal static void SimulateTouch(Touch touch)
        {
            SimulateTouchInternal(touch, DateTime.Now.Ticks);
        }

        [Conditional("UNITY_EDITOR")]
        [NativeConditional("UNITY_EDITOR")]
        [FreeFunction("SimulateTouch")]
        private extern static void SimulateTouchInternal(Touch touch, long timestamp);

        public extern static bool simulateMouseWithTouches { get; set; }
        [NativeThrows]
        public extern static bool anyKey { get; }
        [NativeThrows]
        public extern static bool anyKeyDown { get; }
        [NativeThrows]
        public extern static string inputString { get; }
        [NativeThrows]
        public extern static Vector3 mousePosition { get; }
        [NativeThrows]
        public extern static Vector2 mouseScrollDelta { get; }
        public extern static IMECompositionMode imeCompositionMode { get; set; }
        public extern static string compositionString { get; }
        public extern static bool imeIsSelected { get; }
        public extern static Vector2 compositionCursorPos { get; set; }
        [Obsolete("eatKeyPressOnTextFieldFocus property is deprecated, and only provided to support legacy behavior.")]
        public extern static bool eatKeyPressOnTextFieldFocus { get; set; }

        public extern static bool mousePresent
        {
            [FreeFunction("GetMousePresent")]
            get;
        }

        public extern static int penEventCount
        {
            [FreeFunction("GetPenEventCount")]
            get;
        }

        public extern static int touchCount
        {
            [FreeFunction("GetTouchCount")]
            get;
        }
        public extern static bool touchPressureSupported
        {
            [FreeFunction("IsTouchPressureSupported")]
            get;
        }
        public extern static bool stylusTouchSupported
        {
            [FreeFunction("IsStylusTouchSupported")]
            get;
        }
        public extern static bool touchSupported
        {
            [FreeFunction("IsTouchSupported")]
            get;
        }
        public extern static bool multiTouchEnabled
        {
            [FreeFunction("IsMultiTouchEnabled")]
            get;
            [FreeFunction("SetMultiTouchEnabled")]
            set;
        }
        [Obsolete("isGyroAvailable property is deprecated. Please use SystemInfo.supportsGyroscope instead.")]
        public extern static bool isGyroAvailable
        {
            [FreeFunction("IsGyroAvailable")]
            get;
        }
        public extern static DeviceOrientation deviceOrientation
        {
            [FreeFunction("GetDeviceOrientation")]
            get;
        }
        public extern static Vector3 acceleration
        {
            [FreeFunction("GetAcceleration")]
            get;
        }
        public extern static bool compensateSensors
        {
            [FreeFunction("IsCompensatingSensors")]
            get;
            [FreeFunction("SetCompensatingSensors")]
            set;
        }
        public extern static int accelerationEventCount
        {
            [FreeFunction("GetAccelerationCount")]
            get;
        }
        public extern static bool backButtonLeavesApp
        {
            [FreeFunction("GetBackButtonLeavesApp")]
            get;
            [FreeFunction("SetBackButtonLeavesApp")]
            set;
        }

        private static LocationService locationServiceInstance;
        public static LocationService location
        {
            get
            {
                if (locationServiceInstance == null)
                    locationServiceInstance = new LocationService();
                return locationServiceInstance;
            }
        }
        private static Compass compassInstance;
        public static Compass compass
        {
            get
            {
                if (compassInstance == null)
                    compassInstance = new Compass();
                return compassInstance;
            }
        }
        [FreeFunction("GetGyro")]
        private extern static int GetGyroInternal();
        private static Gyroscope s_MainGyro;
        public static Gyroscope gyro
        {
            get
            {
                if (s_MainGyro == null)
                    s_MainGyro = new Gyroscope(GetGyroInternal());
                return s_MainGyro;
            }
        }

        public static Touch[] touches
        {
            get
            {
                int count = touchCount;
                Touch[] touches = new Touch[count];
                for (int q = 0; q < count; ++q)
                    touches[q] = GetTouch(q);
                return touches;
            }
        }

        public static AccelerationEvent[] accelerationEvents
        {
            get
            {
                int count = accelerationEventCount;
                AccelerationEvent[] events = new AccelerationEvent[count];
                for (int q = 0; q < count; ++q)
                    events[q] = GetAccelerationEvent(q);
                return events;
            }
        }

        internal extern static bool CheckDisabled();
    }
}
