using UnityEditor;
using UnityEngine;
using AnimatorAsCode.V1;
using System.Collections.Generic;
using gay.lilyy.aaccore;
using gay.lilyy.aacshared.runtimecomponents;
using gay.lilyy.Common;

namespace gay.lilyy.aacshared.layers
{
    /**
    <summary>
        ported from v5
    </summary>
    */
    [InitializeOnLoad]
    public class LanternFlicker : LayerGroup
    {
        private static readonly LanternFlicker _instance = new();

        static LanternFlicker() { }

        public override string DisplayName => "Lantern Flicker";
        public override string SystemName => "lantern_flicker";
        public override bool shouldWarnIfNotApplicable => false;

        private Transform GetLantern(AACAssets assets)
        {
            return AACUtils.FindChildRecursive(assets.ctx.AvatarRootTransform, "Lantern Floater");
        }

        public override bool IsApplicable(AACAssets assets)
        {
            var definition = ComponentHelper.GetComponentInChildrenWithError<LanternFlickerDefinition>(assets.ctx.AvatarRootObject);
            if (definition == null) return false;
            if (!definition.enabled) return false;
            var lamp = GetLantern(assets);
            if (lamp == null)
            {
                Logger.LogWarning("Lamp Light could not be found!");
                return false;
            }
            var light = lamp.GetComponentInChildren<Light>();
            return light != null;
        }

        private void CreateFlickerLayer(AACAssets assets)
        {
            var lamp = GetLantern(assets);
            var light = lamp.GetComponentInChildren<Light>();

            var layer = assets.fx.NewLayer("Lantern Flicker");
            var flickerParam = layer.BoolParameter("LanternFlicker");

            var off = layer.NewState("Off").WithAnimation(
                assets.aac.NewClip().Animating(anim =>
                {
                    if (light != null) {
                        anim.Animates(light, "m_Enabled").WithFrameCountUnit(sec =>
                        {
                            sec.Constant(0, 0);
                        });
                        anim.Animates(light, "m_Intensity").WithFrameCountUnit(sec =>
                        {
                            sec.Constant(0, light.intensity);
                        });
                    }
                })
            );

            layer.WithDefaultState(off);

            var on = layer.NewState("Flicker").WithAnimation(
                assets.aac.NewClip().Animating(anim =>
                {
                    AnimateLantern(assets, anim, lamp);
                }).Looping()
            );

            off.TransitionsTo(on).When(flickerParam.IsEqualTo(true));
            on.TransitionsTo(off).When(flickerParam.IsEqualTo(false));
        }

        private void CreateBrightnessLayer(AACAssets assets)
        {
            var lamp = GetLantern(assets);
            var light = lamp.GetComponentInChildren<Light>();
            if (light == null) return;

            var layer = assets.fx.NewLayer("Lantern Brightness");
            var brightnessParam = layer.FloatParameter("LanternBrightness");

            var state = layer.NewState("Brightness").WithAnimation(
                assets.aac.NewClip().Animating(anim =>
                {
                    anim.Animates(light, "m_Enabled").WithUnit(AacFlUnit.Frames, sec =>
                    {
                        sec.Linear(0, 0);
                        sec.Linear(1, 1);
                    });
                    anim.AnimatesColor(light, "m_Color").WithUnit(AacFlUnit.Frames, sec =>
                    {
                        sec.Linear(0, light.color * 0);
                        sec.Linear(100, light.color);
                    });
                })
            ).WithMotionTime(brightnessParam);
        }

        private void AnimateLantern(AACAssets assets, AacFlEditClip anim, Transform lantern)
        {
            var renderer = lantern.GetComponent<MeshRenderer>();
            var light = lantern.GetComponentInChildren<Light>();
            if (renderer == null)
            {
                Logger.LogWarning("AnimateLantern: Missing renderer on lantern");
                return;
            }


            var rng = new System.Random("lillithlanternrandomseed".GetHashCode());

            var keyframes = new List<(float time, double value)>();
            float time = 0;
            while (time < 60)
            {
                double value = rng.NextDouble();
                keyframes.Add((time, value));

                time += (float)(rng.NextDouble() * (0.3 - 0.05) + 0.05);
            }

            if (keyframes.Count == 0)
            {
                Logger.LogWarning("AnimateLantern: No keyframes generated");
            }

            if (light != null)
            {
                anim.Animates(light, "m_Intensity").WithSecondsUnit(sec =>
                {
                    foreach (var (t, v) in keyframes)
                    {
                        sec.Easing(t, (float)v * light.intensity);
                    }
                });
            }
            if (assets.isPC) {
                Logger.LogInfo("Using PC material for lantern flicker");
                Color initialColor = renderer.sharedMaterial.GetColor("_Emission_Color");
                // ValueFactory Orbits
                var prop = "material._Emission_Color";
                void animateChannel(string suffix, float initial)
                {
                    anim.Animates(renderer, $"{prop}.{suffix}").WithSecondsUnit(sec =>
                    {
                        foreach (var (t, v) in keyframes)
                        {
                            sec.Easing(t, initial * (float)v);
                        }
                    });
                }
                animateChannel("x", initialColor.r);
                animateChannel("y", initialColor.g);
                animateChannel("z", initialColor.b);
                animateChannel("w", initialColor.a);
            }
            else
            {
                // Toon Standard
                Logger.LogInfo("Using Non-PC material for lantern flicker");

                // float initialStrength = renderer.sharedMaterial.GetFloat("_EmissionStrength");
                // this still isnt running before vqt for some cursed reason so im just gonna hardcode this
                var initialStrength = 2;
                anim.Animates(renderer, "material._EmissionStrength").WithSecondsUnit(sec =>
                {
                    foreach (var (t, v) in keyframes)
                    {
                        sec.Easing(t, initialStrength * (float)v);
                    }
                });
            }
        }

        public override void Run(AACAssets assets)
        {
            CreateFlickerLayer(assets);
            CreateBrightnessLayer(assets);
        }
    }
}
