using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using Celeste.Mod.CherryHelper;
﻿using Celeste.Mod.Entities;
using Microsoft.Xna.Framework;
using Monocle;
using MonoMod.Utils;

namespace Celeste.Mod.DownTeam_AstralContest
{
    [CustomEntity("DownTeam_AstralContest/ModifiedShadowDashController")]
    [Tracked]
    class ModifiedShadowDashController : Entity
    {
        private static Vector2 oldPos;
        private static readonly FieldInfo shadowEndDelayField =
        typeof(CherryHelper.ShadowDashRefill).GetField(
            "shadowEndDelayCoroutine",
            BindingFlags.Static | BindingFlags.NonPublic);

        public ModifiedShadowDashController(EntityData data, Vector2 offset) : base(data.Position + offset)
        {
            Add(new TransitionListener
            {
                OnOutBegin = transitionOnOutBegin
            });
        }

        public static void Load()
        {
            On.Celeste.Player.DreamDashBegin += Player_DreamDashBegin;
            On.Celeste.Player.DreamDashUpdate += Player_DreamDashUpdate;
            On.Celeste.Player.DreamDashEnd += Player_DreamDashEnd;
            On.Celeste.Player.DreamDashedIntoSolid += Player_DreamDashedIntoSolid;
            On.Celeste.LedgeBlocker.DashCorrectCheck += LedgeBlocker_DashCorrectCheck;
        }

        public static void Unload()
        {
            On.Celeste.Player.DreamDashBegin -= Player_DreamDashBegin;
            On.Celeste.Player.DreamDashUpdate -= Player_DreamDashUpdate;
            On.Celeste.Player.DreamDashEnd -= Player_DreamDashEnd;
            On.Celeste.Player.DreamDashedIntoSolid -= Player_DreamDashedIntoSolid;
            On.Celeste.LedgeBlocker.DashCorrectCheck -= LedgeBlocker_DashCorrectCheck;
        }

        private static void Player_DreamDashBegin(On.Celeste.Player.orig_DreamDashBegin orig, Player player)
        {
            ModifiedShadowDashController controller = player.Scene.Tracker.GetEntity<ModifiedShadowDashController>();
            orig(player);
            if (controller is not null && CherryHelper.CherryHelper.Session.ShadowDashActive)
            {
                Coroutine coroutine = (Coroutine)shadowEndDelayField.GetValue(null);
                coroutine?.Cancel();
                coroutine?.RemoveSelf();
            }
            return;

        }

        //Capture the player's position to ensure parity with vanilla invincibility behavior
        private static int Player_DreamDashUpdate(On.Celeste.Player.orig_DreamDashUpdate orig, Player player)
        {
            oldPos = player.Position;
            return orig(player);
        }

        private static void Player_DreamDashEnd(On.Celeste.Player.orig_DreamDashEnd orig, Player player)
        {
            orig(player);
            ModifiedShadowDashController controller = player.Scene.Tracker.GetEntity<ModifiedShadowDashController>();
            if (controller is not null && CherryHelper.CherryHelper.Session.ShadowDashActive)
            {
                Coroutine coroutine = new Coroutine(MyShadowEndDelay());
                player.Add(coroutine);
                shadowEndDelayField.SetValue(null, coroutine);
            }
        }

        private static IEnumerator MyShadowEndDelay()
        {
            yield return 0.03f;
            CherryHelper.CherryHelper.Session.ShadowDashActive = false;
        }

        //Hook the solid collision out of a dream dash, and bounce the player as if they were invincible if they are shadow dashing
        private static bool Player_DreamDashedIntoSolid(On.Celeste.Player.orig_DreamDashedIntoSolid orig, Player player)
        {
            ModifiedShadowDashController controller = player.Scene.Tracker.GetEntity<ModifiedShadowDashController>();

            bool WillCollide = orig(player);

            if (controller is not null && WillCollide && CherryHelper.CherryHelper.Session.ShadowDashActive)
            {
                player.Position = oldPos;
                player.Speed *= -1;
                player.Play("event:/game/general/assist_dreamblockbounce");
                player.dreamDashCanEndTimer += Engine.DeltaTime;
                return false;
            }
            return WillCollide;

        }

        //Idk, I just copied this
        private void transitionOnOutBegin()
        {
            List<ModifiedShadowDashController> controllers = Scene.Entities.FindAll<ModifiedShadowDashController>();
        }

        //Re-enables dash correction in hazards while shadow dashing
        private static bool LedgeBlocker_DashCorrectCheck(On.Celeste.LedgeBlocker.orig_DashCorrectCheck orig, LedgeBlocker self, Player player)
        {
            ModifiedShadowDashController controller = player.Scene.Tracker.GetEntity<ModifiedShadowDashController>();
            if (controller is not null && CherryHelper.CherryHelper.Session.ShadowDashActive)
            {
                return false;
            }
            return orig(self, player);
        }

    }

}