// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEngine;
using UnityEngine.Networking;

namespace HoloToolkit.Unity
{
    /// <summary>
    /// Renders the UI and handles update logic for HoloToolkit/Configure/Apply Mixed Reality Project Settings.
    /// </summary>
    public class ProjectSettingsWindow : AutoConfigureWindow<ProjectSettingsWindow.ProjectSetting>
    {
        private const string SharingServiceURL = "https://raw.githubusercontent.com/Microsoft/MixedRealityToolkit-Unity/master/External/HoloToolkit/Sharing/Server/SharingService.exe";

        /// <summary>
        /// This is used to keep a local list of axis names, so we don't have to keep iterating through each SerializedProperty.
        /// </summary>
        private List<string> axisNames = new List<string>();

        /// <summary>
        /// This is used to keep a single reference to InputManager.asset, refreshed when necessary.
        /// </summary>
        private SerializedObject inputManagerAsset;

        /// <summary>
        /// Define new axes here adding a new InputManagerAxis to the array.
        /// </summary>
        private readonly InputManagerAxis[] newInputAxes =
        {
            new InputManagerAxis() { Name = InputMappingAxisUtility.CONTROLLER_LEFT_STICK_HORIZONTAL,  Dead = 0.19f, Sensitivity = 1, Invert = false, Type = AxisType.JoystickAxis, Axis = 1 },
            new InputManagerAxis() { Name = InputMappingAxisUtility.CONTROLLER_LEFT_STICK_VERTICAL,    Dead = 0.19f, Sensitivity = 1, Invert = true,  Type = AxisType.JoystickAxis, Axis = 2 },
            new InputManagerAxis() { Name = InputMappingAxisUtility.XBOX_SHARED_TRIGGER,               Dead = 0.19f, Sensitivity = 1, Invert = false, Type = AxisType.JoystickAxis, Axis = 3 },
            new InputManagerAxis() { Name = InputMappingAxisUtility.CONTROLLER_RIGHT_STICK_HORIZONTAL, Dead = 0.19f, Sensitivity = 1, Invert = false, Type = AxisType.JoystickAxis, Axis = 4 },
            new InputManagerAxis() { Name = InputMappingAxisUtility.CONTROLLER_RIGHT_STICK_VERTICAL,   Dead = 0.19f, Sensitivity = 1, Invert = true,  Type = AxisType.JoystickAxis, Axis = 5 },
            new InputManagerAxis() { Name = InputMappingAxisUtility.XBOX_DPAD_HORIZONTAL,              Dead = 0.19f, Sensitivity = 1, Invert = false, Type = AxisType.JoystickAxis, Axis = 6 },
            new InputManagerAxis() { Name = InputMappingAxisUtility.XBOX_DPAD_VERTICAL,                Dead = 0.19f, Sensitivity = 1, Invert = false, Type = AxisType.JoystickAxis, Axis = 7 },
            new InputManagerAxis() { Name = InputMappingAxisUtility.CONTROLLER_LEFT_TRIGGER,           Dead = 0.19f, Sensitivity = 1, Invert = false, Type = AxisType.JoystickAxis, Axis = 9 },
            new InputManagerAxis() { Name = InputMappingAxisUtility.CONTROLLER_RIGHT_TRIGGER,          Dead = 0.19f, Sensitivity = 1, Invert = false, Type = AxisType.JoystickAxis, Axis = 10 },

            new InputManagerAxis() { Name = InputMappingAxisUtility.XBOX_A,                          PositiveButton = "joystick button 0", Gravity = 1000, Dead = 0.001f, Sensitivity = 1000, Type = AxisType.KeyOrMouseButton, Axis = 1 },
            new InputManagerAxis() { Name = InputMappingAxisUtility.XBOX_B,                          PositiveButton = "joystick button 1", Gravity = 1000, Dead = 0.001f, Sensitivity = 1000, Type = AxisType.KeyOrMouseButton, Axis = 1 },
            new InputManagerAxis() { Name = InputMappingAxisUtility.XBOX_X,                          PositiveButton = "joystick button 2", Gravity = 1000, Dead = 0.001f, Sensitivity = 1000, Type = AxisType.KeyOrMouseButton, Axis = 1 },
            new InputManagerAxis() { Name = InputMappingAxisUtility.XBOX_Y,                          PositiveButton = "joystick button 3", Gravity = 1000, Dead = 0.001f, Sensitivity = 1000, Type = AxisType.KeyOrMouseButton, Axis = 1 },
            new InputManagerAxis() { Name = InputMappingAxisUtility.CONTROLLER_LEFT_BUMPER_OR_GRIP,  PositiveButton = "joystick button 4", Gravity = 1000, Dead = 0.001f, Sensitivity = 1000, Type = AxisType.KeyOrMouseButton, Axis = 1 },
            new InputManagerAxis() { Name = InputMappingAxisUtility.CONTROLLER_RIGHT_BUMPER_OR_GRIP, PositiveButton = "joystick button 5", Gravity = 1000, Dead = 0.001f, Sensitivity = 1000, Type = AxisType.KeyOrMouseButton, Axis = 1 },
            new InputManagerAxis() { Name = InputMappingAxisUtility.CONTROLLER_LEFT_MENU,            PositiveButton = "joystick button 6", Gravity = 1000, Dead = 0.001f, Sensitivity = 1000, Type = AxisType.KeyOrMouseButton, Axis = 1 },
            new InputManagerAxis() { Name = InputMappingAxisUtility.CONTROLLER_RIGHT_MENU,           PositiveButton = "joystick button 7", Gravity = 1000, Dead = 0.001f, Sensitivity = 1000, Type = AxisType.KeyOrMouseButton, Axis = 1 },
            new InputManagerAxis() { Name = InputMappingAxisUtility.CONTROLLER_LEFT_STICK_CLICK,     PositiveButton = "joystick button 8", Gravity = 1000, Dead = 0.001f, Sensitivity = 1000, Type = AxisType.KeyOrMouseButton, Axis = 1 },
            new InputManagerAxis() { Name = InputMappingAxisUtility.CONTROLLER_RIGHT_STICK_CLICK,    PositiveButton = "joystick button 9", Gravity = 1000, Dead = 0.001f, Sensitivity = 1000, Type = AxisType.KeyOrMouseButton, Axis = 1 },
        };

