using Godot;
using Godot.Collections;
using Newlon.Systems.Effects;

namespace Newlon.Components;

[GlobalClass]
public partial class Entity : Node2D
{
	#region Health points
	[Export] public float MaxHP;
	public float HP;
	[Signal] public delegate void OnHPChangedEventHandler(float deltaHP, Node origin);
	[Signal] public delegate void OnDamagedEventHandler();

	public virtual void TakeDamage(float amount, Node origin)
	{
		HP -= amount;
		EmitSignal(SignalName.OnHPChanged, -amount, origin);
		EmitSignal(SignalName.OnDamaged);
		if (HP <= 0)
		{
			KillByDamage();
		}
	}

	public virtual void Heal(float amount, Node origin)
	{
		EmitSignal(SignalName.OnHPChanged, amount, origin);
		HP += amount;
		if (HP > MaxHP) HP = MaxHP;
	}

	public virtual void KillByDamage()
	{
		Kill();
	}

	public virtual void Kill()
	{
		QueueFree();
	}


	#endregion
	#region Brain
	[Export] private AnimationPlayer _player;
	[Export] private AnimationTree _tree;
	public virtual void DisableBrain()
	{
		if (_player != null)
			_player.ProcessMode = ProcessModeEnum.Always;
		if (_tree != null)
			_tree.ProcessMode = ProcessModeEnum.Always;
		ProcessMode = ProcessModeEnum.Disabled;
	}

	public virtual void EnableBrain()
	{
		if (_player != null)
			_player.ProcessMode = ProcessModeEnum.Inherit;
		if (_tree != null)
			_tree.ProcessMode = ProcessModeEnum.Inherit;
		ProcessMode = ProcessModeEnum.Inherit;
	}
	#endregion
	#region Effects
	[Export] private Array<Effect> _effectImmunities = new();
	[Export] private bool completeInvulnerability = false;
	private readonly Dictionary<string, Effect> _activeEffectSlots = new();
	private readonly Dictionary<string, Timer> _effectSlotTimers = new();

	[Signal] public delegate void EffectStartedEventHandler(Effect what);
	[Signal] public delegate void EffectEndedEventHandler(Effect what);
	[Signal] public delegate void EffectContinuedEventHandler(Effect what);


	public virtual void GiveEffect(Effect what)
	{
		if (_effectImmunities.Contains(what) || completeInvulnerability)
		{
			return;
		}

		string slot = what.Slot;
		if (_activeEffectSlots.ContainsKey(slot) == false)
		{
			InitSlot(slot);
		}

		if (what == _activeEffectSlots[slot])
		{
			EmitSignal(SignalName.EffectContinued, what);
		}
		else
		{
			EmitSignal(SignalName.EffectStarted, what);
		}

		if (_activeEffectSlots[slot] != null)
		{
			_effectSlotTimers[slot].Stop();
			_activeEffectSlots[slot].Exit(this);
		}
		_effectSlotTimers[slot].WaitTime = what.Duration;
		_effectSlotTimers[slot].Start();

		what.Enter(this);
		_activeEffectSlots[slot] = what;

	}

	private void InitSlot(string key)
	{
		_activeEffectSlots.Add(key, null);

		var timer = new Timer() { Autostart = false, OneShot = true };
		AddChild(timer);
		timer.Timeout += () => { EndEffectAtSlot(key); };

		_effectSlotTimers.Add(key, timer);

	}

	public void EndEffect(Effect what)
	{
		EndEffectAtSlot(what.Slot);
	}

	public void ProcessEffects()
	{
		foreach (string key in _activeEffectSlots.Keys)
			_activeEffectSlots[key]?.Process(this);
	}

	private void EndEffectAtSlot(string slot)
	{
		_activeEffectSlots[slot].Exit(this);
		_activeEffectSlots[slot] = null;

		EmitSignal(SignalName.EffectEnded, _activeEffectSlots[slot]);

	}
	#endregion
	#region LocalTimescale
	private float _localTimescale = 1.0f;
	[Signal] public delegate void OnLocalTimescaleChangedEventHandler(float scale);
	public float LocalTimescale
	{
		get => _localTimescale;
		set
		{
			_localTimescale = value;
			EmitSignal(SignalName.OnLocalTimescaleChanged, _localTimescale);
		}
	}
	#endregion
	#region Godot overrides
	public override void _Ready()
	{
		HP = MaxHP;
	}
	#endregion
}
