using Godot;
using Godot.Collections;
using System.Collections.Generic;
using System.Text;

public partial class ResearchList : PanelContainer
{
	[Export] private GraphEdit researchGraph;
	private System.Collections.Generic.Dictionary<string, Vector2> nodePositions = new System.Collections.Generic.Dictionary<string, Vector2>();
	private bool hasArrangedNodes = false;

	private List<Research> currentResearch = new List<Research>();
	private List<Research> toDelete = new List<Research>();

	public override void _Ready()
	{
		RecalculateResearchStates();
		if (Visible) SetupGraph();
	}

    public override void _Process(double delta)
	{
		if(currentResearch.Count > 0) toDelete = new List<Research>();
		foreach (Research research in currentResearch)
		{
			if (research.Execute(delta) == ResearchResult.FINISHED)
			{
				toDelete.Add(research);
				RecalculateResearchStates();
				SetupGraph();
			}
		}
		foreach (Research delete in toDelete)
		{
			currentResearch.Remove(delete);
		}
	}

	public void SetupGraph()
	{
		RememberNodePositions();
		ClearGraph();
		CreateResearchNodes();
		CreateResearchConnections();

		if (!hasArrangedNodes)
		{
			researchGraph.ArrangeNodes();
			hasArrangedNodes = true;
		}
	}

	private void RememberNodePositions()
	{
		foreach (Node child in researchGraph.GetChildren())
		{
			GraphNode node = child as GraphNode;
			if (node == null) continue;

			nodePositions[node.Name] = node.PositionOffset;
		}
	}

	private void ClearGraph()
	{
		foreach (Dictionary connection in researchGraph.GetConnectionList())
		{
			researchGraph.DisconnectNode(
				connection["from_node"].AsStringName(),
				(int)connection["from_port"],
				connection["to_node"].AsStringName(),
				(int)connection["to_port"]
			);
		}

		foreach (Node child in researchGraph.GetChildren())
		{
			if (child is GraphNode)
			{
				researchGraph.RemoveChild(child);
				child.QueueFree();
			}
		}
	}

	private void CreateResearchNodes()
	{
		foreach (Research research in GameData.availableResearch.Values)
		{
			GraphNode node = CreateResearchNode(
				research.data.Id,
				research.data.Texture,
				research.state
			);

			researchGraph.AddChild(node);
		}
	}

	private void CreateResearchConnections()
	{
		foreach (Research research in GameData.availableResearch.Values)
		{
			string prerequisite = research.data.Research;
			string current = research.data.Id;

			if (string.IsNullOrEmpty(prerequisite)) continue;

			if (!researchGraph.HasNode(prerequisite) || !researchGraph.HasNode(current)) continue;

			researchGraph.ConnectNode(
				prerequisite,
				0,
				current,
				0
			);
		}
	}

	private GraphNode CreateResearchNode(string id, string texturePath, ResearchState state)
	{
		Texture2D texture = GD.Load<Texture2D>(texturePath);
		Color stateColor = GetColorByState(state);
		string tooltipText = GetResearchTooltip(GameData.availableResearch[id]);

		TextureRect icon = new TextureRect
		{
			Texture = texture,
			StretchMode = TextureRect.StretchModeEnum.KeepAspectCentered,
			CustomMinimumSize = new Vector2(64, 64),
			SelfModulate = stateColor
		};

		Button button = new Button
		{
			Text = "Research",
			Disabled = state != ResearchState.AVAILABLE,
			TooltipText = tooltipText
		};

		button.Pressed += () => OnResearchPressed(id);

		GraphNode node = new GraphNode
		{
			Name = id,
			Title = Research.GetReadableName(id),
			SelfModulate = stateColor,
			TooltipText = tooltipText
		};

		if (nodePositions.ContainsKey(id))
		{
			node.PositionOffset = nodePositions[id];
		}

		node.SetSlot(
			0,
			true,
			0,
			Colors.White,
			true,
			0,
			Colors.White
		);

		node.AddChild(icon);
		node.AddChild(button);

		return node;
	}

	private void OnResearchPressed(string id)
	{
		GameData.availableResearch[id].state = ResearchState.RESEARCHING;
		currentResearch.Add(GameData.availableResearch[id]);
		RecalculateResearchStates();
		SetupGraph();
	}

	private string GetResearchTooltip(Research research)
	{
		if (research.data.Effects == null || research.data.Effects.Count <= 0)
		{
			return Research.GetReadableName(research.data.Id);
		}

		StringBuilder tooltip = new StringBuilder(Research.GetReadableName(research.data.Id));
		tooltip.AppendLine();
		tooltip.AppendLine();
		tooltip.AppendLine("Effects:");

		foreach (ResearchEffect effect in research.data.Effects)
		{
			tooltip.Append("- ");
			tooltip.AppendLine(effect.GetDisplayText());
		}

		return tooltip.ToString();
	}

	private void RecalculateResearchStates()
	{
		bool changedState = true;

		while (changedState)
		{
			changedState = false;

			foreach (Research research in GameData.availableResearch.Values)
			{
				ResearchState newState = GetUpdatedResearchState(research);

				if (research.state == newState) continue;

				research.state = newState;
				changedState = true;
			}
		}
	}

	private ResearchState GetUpdatedResearchState(Research research)
	{
		if (research.state == ResearchState.RESEARCHED)
		{
			return ResearchState.RESEARCHED;
		}

		if (research.data.Research.Length <= 0)
		{
			return ResearchState.RESEARCHED;
		}

		if (research.state == ResearchState.RESEARCHING)
		{
			return ResearchState.RESEARCHING;
		}

		if (GameData.availableResearch[research.data.Research].state == ResearchState.RESEARCHED)
		{
			return ResearchState.AVAILABLE;
		}

		return ResearchState.LOCKED;
	}

	private Color GetColorByState(ResearchState state)
	{
		return state switch
		{
			ResearchState.AVAILABLE => Colors.White,
			ResearchState.LOCKED => new Color(0.6f, 0.6f, 0.6f, 0.5f),
			ResearchState.RESEARCHED => new Color(0.7f, 1f, 0.7f, 1f),
			ResearchState.RESEARCHING => new Color(0.3f, 1f, 0.3f, 1f),
			_ => Colors.Red
		};
	}
}