        /// <summary>
        /// As axes in newInputAxes are removed or renamed, move them here for proper clean-up in user projects.
        /// </summary>
        private readonly InputManagerAxis[] obsoleteInputAxes = { };

        #region Nested Types

        public enum ProjectSetting
        {
            BuildWsaUwp,
            WsaEnableXR,
            WsaUwpBuildToD3D,
            TargetOccludedDevices,
            SharingServices,
            UseInputManagerAxes,
            DotNetScriptingBackend,
        }

        /// <summary>
        /// Used to map AxisType from a useful name to the int value the InputManager wants.
        /// </summary>
        private enum AxisType
        {
            KeyOrMouseButton,
            MouseMovement,
            JoystickAxis
        };

        /// <summary>
        /// Used to define an entire InputManagerAxis, with each variable defined by the same term the Inspector shows.
        /// </summary>
        private class InputManagerAxis
        {
            public string Name = "";
            public string DescriptiveName = "";
            public string DescriptiveNegativeName = "";
            public string NegativeButton = "";
            public string PositiveButton = "";
            public string AltNegativeButton = "";
            public string AltPositiveButton = "";
            public float Gravity = 0.0f;
            public float Dead = 0.0f;
            public float Sensitivity = 0.0f;
            public bool Snap = false;
            public bool Invert = false;
            public AxisType Type = default(AxisType);
            public int Axis = 0;
            public int JoyNum = 0;
        }

        #endregion // Nested Types

        #region Overrides / Event Handlers

