using System;
using UnityEngine.InputSystem.Controls;
using UnityEngine.InputSystem.Layouts;

////TODO: expose user index

////TODO: set displayNames of the controls according to Xbox controller standards

namespace UnityEngine.InputSystem.XInput
{
    /// <summary>
    /// An XInput-compatible game controller.
    /// </summary>
    /// <remarks>
    /// Note that on non-Microsoft platforms, XInput controllers will not actually use the XInput interface
    /// but will rather be interfaced with through different APIs -- on OSX, for example, HID is used to
    /// interface with Xbox controlllers. In those cases, XInput-specific functionality (like <see cref="Capabilities"/>)
    /// will not be available.
    ///
    /// On Windows, XInput controllers will be reported with <see cref="InputDeviceDescription.interfaceName"/>
    /// set to <c>"XInput"</c> and with a JSON representation of <a
    /// href="https://docs.microsoft.com/en-us/windows/win32/api/xinput/ns-xinput-xinput_capabilities">XINPUT_CAPABILITIES</a>
    /// available in <see cref="InputDeviceDescription.capabilities"/>. This means that you match on those
    /// <c>subType</c> and/or <c>flags</c> for example.
    ///
    /// <example>
    /// <code>
    /// // Create an XInput-specific guitar layout subtype.
    /// // NOTE: Works only on Windows.
    /// InputSystem.RegisterLayout(@"
    ///     {
    ///         ""name"" : ""XInputGuitar"",
    ///         ""displayName"" : ""Guitar"",
    ///         ""extend"" : ""XInputController"",
    ///         ""device"" : {
    ///             ""interface"" : ""XInput"",
    ///             ""capabilities"" : [
    ///                 { ""path"" : ""subType"", ""value"" : ""6"" }
    ///             ]
    ///         }
    ///     }
    /// ");
    /// </code>
    /// </example>
    ///
    /// Now, when an XInput controller is connected and reports itself with the
    /// subtype "Guitar", it is turned into an "XInputGuitar" instead of an
    /// "XInputController".
    /// </remarks>
    [InputControlLayout(displayName = "Xbox Controller")]
    [Scripting.Preserve]
    public class XInputController : Gamepad
    {
        /// <summary>
        /// Same as <see cref="Gamepad.startButton"/>.
        /// </summary>
        /// <value>Same control as <see cref="Gamepad.startButton"/>.</value>
        // Change the display names for the buttons to conform to Xbox conventions.
        [InputControl(name = "buttonSouth", displayName = "A")]
        [InputControl(name = "buttonEast", displayName = "B")]
        [InputControl(name = "buttonWest", displayName = "X")]
        [InputControl(name = "buttonNorth", displayName = "Y")]
        [InputControl(name = "leftShoulder", displayName = "Left Bumper", shortDisplayName = "LB")]
        [InputControl(name = "rightShoulder", displayName = "Right Bumper", shortDisplayName = "RB")]
        [InputControl(name = "leftTrigger", shortDisplayName = "LT")]
        [InputControl(name = "rightTrigger", shortDisplayName = "RT")]
        // This follows Xbox One conventions; on Xbox 360, this is start=start and select=back.
        [InputControl(name = "start", displayName = "Menu", alias = "menu")]
        [InputControl(name = "select", displayName = "View", alias = "view")]
        public ButtonControl menu { get; private set; }

        /// <summary>
        /// Same as <see cref="Gamepad.selectButton"/>
        /// </summary>
        /// <value>Same control as <see cref="Gamepad.selectButton"/>.</value>
        public ButtonControl view { get; private set; }

        /// <summary>
        /// What specific kind of XInput controller this is.
        /// </summary>
        /// <value>XInput device subtype.</value>
        /// <remarks>
        /// When the controller is picked up through interfaces other than XInput or through old versions of
        /// XInput, this will always be <see cref="DeviceSubType.Unknown"/>. Put another way, this value is
        /// meaningful only on recent Microsoft platforms.
        /// </remarks>
        /// <seealso href="https://docs.microsoft.com/en-us/windows/win32/xinput/xinput-and-controller-subtypes"/>
        public DeviceSubType subType
        {
            get
            {
                if (!m_HaveParsedCapabilities)
                    ParseCapabilities();
                return m_SubType;
            }
        }

        /// <summary>
        /// Return the device flags as reported by XInput.
        /// </summary>
        /// <value>XInput device flags.</value>
        /// <seealso href="https://docs.microsoft.com/en-us/windows/win32/api/xinput/ns-xinput-xinput_capabilities"/>
        public DeviceFlags flags
        {
            get
            {
                if (!m_HaveParsedCapabilities)
                    ParseCapabilities();
                return m_Flags;
            }
        }

        /// <inheritdoc />
        protected override void FinishSetup()
        {
            base.FinishSetup();

            menu = startButton;
            view = selectButton;
        }

        private bool m_HaveParsedCapabilities;
        private DeviceSubType m_SubType;
        private DeviceFlags m_Flags;

        private void ParseCapabilities()
        {
            if (!string.IsNullOrEmpty(description.capabilities))
            {
                var capabilities = JsonUtility.FromJson<Capabilities>(description.capabilities);
                m_SubType = capabilities.subType;
                m_Flags = capabilities.flags;
            }
            m_HaveParsedCapabilities = true;
        }

        /// <summary>
        /// Controller type enumeration in <c>Type</c> field of <c>XINPUT_CAPABILITIES</c>.
        /// </summary>
        /// <remarks>
        /// See <a href="https://docs.microsoft.com/en-us/windows/win32/api/xinput/ns-xinput-xinput_capabilities">MSDN</a>.
        /// </remarks>
        internal enum DeviceType
        {
            Gamepad = 0x00
        }

        /// <summary>
        /// Controller subtype enumeration in <c>SubType</c> field of <c>XINPUT_CAPABILITIES</c>.
        /// </summary>
        /// <remarks>
        /// See <a href="https://docs.microsoft.com/en-us/windows/win32/xinput/xinput-and-controller-subtypes">MSDN</a>.
        /// </remarks>
        public enum DeviceSubType
        {
            Unknown = 0x00,
            Gamepad = 0x01,
            Wheel = 0x02,
            ArcadeStick = 0x03,
            FlightStick = 0x04,
            DancePad = 0x05,
            Guitar = 0x06,
            GuitarAlternate = 0x07,
            DrumKit = 0x08,
            GuitarBass = 0x0B,
            ArcadePad = 0x13
        }

        /// <summary>
        /// Controller flags in <c>Flags</c> field of <c>XINPUT_CAPABILITIES</c>.
        /// </summary>
        /// <remarks>
        /// See <a href="https://docs.microsoft.com/en-us/windows/win32/api/xinput/ns-xinput-xinput_capabilities">MSDN</a>.
        /// </remarks>
        [Flags]
        public new enum DeviceFlags
        {
            ForceFeedbackSupported = 0x01,
            Wireless = 0x02,
            VoiceSupported = 0x04,
            PluginModulesSupported = 0x08,
            NoNavigation = 0x10,
        }

        [Serializable]
        internal struct Capabilities
        {
            public DeviceType type;
            public DeviceSubType subType;
            public DeviceFlags flags;
        }
    }
}
