using Celeste.Mod.Entities;
using Microsoft.Xna.Framework;
using Monocle;
using System.Collections;

namespace Celeste.Mod.LakeSideCode.Entities
{
    [CustomEntity("LakeSideCode/StartDoor")]
    [Tracked]
    public class StartDoor : Solid
    {
        private static readonly MTexture DoorTex = GFX.Game["objects/Phobscodestuff/Lakesidecodestuff/startdoor"];
        private readonly Image Door;
        private readonly SoundSource sfx;
        private readonly string eventName;
        private readonly string flag;
        private readonly string endingFlag;
        private readonly string colorgradeA;
        private readonly string colorgradeB;
        private readonly float endAmbience;
        private bool inSequence;
        private readonly float duration;
        private Vector2 node;
        private Vector2 start;
        private bool paused;
        private readonly Coroutine routine;

        public bool FlagState
        {
            get
            {
                return !string.IsNullOrEmpty(flag) && Scene is Level level && level.Session.GetFlag(flag);
            }
        }
        public bool EndFlagState
        {
            get
            {
                return !string.IsNullOrEmpty(flag) && Scene is Level level && level.Session.GetFlag(endingFlag);
            }
        }

        public StartDoor(EntityData data, Vector2 offset) : base(data.Position + offset, DoorTex.Width, DoorTex.Height, false)
        {
            Add(sfx = new SoundSource());
            Add(Door = new Image(DoorTex));
            eventName = data.Attr("eventName");
            flag = data.Attr("flag");
            endingFlag = data.Attr("endFlag");
            colorgradeA = data.Attr("closedColorgrade");
            colorgradeB = data.Attr("openColorgrade");
            endAmbience = data.Float("endAmbience");
            duration = data.Float("duration", 4);
            start = Position;
            node = data.Nodes[0] + offset;
            Tag |= Tags.TransitionUpdate;
			TransitionListener listener = new() {
				OnOutBegin = OnOutBegin
			};
			Add(listener);
            SurfaceSoundIndex = 7;
        }
        public void OnOutBegin()
        {
            if (inSequence)
            {
                routine?.Cancel();
                SetState(true);
                if (Scene is not Level level) return;
                level.Session.SetFlag(endingFlag);
            }
        }
        public override void Added(Scene scene)
        {
            base.Added(scene);
            SetState(EndFlagState);
            if (!EndFlagState)
            {
                (scene as Level).Session.SetFlag(flag, false);
            }
        }
        public override void Update()
        {
            base.Update();
            if (Scene as Level is not Level level) return;
            if (inSequence)
            {
                if (!level.Paused && paused)
                {
                    sfx.Resume();
                    paused = false;
                }
                if (level.Paused && sfx.Playing)
                {
                    sfx.Pause();
                    paused = true;
                }
                return;
            }
            if (!EndFlagState && FlagState)
            {
                Add(new Coroutine(Sequence()));
            }
        }
        public void SetState(bool state)
        {
            if (state)
            {
                Audio.CurrentAmbienceEventInstance?.setVolume(endAmbience);
            }
            MoveTo(state ? node : start);
            if (Scene is Level level)
            {
                level.SnapColorGrade(state ? colorgradeB : colorgradeA);
            }
        }
        private IEnumerator Sequence()
        {
            if (EndFlagState || Scene is not Level level) yield break;
            if (level.Tracker.GetEntity<Player>() is not Player player) yield break;
            inSequence = true;
            MoveTo(start);
            Audio.CurrentAmbienceEventInstance.getVolume(out float volume, out _);
            PlaySound();
            level.NextColorGrade(colorgradeB, 1 / duration);
            for (float i = 0; i < 1; i += Engine.DeltaTime / duration)
            {
                if (player is null || player.Dead)
                {
                    level.Session.SetFlag(flag, false);
                }
                MoveTo(Vector2.Lerp(start, node, i));
                Audio.CurrentAmbienceEventInstance?.setVolume(Calc.LerpClamp(volume, endAmbience, i));
                yield return null;
            }
            level.Session.SetFlag(endingFlag);
            inSequence = false;
            yield return null;
        }
        public override void Render()
        {
            Door.DrawSimpleOutline();
            base.Render();
        }
        private void PlaySound()
        {
            if (!sfx.Playing)
            {
                sfx.Play(eventName);
            }
        }
    }
}