        protected override void ApplySettings()
        {
            // Apply individual settings
            if (Values[ProjectSetting.BuildWsaUwp])
            {
                if (EditorUserBuildSettings.activeBuildTarget != BuildTarget.WSAPlayer)
                {
#if UNITY_2017_1_OR_NEWER
                    EditorUserBuildSettings.SwitchActiveBuildTargetAsync(BuildTargetGroup.WSA, BuildTarget.WSAPlayer);
#else
                    EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTargetGroup.WSA, BuildTarget.WSAPlayer);
#endif
                }
                else
                {
                    UpdateSettings(EditorUserBuildSettings.activeBuildTarget);
                }
            }
            else
            {
                EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTargetGroup.Standalone, BuildTarget.StandaloneWindows64);
            }
        }

        protected override void LoadSettings()
        {
            for (int i = (int)ProjectSetting.BuildWsaUwp; i <= (int)ProjectSetting.DotNetScriptingBackend; i++)
            {
                switch ((ProjectSetting)i)
                {
                    case ProjectSetting.BuildWsaUwp:
                    case ProjectSetting.WsaEnableXR:
                    case ProjectSetting.WsaUwpBuildToD3D:
                    case ProjectSetting.DotNetScriptingBackend:
                        Values[(ProjectSetting)i] = true;
                        break;
                    case ProjectSetting.TargetOccludedDevices:
                        Values[(ProjectSetting)i] = EditorPrefsUtility.GetEditorPref(Names[(ProjectSetting)i], false);
                        break;
                    case ProjectSetting.SharingServices:
                        Values[(ProjectSetting)i] = EditorPrefsUtility.GetEditorPref(Names[(ProjectSetting)i], false);
                        break;
                    case ProjectSetting.UseInputManagerAxes:
                        Values[(ProjectSetting)i] = EditorPrefsUtility.GetEditorPref(Names[(ProjectSetting)i], false);
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            }

        }

        private void UpdateSettings(BuildTarget currentBuildTarget)
        {
            EditorPrefsUtility.SetEditorPref(Names[ProjectSetting.SharingServices], Values[ProjectSetting.SharingServices]);
            if (Values[ProjectSetting.SharingServices])
            {
                string sharingServiceDirectory = Directory.GetParent(Path.GetFullPath(Application.dataPath)).FullName + "\\External\\HoloToolkit\\Sharing\\Server";
                string sharingServicePath = sharingServiceDirectory + "\\SharingService.exe";
                if (!File.Exists(sharingServicePath) &&
                    EditorUtility.DisplayDialog("Attention!",
                        "You're missing the Sharing Service Executable in your project.\n\n" +
                        "Would you like to download the missing files from GitHub?\n\n" +
                        "Alternatively, you can download it yourself or specify a target IP to connect to at runtime on the Sharing Stage.",
                        "Yes", "Cancel"))
                {
                    using (var webRequest = UnityWebRequest.Get(SharingServiceURL))
                    {
#if UNITY_2017_2_OR_NEWER
                        webRequest.SendWebRequest();
#else
                        webRequest.Send();
#endif
                        while (!webRequest.isDone)
                        {
                            if (webRequest.downloadProgress > -1)
                            {
                                EditorUtility.DisplayProgressBar(
                                    "Downloading the SharingService executable from GitHub",
                                    "Progress...", webRequest.downloadProgress);
                            }
                        }

                        EditorUtility.ClearProgressBar();

#if UNITY_2017_1_OR_NEWER
                        if (webRequest.isNetworkError || webRequest.isHttpError)
#else
                        if (webRequest.isError)
#endif
                        {
                            Debug.LogError("Network Error: " + webRequest.error);
                        }
                        else
                        {
                            byte[] sharingServiceData = webRequest.downloadHandler.data;
                            Directory.CreateDirectory(sharingServiceDirectory);
                            File.WriteAllBytes(sharingServicePath, sharingServiceData);
                        }
                    }
                }
                else
                {
                    Debug.LogFormat("Alternatively, you can download from this link: {0}", SharingServiceURL);
                }

                PlayerSettings.WSA.SetCapability(PlayerSettings.WSACapability.InternetClientServer, true);
                PlayerSettings.WSA.SetCapability(PlayerSettings.WSACapability.PrivateNetworkClientServer, true);
            }
            else
            {
                PlayerSettings.WSA.SetCapability(PlayerSettings.WSACapability.InternetClient, false);
                PlayerSettings.WSA.SetCapability(PlayerSettings.WSACapability.InternetClientServer, false);
                PlayerSettings.WSA.SetCapability(PlayerSettings.WSACapability.PrivateNetworkClientServer, false);
            }

            bool useToolkitAxes = Values[ProjectSetting.UseInputManagerAxes];

            if (useToolkitAxes != EditorPrefsUtility.GetEditorPref(Names[ProjectSetting.UseInputManagerAxes], false))
            {
                EditorPrefsUtility.SetEditorPref(Names[ProjectSetting.UseInputManagerAxes], useToolkitAxes);

                // Grabs the actual asset file into a SerializedObject, so we can iterate through it and edit it.
                inputManagerAsset = new SerializedObject(AssetDatabase.LoadAssetAtPath("ProjectSettings/InputManager.asset", typeof(UnityEngine.Object)));

                if (useToolkitAxes)
                {
                    foreach (InputManagerAxis axis in newInputAxes)
                    {
                        if (!DoesAxisNameExist(axis.Name))
                        {
                            AddAxis(axis);
                        }
                    }

                }
                else
                {
                    foreach (InputManagerAxis axis in newInputAxes)
                    {
                        if (DoesAxisNameExist(axis.Name))
                        {
                            RemoveAxis(axis.Name);
                        }
                    }

                    foreach (InputManagerAxis axis in obsoleteInputAxes)
                    {
                        if (DoesAxisNameExist(axis.Name))
                        {
                            RemoveAxis(axis.Name);
                        }
                    }
                }

                inputManagerAsset.ApplyModifiedProperties();
            }

            if (currentBuildTarget != BuildTarget.WSAPlayer)
            {
                AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
                Close();
                return;
            }

            EditorUserBuildSettings.wsaUWPBuildType = Values[ProjectSetting.WsaUwpBuildToD3D]
                ? WSAUWPBuildType.D3D
                : WSAUWPBuildType.XAML;

            UnityEditorInternal.VR.VREditor.SetVREnabledOnTargetGroup(BuildTargetGroup.WSA, Values[ProjectSetting.WsaEnableXR]);

            if (!Values[ProjectSetting.WsaEnableXR])
            {
                EditorUserBuildSettings.wsaSubtarget = WSASubtarget.AnyDevice;
                UnityEditorInternal.VR.VREditor.SetVREnabledDevicesOnTargetGroup(BuildTargetGroup.WSA, new[] { "None" });
                PlayerSettings.WSA.SetCapability(PlayerSettings.WSACapability.HumanInterfaceDevice, false);
                BuildDeployPrefs.BuildPlatform = "Any CPU";
            }
            else
            {
#if !UNITY_2017_2_OR_NEWER
                Values[ProjectSetting.TargetOccludedDevices] = false;
#endif
                if (!Values[ProjectSetting.TargetOccludedDevices])
                {
                    EditorUserBuildSettings.wsaSubtarget = WSASubtarget.HoloLens;
#if UNITY_2017_2_OR_NEWER
                    UnityEditorInternal.VR.VREditor.SetVREnabledDevicesOnTargetGroup(BuildTargetGroup.WSA, new[] { "WindowsMR" });
#else
                    UnityEditorInternal.VR.VREditor.SetVREnabledDevicesOnTargetGroup(BuildTargetGroup.WSA, new[] { "HoloLens" });
#endif
                    PlayerSettings.WSA.SetCapability(PlayerSettings.WSACapability.HumanInterfaceDevice, Values[ProjectSetting.UseInputManagerAxes]);
                    BuildDeployPrefs.BuildPlatform = "x86";

                    for (var i = 0; i < QualitySettings.names.Length; i++)
                    {
                        QualitySettings.DecreaseLevel(true);
                    }
                }
                else
                {
                    EditorUserBuildSettings.wsaSubtarget = WSASubtarget.PC;
                    UnityEditorInternal.VR.VREditor.SetVREnabledDevicesOnTargetGroup(BuildTargetGroup.WSA, new[] { "WindowsMR" });
                    PlayerSettings.WSA.SetCapability(PlayerSettings.WSACapability.HumanInterfaceDevice, false);
                    BuildDeployPrefs.BuildPlatform = "x64";

                    for (var i = 0; i < QualitySettings.names.Length; i++)
                    {
                        QualitySettings.IncreaseLevel(true);
                    }
                }

                int currentQualityLevel = QualitySettings.GetQualityLevel();

                // HACK: Edits QualitySettings.asset Directly
                // TODO: replace with friendlier version that uses built in APIs when Unity fixes or makes available.
                // See: http://answers.unity3d.com/questions/886160/how-do-i-change-qualitysetting-for-my-platform-fro.html
                try
                {
                    // Find the WSA element under the platform quality list and replace it's value with the current level.
                    string settingsPath = "ProjectSettings/QualitySettings.asset";
                    string matchPattern = @"(m_PerPlatformDefaultQuality.*Windows Store Apps:) (\d+)";
                    string replacePattern = @"$1 " + currentQualityLevel;

                    string settings = File.ReadAllText(settingsPath);
                    settings = Regex.Replace(settings, matchPattern, replacePattern, RegexOptions.Singleline);

                    File.WriteAllText(settingsPath, settings);
                }
                catch (Exception e)
                {
                    Debug.LogException(e);
                }
            }

            EditorPrefsUtility.SetEditorPref(Names[ProjectSetting.TargetOccludedDevices], Values[ProjectSetting.TargetOccludedDevices]);

            PlayerSettings.SetScriptingBackend(BuildTargetGroup.WSA,
                Values[ProjectSetting.DotNetScriptingBackend]
                    ? ScriptingImplementation.WinRTDotNET
                    : ScriptingImplementation.IL2CPP);

            AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
            Close();
        }

        protected override void OnGuiChanged()
        {
        }

        protected override void LoadStrings()
        {
            Names[ProjectSetting.BuildWsaUwp] = "Target Windows Universal UWP";
            Descriptions[ProjectSetting.BuildWsaUwp] =
                "<b>Required</b>\n\n" +
                "Switches the currently active target to produce a Store app targeting the Universal Windows Platform.\n\n" +
                "<color=#ffff00ff><b>Note:</b></color> Cross platform development can be done with this toolkit, but many features and " +
                "tools will not work if the build target is not Windows Universal.";

            Names[ProjectSetting.WsaEnableXR] = "Enable XR";
            Descriptions[ProjectSetting.WsaEnableXR] =
                "<b>Required</b>\n\n" +
                "Enables 'Windows Holographic' for Windows Store apps.\n\n" +
                "If disabled, your application will run as a normal UWP app on PC, and will launch as a 2D app on HoloLens.\n\n" +
                "<color=#ff0000ff><b>Warning!</b></color> HoloLens and tools like 'Holographic Remoting' will not function without this enabled.";

            Names[ProjectSetting.WsaUwpBuildToD3D] = "Build for Direct3D";
            Descriptions[ProjectSetting.WsaUwpBuildToD3D] =
                "Recommended\n\n" +
                "Produces an app that targets Direct3D instead of XAML.\n\n" +
                "Pure Direct3D apps run faster than applications that include XAML. This option should remain checked unless you plan to " +
                "overlay Unity content with XAML content or you plan to switch between Unity views and XAML views at runtime.";

            Names[ProjectSetting.TargetOccludedDevices] = "Target Occluded Devices";
            Descriptions[ProjectSetting.TargetOccludedDevices] =
                "Changes the target Device and updates the default quality settings, if needed. Occluded devices are generally VR hardware (like the Acer HMD) " +
                "that do not have a 'see through' display, while transparent devices (like the HoloLens) are generally AR hardware where users can see " +
                "and interact with digital elements in the physical world around them.\n\n" +
#if !UNITY_2017_2_OR_NEWER
                "<color=#ff0000ff><b>Warning!</b></color> Occluded Devices are only supported in Unity 2017.2 and newer and cannot be enabled.\n\n" +
#endif
                "<color=#ffff00ff><b>Note:</b></color> If you're not targeting Occluded devices, It's generally recommended that Transparent devices use " +
                "the lowest default quality setting, and is set automatically for you. This can be manually changed in your the Project's Quality Settings.";

            Names[ProjectSetting.SharingServices] = "Enable Sharing Services";
            Descriptions[ProjectSetting.SharingServices] =
                "Enables the use of the Sharing Services in your project for all apps on any platform.\n\n" +
                "<color=#ffff00ff><b>Note:</b></color> Start the Sharing Server via 'Mixed Reality Toolkit/Sharing Service/Launch Sharing Service'.\n\n" +
                "<color=#ffff00ff><b>Note:</b></color> The InternetClientServer and PrivateNetworkClientServer capabilities will be enabled in the " +
                "appx manifest for you.";

            Names[ProjectSetting.UseInputManagerAxes] = "Use Toolkit-specific InputManager axes";
            Descriptions[ProjectSetting.UseInputManagerAxes] =
                "Enables the use of the Xbox Controller for all apps on any platform.\n\n" +
                "To remove the added axes, simply disable this setting.\n\n" +
                "<color=#ffff00ff><b>Note:</b></color> The HoloLens platform target requires the HID capability to be defined in the appx manifest. " +
                "This capability is automatically enabled for you if you select this setting, \"Enable XR\", and don't select \"Target Occluded Devices\".";

            Names[ProjectSetting.DotNetScriptingBackend] = "Enable .NET scripting backend";
            Descriptions[ProjectSetting.DotNetScriptingBackend] =
                "Recommended\n\n" +
                "If you have the .NET unity module installed this will update the backend scripting profile, otherwise the scripting backend will be IL2CPP.";
        }

        protected override void OnEnable()
        {
            base.OnEnable();

#if UNITY_2017_1_OR_NEWER
            AutoConfigureMenu.ActiveBuildTargetChanged += UpdateSettings;
#endif

            minSize = new Vector2(350, 350);
            maxSize = minSize;
        }

        private void OnDisable()
        {
            if (inputManagerAsset != null)
            {
                inputManagerAsset.Dispose();
            }
        }

        #endregion // Overrides / Event Handlers

        private void AddAxis(InputManagerAxis axis)
        {
            SerializedProperty axesProperty = inputManagerAsset.FindProperty("m_Axes");

            // Creates a new axis by incrementing the size of the m_Axes array.
            axesProperty.arraySize++;

            // Get the new axis be querying for the last array element.
            SerializedProperty axisProperty = axesProperty.GetArrayElementAtIndex(axesProperty.arraySize - 1);

            // Iterate through all the properties of the new axis.
            while (axisProperty.Next(true))
            {
                switch (axisProperty.name)
                {
                    case "m_Name":
                        axisProperty.stringValue = axis.Name;
                        break;
                    case "descriptiveName":
                        axisProperty.stringValue = axis.DescriptiveName;
                        break;
                    case "descriptiveNegativeName":
                        axisProperty.stringValue = axis.DescriptiveNegativeName;
                        break;
                    case "negativeButton":
                        axisProperty.stringValue = axis.NegativeButton;
                        break;
                    case "positiveButton":
                        axisProperty.stringValue = axis.PositiveButton;
                        break;
                    case "altNegativeButton":
                        axisProperty.stringValue = axis.AltNegativeButton;
                        break;
                    case "altPositiveButton":
                        axisProperty.stringValue = axis.AltPositiveButton;
                        break;
                    case "gravity":
                        axisProperty.floatValue = axis.Gravity;
                        break;
                    case "dead":
                        axisProperty.floatValue = axis.Dead;
                        break;
                    case "sensitivity":
                        axisProperty.floatValue = axis.Sensitivity;
                        break;
                    case "snap":
                        axisProperty.boolValue = axis.Snap;
                        break;
                    case "invert":
                        axisProperty.boolValue = axis.Invert;
                        break;
                    case "type":
                        axisProperty.intValue = (int)axis.Type;
                        break;
                    case "axis":
                        axisProperty.intValue = axis.Axis - 1;
                        break;
                    case "joyNum":
                        axisProperty.intValue = axis.JoyNum;
                        break;
                }
            }
        }

        private void RemoveAxis(string axis)
        {
            SerializedProperty axesProperty = inputManagerAsset.FindProperty("m_Axes");

            // This loop accounts for multiple axes with the same name.
            while (axisNames.Contains(axis))
            {
                int index = axisNames.IndexOf(axis);
                axesProperty.DeleteArrayElementAtIndex(index);
                axisNames.RemoveAt(index);
            }
        }

        /// <summary>
        /// Checks our local cache of axis names to see if an axis exists. This cache is refreshed if it's empty or if InputManager.asset has been changed.
        /// </summary>
        private bool DoesAxisNameExist(string axisName)
        {
            if (axisNames.Count == 0 || inputManagerAsset.UpdateIfRequiredOrScript())
            {
                RefreshLocalAxesList();
            }

            return axisNames.Contains(axisName);
        }

        /// <summary>
        /// Clears our local cache, then refills it by iterating through the m_Axes arrays and storing the display names.
        /// </summary>
        private void RefreshLocalAxesList()
        {
            axisNames.Clear();

            SerializedProperty axesProperty = inputManagerAsset.FindProperty("m_Axes");

            for (int i = 0; i < axesProperty.arraySize; i++)
            {
                axisNames.Add(axesProperty.GetArrayElementAtIndex(i).displayName);
            }
        }
    }
}
