using System;
using System.Runtime.InteropServices;
using FMOD;
using Ktyl.Util;
using UnityEngine;
using INITFLAGS = FMOD.Studio.INITFLAGS;

[CreateAssetMenu(menuName = "Audio Visualizer Settings")]
public class AudioVisualizer : ScriptableObject
{
    [Serializable]
    private struct ShaderPropertyAnimation
    {
        public string name;
        public float multiplier;
        [UnityEngine.Range(0, 5)] 
        public int channel;
        [UnityEngine.Range(0, 2048)]
        public int sample;
        
        public float Initial { get; set; }
    }

    [SerializeField] private SerialFloat _distanceToNextBeat;
    [SerializeField] private SerialFloat _distanceSinceLastBeat;
    
    [SerializeField] private ShaderPropertyAnimation[] _fractalAnimations;
    [SerializeField] private ShaderPropertyAnimation[] _tunnelAnimations;
    [SerializeField] private ShaderPropertyAnimation[] _shipAnimations;
    [SerializeField] private AccessibilityOptions _accessibility;

    public void Initialise(Material fractal, Material tunnel, Material ship)
    {
        // _fractal = fractal;
        // _tunnel = tunnel;
        // _ship = ship;
        
        InitialiseAnimations(_fractalAnimations, fractal);
        InitialiseAnimations(_tunnelAnimations, tunnel);
        InitialiseAnimations(_shipAnimations, ship);
    }

    private void InitialiseAnimations(ShaderPropertyAnimation[] animations, Material material)
    {
        for (int i = 0; i < animations.Length; i++)
        {
            var anim = animations[i];
            anim.Initial = material.GetFloat(anim.name);
            animations[i] = anim;
        }
    }

    public void Update(DSP fft, Renderer fractal, Renderer tunnel, Renderer ship)
    {
        // update beat-based things
        Shader.SetGlobalFloat("_DistanceSinceLastBeat", _distanceSinceLastBeat);
        Shader.SetGlobalFloat("_DistanceToNextBeat", _distanceToNextBeat);
        
        fft.getParameterData((int) FMOD.DSP_FFT.SPECTRUMDATA, out var unmanagedData, out var length);
        var fftData = (FMOD.DSP_PARAMETER_FFT) Marshal.PtrToStructure(unmanagedData, typeof(FMOD.DSP_PARAMETER_FFT));
        
        var spectrum = fftData.spectrum;
        var l = spectrum.Length;
        if (l == 0) return;

        for (int i = 0; i < _fractalAnimations.Length; i++)
        {
            var anim = _fractalAnimations[i];
            var a = spectrum[anim.channel][anim.sample];
            
            fractal.material.SetFloat(anim.name, anim.Initial + anim.multiplier * a);
        }
        
        for (int i = 0; i < _tunnelAnimations.Length; i++)
        {
            var anim = _tunnelAnimations[i];
            var a = spectrum[anim.channel][anim.sample];
            
            tunnel.material.SetFloat(anim.name, anim.Initial + anim.multiplier * a);
        }

        UpdateAnimations(spectrum, _shipAnimations, ship);
    }

    private void UpdateAnimations(float[][] spectrum, ShaderPropertyAnimation[] animations, Renderer renderer)
    {
        for (int i = 0; i < animations.Length; i++)
        {
            var anim = animations[i];
            var a = spectrum[anim.channel][anim.sample];
            renderer.material.SetFloat(anim.name, anim.Initial + anim.multiplier * a);
        }
    }
}
