using System;
using System.Collections;
using System.Collections.Generic;
using Lofelt.NiceVibrations;
using UnityEngine;

namespace OCES.Haptic
{
    public class HapticSystem : MonoBehaviour
    {
        public static HapticSystem Instance {get; private set;}
        [NonSerialized]
        public bool IsHapticSupported = DeviceCapabilities.isVersionSupported;
        [NonSerialized]
        public bool IsMeetsAdvanceRequirements = DeviceCapabilities.meetsAdvancedRequirements;

        HapticObjectConfig m_hapticObjects;
        const string k_hapticConfigPath = "HapticData/";
        const string k_hapticResourcesPath = "Haptics/";
        
        public void Play(uint hapticId, bool isDirectCall = true)
        {
            HapticObject hapticObject = this.m_hapticObjects.QueryById(hapticId);
            if (hapticObject != null)
            {
                Play(hapticObject);
                if (isDirectCall)
                {
                    Debug.LogWarning($"[Haptic System] Playing haptic id {hapticId} without play audio." + 
                                     "This method should only be called during debugging.");
                }
            }
            else
            {
                Debug.LogError("[Haptic System] Could not find Haptic Object with id: " + hapticId);
            }

        }

        public void Play(HapticObject hapticObject)
        {
            switch (hapticObject.Type)
            {
                case HapticType.Preset:
                    if (Enum.TryParse(hapticObject.Payload, out HapticPatterns.PresetType hapticPattern))
                    {
                        HapticPatterns.PlayPreset(hapticPattern);
                    }
                    else
                    {
                        Debug.LogError($"[Haptic System] Could not parse haptic pattern: {hapticObject.Payload}]");
                    }
                    break;
                case HapticType.Emphasis:
                    if (hapticObject.Amplitude < 0f || hapticObject.Frequency < 0f)
                    {
                        Debug.LogWarning($"[Haptic System] Haptic {hapticObject.Id} have no amplitude or frequency." +
                                         "Please check the datatable.");
                        break;
                    }
                    HapticPatterns.PlayEmphasis(hapticObject.Amplitude, hapticObject.Frequency);
                    break;
                case HapticType.Constant:
                    if (hapticObject.Amplitude < 0f || hapticObject.Frequency < 0f || hapticObject.Duration < 0f)
                    {
                        Debug.LogWarning($"[Haptic System] Haptic {hapticObject.Id} have no amplitude, frequency or duration." +
                                         "Please check the datatable.");
                        break;
                    }
                    HapticController.Stop();
                    HapticPatterns.PlayConstant(hapticObject.Amplitude, hapticObject.Frequency, hapticObject.Duration);
                    break;
                case HapticType.Advance:
                    if (Enum.TryParse(hapticObject.FallbackPreset, out HapticPatterns.PresetType fallbackPreset))
                    {
                        HapticController.fallbackPreset = fallbackPreset;
                        HapticClip hapticClip = GetHapticClip(hapticObject);
                        if (hapticClip)
                        {
                            HapticController.Stop();
                            HapticController.Play(hapticClip);
                        }
                        else
                        {
                            Debug.LogError($"[Haptic System] Could not parse haptic file: {hapticObject.Payload}");
                        }
                    }
                    else
                    {
                        Debug.LogError($"[Haptic System] Could not parse fallback preset: {hapticObject.FallbackPreset}]");
                    }
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
                
        }

        public void Stop()
        {
            HapticController.Stop();
        }
        
        void Awake()
        {
            if (Instance && Instance != this)
            {
                Destroy(gameObject);
                return;
            }
            Instance = this;
            DontDestroyOnLoad(gameObject);

            this.m_hapticObjects = HapticConfigLoader.Load<HapticObjectConfig>(k_hapticConfigPath + "HapticObject");
        }

        static HapticClip GetHapticClip(HapticObject hapticObject)
        {
            return Resources.Load<HapticClip>(k_hapticResourcesPath + hapticObject.Payload);
        }

        static class HapticConfigLoader
        {
            internal static T Load<T>(string tableName) where T : IBinarySerializable, new()
            {
                TextAsset bytes = Resources.Load<TextAsset>(tableName);
                if (!bytes)
                    Debug.LogError($"未找到表 {tableName}");
                IBinarySerializable data = new T();
                bool readOk = FileManager.ReadBinaryDataFromBytes(bytes.bytes, ref data);
                if (readOk)
                    return (T)data;
                Debug.LogError($"{tableName} 解析出错，类型 {typeof(T)}");
                return default;
            }
        }
    }
}
