using System.Collections.Generic;
using AnimatorAsCode.V1;
using AnimatorAsCode.V1.VRC;
using gay.lilyy.aaccore;
using gay.lilyy.aacshared.runtimecomponents;
using UnityEditor;
using UnityEngine;
using VRC.SDK3.Avatars.ScriptableObjects;

namespace gay.lilyy.aacshared.layers
{
    [InitializeOnLoad]
    public class ParameterPreset : LayerGroup
    {
        private static readonly ParameterPreset _instance = new();

        static ParameterPreset() { }

        public override string DisplayName => "Parameter Preset";
        public override string SystemName => "parameter_preset";
        public override bool shouldWarnIfNotApplicable => false;

        private static ParameterPresetDefinition[] getDefinitions(AACAssets assets)
        {
            return assets.ctx.AvatarRootObject.GetComponents<ParameterPresetDefinition>();
        }

        public override bool IsApplicable(AACAssets assets)
        {
            return getDefinitions(assets).Length > 0;
        }

        private void RunGroup(AACAssets assets, List<ParameterPresetDefinition> presets)
        {
            if (presets.Count == 0) return; // If there are none in the avatar, skip this entirely.

            var layer = assets.fx.NewLayer("Presets " + presets[0].Group);
            var presetParam = layer.IntParameter("Preset " + presets[0].Group);
            Dictionary<string, AacFlParameter<int>> intParams = new();
            Dictionary<string, AacFlParameter<float>> floatParams = new();
            Dictionary<string, AacFlParameter<bool>> boolParams = new();
            var idle = layer.NewState("Idle").WithAnimation(assets.emptyClip);
            layer.WithDefaultState(idle);

            var presetIndex = 0;
            foreach (var preset in presets)
            {
                presetIndex++;
                var state = layer.NewState("Preset " + preset.Name)
                    .WithAnimation(assets.emptyClip);

                foreach (var param in preset.Parameters)
                {
                    if (!param.shouldChange) continue;
                    switch (param.valueType)
                    {
                        case VRCExpressionParameters.ValueType.Int:
                            {
                                if (!intParams.ContainsKey(param.name))
                                {
                                    intParams[param.name] = layer.IntParameter(param.name);
                                }
                                var layerParam = intParams[param.name];
                                state.Drives(layerParam, (int)param.setTo);
                                break;
                            }
                        case VRCExpressionParameters.ValueType.Float:
                            {
                                if (!floatParams.ContainsKey(param.name))
                                {
                                    floatParams[param.name] = layer.FloatParameter(param.name);
                                }
                                var layerParam = floatParams[param.name];
                                state.Drives(layerParam, param.setTo);
                                break;
                            }
                        case VRCExpressionParameters.ValueType.Bool:
                            {
                                if (!boolParams.ContainsKey(param.name))
                                {
                                    boolParams[param.name] = layer.BoolParameter(param.name);
                                }
                                var layerParam = boolParams[param.name];
                                state.Drives(layerParam, param.setTo > 0.5f);
                                break;
                            }
                    }
                }

                idle.TransitionsTo(state).When(presetParam.IsEqualTo(presetIndex));
                state.TransitionsTo(idle).When(presetParam.IsEqualTo(0));
            }
        }
            

        public override void Run(AACAssets assets)
        {
            var presets = getDefinitions(assets);
            Dictionary<string, List<ParameterPresetDefinition>> groupedPresets = new();
            foreach (var preset in presets)
            {
                if (!groupedPresets.ContainsKey(preset.Group))
                {
                    groupedPresets[preset.Group] = new List<ParameterPresetDefinition>();
                }
                groupedPresets[preset.Group].Add(preset);
            }
            foreach (var group in groupedPresets)
            {
                RunGroup(assets, group.Value);
            }
        }
    }
}
