using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using MagicLeap.OpenXR.Constants;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine;
using UnityEngine.XR;
using UnityEngine.XR.MagicLeap;
using UnityEngine.XR.Management;
using UnityEngine.XR.OpenXR;
using UnityEngine.XR.OpenXR.Features;
using UnityEngine.XR.OpenXR.NativeTypes;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace MagicLeap.OpenXR
{
    public static class Utils
    {
        public static InputDevice FindMagicLeapController(InputDeviceCharacteristics inputDeviceCharacteristics)
        {
            List<InputDevice> devices = new List<InputDevice>();
            InputDevices.GetDevicesWithCharacteristics(inputDeviceCharacteristics, devices);
            
            
            
            for (int i = devices.Count - 1; i > 0; i--)
            {
                if (devices[i].name == "Magic Leap Controller OpenXR" && devices[i].isValid)
                {
                    return devices[i];
                }
            }
            return new InputDevice();
        }
        
        internal static bool TryGetOpenXRFeature<TFeature>(out TFeature feature) where TFeature : OpenXRFeature
        {
            feature = null;
            var settings = OpenXRSettings.Instance;
            if (settings == null)
                return false;
            feature = settings.GetFeature<TFeature>();
            return feature != null;
        }
        internal static bool TryGetOpenXRLoader(out OpenXRLoader loader)
        {
            loader = null;
            var settings = XRGeneralSettings.Instance;
            if (settings == null)
                return false;
            var mgr = settings.Manager;
            if (mgr == null)
                return false;
            loader = mgr.activeLoader as OpenXRLoader;
            return loader != null;
        }
        internal static bool TryGetSubsytemFromOpenXRLoader<TSubsystem>(out TSubsystem subsystem)
            where TSubsystem : class, ISubsystem
        {
            subsystem = null;
            if (!TryGetOpenXRLoader(out var loader))
                return false;
            subsystem = loader.GetLoadedSubsystem<TSubsystem>();
            return subsystem != null;
        }
        
        internal static bool DidXrCallSucceed(XrResult result, string functionName = "A native function", Predicate<XrResult> successCase = null, bool showError = true)
        {
            var success = successCase?.Invoke(result) ?? result == XrResult.Success;
            if (success || !showError)
            {
                return success;
            }
            var logResult = result.ToString();
            if (!Enum.IsDefined(typeof(XrResult), result))
            {
                logResult = ((MLXrResult)result).ToString();
            }
            MLPluginLog.ErrorFormat($"{functionName} in the Magic Leap API failed. Reason: {logResult} ");
            return false;
        }
        
        internal static Vector3 InvertZ(this Vector3 input)
        {
            return new Vector3(input.x, input.y, -input.z);
        }
        internal static Quaternion InvertXY(this Quaternion quaternion)
        {
            return new Quaternion(-quaternion.x, -quaternion.y, quaternion.z, quaternion.w);
        }
        
        internal static class OpenXRStructHelpers<T> where T : unmanaged 
        {
            
            internal static void Create<TType>(TType type, out T result, string typeFieldName = "Type", T defaultValue = default) 
                where TType: unmanaged
            {
                var nativeResult = defaultValue;
                unsafe
                {
                    var nativePtr = new IntPtr(&nativeResult);
                    var offset = Marshal.OffsetOf<T>(typeFieldName);
                    var sourceType = type;
                    UnsafeUtility.MemCpy((nativePtr + offset.ToInt32()).ToPointer(), &sourceType, sizeof(TType));
                    result = nativeResult;
                }
            }
        }
        
      
        
#if UNITY_EDITOR
        internal static string GetNiceTypeName(System.Type type)
            => UnityEditor.ObjectNames.NicifyVariableName(type.Name);
        internal static string GetNiceTypeName<T>()
            => GetNiceTypeName(typeof(T));
        
        private static OpenXRSettings GetSettings(BuildTargetGroup group)
            => OpenXRSettings.GetSettingsForBuildTargetGroup(group);
        private static OpenXRFeature GetFeatureForBuildTarget(BuildTargetGroup group, Type featureType)
        {
            var settings = GetSettings(group);
            if (settings == null)
                return null;
            return settings.GetFeature(featureType);
        }
        private static TFeature GetFeatureForBuildTarget<TFeature>(BuildTargetGroup group) where TFeature: OpenXRFeature
        {
            var settings = GetSettings(group);
            if (settings == null)
                return null;
            return settings.GetFeature<TFeature>();
        }
        internal static bool IsFeatureEnabled(BuildTargetGroup group, Type featureType)
        {
            var feature = GetFeatureForBuildTarget(group, featureType);
            return feature != null && feature.enabled;
        }
        
        internal static bool IsFeatureEnabled<TFeature>(BuildTargetGroup group) where TFeature: OpenXRFeature
        {
            var feature = GetFeatureForBuildTarget<TFeature>(group);
            return feature != null && feature.enabled;
        }
        internal static bool TryEnableFeature(BuildTargetGroup group, Type featureType)
        {
            var feature = GetFeatureForBuildTarget(group, featureType);
            return feature != null && (feature.enabled = true) || false;
        }
        internal static bool TryEnableFeature<TFeature>(BuildTargetGroup group) where TFeature : OpenXRFeature
        {
            var feature = GetFeatureForBuildTarget<TFeature>(group);
            return feature != null && (feature.enabled = true) || false;
        }
#endif 
    }
}