using Celeste.Mod.LakeSideCode.Entities;
using Celeste.Mod.LakeSideCode.FishDefs;
using Monocle;
using System;
using System.Collections;
using System.Collections.Generic;

namespace Celeste.Mod.LakeSideCode;

public class LakeSideCodeModuleSession : EverestModuleSession {

	/// <summary>
	/// Count of fish catches by type. Key not in dictionary = not discovered.
	/// </summary>
	public Dictionary<FishType, int> CatchCounter { get; private set; } = [];

	/// <summary>
	/// The type of fish Madeline is currently holding in her inventory
	/// </summary>
	public FishType Inventory { get; private set; } = FishType.Nothing;

	/// <summary>
	/// Whether the player has picked up the fishing rod
	/// </summary>
	public bool HasFishingRod { get; internal set; }

	public float FishDiscoveryFade = 0.0f;

	public FishType FishDiscoveryFish = FishType.Nothing;

	[Command("give_fish", "gives the player a fish")]
	public static void CmdGiveFish(string fishType) {
		LakeSideCodeModule.Session.FishWasCaught((FishType) Enum.Parse(typeof(FishType), fishType, true), Engine.Scene.Tracker.GetEntity<Player>().level);
	}

	public void fadeIn(Level level) {
		Tween tween = Tween.Create(Tween.TweenMode.Oneshot, Ease.CubeInOut, 1.5f, true);

		Entity tweenThing = new Entity();

		tween.OnUpdate = t => {
			FishDiscoveryFade = t.Eased;

			Logger.Log(LogLevel.Info, "f", FishDiscoveryFade.ToString());

			level.CanRetry = false;
		};

		tween.OnComplete = t => {
			FishDiscoveryFade = 0.0f;
			FishDiscoveryFish = FishType.Nothing;
			tweenThing.RemoveSelf();

			level.CanRetry = true;
		};

		tweenThing.Add(tween);
		level.Add(tweenThing);
	}


	/// <summary>
	/// To be called when player catches a fish.
	/// </summary>
	/// <param name="type">The type of fish that was caught</param>
	/// <returns>The type of fish that was ejected from the inventory to make space for the new one</returns>
	public FishType FishWasCaught(FishType type, Level level) {
		level.Add(new NewfishPopup(type));

		if (type == FishType.Nothing) return FishType.Nothing;
		if (type == FishType.Coin) {
			Audio.Play("event:/game/07_summit/checkpoint_confetti");

			FishDiscoveryFish = type;
			fadeIn(level);

			return PickUpFish(type);
		}

		if (CatchCounter.ContainsKey(type)) {
			// Caught another of something we already have
			CatchCounter[type] += 1;
			Audio.Play("event:/game/07_summit/checkpoint_confetti");
		}
		else {
			FishDiscoveryFish = type;
			fadeIn(level);

			// Caught a new type of fish
			Audio.Play("event:/classic/sfx55");
			Session session = level.Session;
			CatchCounter.Add(type, 1);
			switch(CatchCounter.Keys.Count) {
				default:
					break;
				case 1:  // first fish
					session.SetFlag("LS_FirstFish");
					session.Audio.Music.Param("LakeSide_MusicStart", 1);
					session.Audio.Apply(false);
					break;
				case 3:  // sunrise -> daytime
					session.SetFlag("LS_Sunrise", false);
					session.SetFlag("LS_Daytime");
					session.SetFlag("LS_Catchable_Daytime");
					session.Audio.Music.Param("LakeSide_MusicDaytime", 1);
					session.Audio.Ambience.Event = "event:/Lakeside_SFXDaytime";
					session.Audio.Apply(false);
					level.NextColorGrade("LS_Daytime", 0.5f);
					break;
				case 6:  // daytime -> evening
					session.SetFlag("LS_Daytime", false);
					session.SetFlag("LS_Evening");
					session.SetFlag("LS_MistEvening");
					session.SetFlag("LS_Catchable_Evening");
					session.SetFlag("LS_Rain");
					session.Audio.Music.Param("LakeSide_MusicEvening", 1);
					session.Audio.Ambience.Event = "event:/Lakeside_SFXEvening";
					session.Audio.Apply(false);
					level.NextColorGrade("LS_Evening", 0.5f);
					break;
				case 9:  // evening -> sunset
					session.SetFlag("LS_Evening", false);
					session.SetFlag("LS_MistEvening", false);
					session.SetFlag("LS_Rain", false);
					session.SetFlag("LS_Sunset");
					session.SetFlag("LS_MistSunset");
					session.SetFlag("LS_Catchable_Sunset");
					session.Audio.Music.Param("LakeSide_MusicSunset", 1);
					session.Audio.Ambience.Event = "event:/Lakeside_SFXSunset";
					session.Audio.Apply(false);
					level.NextColorGrade("LS_Sunset", 0.5f);
					break;
				case 12:  // sunset -> night
					session.SetFlag("LS_Sunset", false);
					session.SetFlag("LS_MistSunset", false);
					session.SetFlag("LS_Nightime");
					session.SetFlag("LS_MistNightime");
					session.SetFlag("LS_Catchable_Nighttime");
					session.Audio.Music.Param("LakeSide_MusicNightime", 1);
					session.Audio.Ambience.Event = "event:/Lakeside_SFXNightime";
					session.Audio.Apply(false);
					level.NextColorGrade("LS_Nightime", 0.5f);
					Entity fader = new();
					fader.Add(new Coroutine(DarknessFadeCorou(level, fader, level.Lighting.Alpha + 0.12f, 0.5f)));
					fader.AddTag(Tags.Global | Tags.Persistent);
					level.Add(fader);
					break;
			}
		}
		// Actually add to inventory
		return PickUpFish(type);
	}

	private static IEnumerator DarknessFadeCorou(Level level, Entity ent, float to, float speed) {
		while (level.Lighting.Alpha != to) {
			yield return null;
			level.Lighting.Alpha = Calc.Approach(level.Lighting.Alpha, to, Engine.DeltaTime * speed);
			level.Session.LightingAlphaAdd = level.Lighting.Alpha - level.BaseLightingAlpha;
		}
		ent.RemoveSelf();
	}

	internal FishType EmptyInventory() => PickUpFish(FishType.Nothing);

	internal FishType PickUpFish(FishType type) {
		FishType ret = Inventory;
		Inventory = type;
		return ret;
	}
}