using Godot;
using System.Collections.Generic;

public partial class CodingWindow : PanelContainer
{
	private Robot robot;

	[Export] VBoxContainer codeBlocks;
	[Export] VBoxContainer editorWindow;
	[Export] OptionButton availableScripts;
	[Export] LineEdit scriptName;
	[Export] LineEdit nameInput;

	public Dictionary<ProgramNode, PackedScene> DSLNodes;

	public override void _Ready()
	{
		DSLNodes = ResourceLoader.LoadDSLNodes();
		GenerateCodingBlocks();
	}

	public override void _Notification(int id)
	{
		if (id == NotificationVisibilityChanged)
		{
			if (Visible) LoadWindow();
		}
	}

	private void LoadWindow()
	{
		if (robot == null) return;

		nameInput.Text = robot.Name;
		SetupScriptOptions();
		ClearWindow();
	}

	private void SetupScriptOptions()
	{
		availableScripts.Clear();
		availableScripts.AddItem("Select script to load...");
		List<string> scripts = FileHandler.LoadProgramNames();
		scripts.Sort((a, b) => a.CompareTo(b));
		foreach (string script in scripts)
		{
			availableScripts.AddItem(script);
		}
	}

	public void SaveRobotName()
	{
		if (robot == null) return;

		robot.Name = nameInput.Text;
	}

	public void CloseWindow()
	{
		Hide();
	}

	public void GenerateCodingBlocks()
	{
		NodeDisplay nodeDisplay;
		foreach (ProgramNode nodeTemplate in DSLNodes.Keys)
		{
			nodeDisplay = DSLNodes[nodeTemplate].Instantiate<NodeDisplay>();
			nodeDisplay.node = nodeTemplate;
			codeBlocks.AddChild(nodeDisplay);
			nodeDisplay.ShowListDisplay();
			nodeDisplay.listDisplay.Pressed += () =>
			{
				AddEditorNode(DSLNodes[nodeTemplate], nodeTemplate.Duplicate());
			};
		}
	}

	private void AddEditorNode(PackedScene prefab, ProgramNode node)
	{
		NodeDisplay editorDisplay = prefab.Instantiate<NodeDisplay>();
		editorDisplay.node = node;
		editorWindow.AddChild(editorDisplay);
		editorDisplay.ShowEditorDisplay();
		editorDisplay.OnDeleteNode += () =>
		{
			editorWindow.RemoveChild(editorDisplay);
			editorDisplay.QueueFree();
		};
	}

	public void ClearWindow()
	{
		foreach (Node node in editorWindow.GetChildren())
		{
			editorWindow.RemoveChild(node);
			node.QueueFree();
		}
		scriptName.Text = "";
	}

	public void CompileProgram()
	{
		List<ProgramNode> nodes = new List<ProgramNode>();

		for (int i = 0; i < editorWindow.GetChildCount(); i++)
		{
			NodeDisplay nodeDisplay = editorWindow.GetChild<NodeDisplay>(i);
			nodeDisplay.node.ReadParameters(nodeDisplay);
			nodes.Add(nodeDisplay.node.Duplicate());
			if (i != 0)
			{
				nodes[i - 1].nextNode = nodes[i];
			}
		}

		if (robot == null) return;
		if (nodes.Count > 0) robot.SetupExecution(nodes);
		robot.currentProgram = scriptName.Text.Length <= 0 ? $"Script{availableScripts.ItemCount}" : scriptName.Text;
	}

	public void SetRobot(Robot robot)
	{
		this.robot = robot;
	}

	public void LoadProgram(int index)
	{
		ClearWindow();
		string scriptContent = FileHandler.LoadProgram(availableScripts.GetItemText(index));
		string[] nodes = scriptContent.Split(";");
		foreach (string node in nodes)
		{
			NodeDisplay nodeDisplay = NodeDisplay.Load(node, DSLNodes);
			if (nodeDisplay != null)
			{
				editorWindow.AddChild(nodeDisplay);
				nodeDisplay.ShowEditorDisplay();
				nodeDisplay.OnDeleteNode += () =>
				{
					editorWindow.RemoveChild(nodeDisplay);
					nodeDisplay.QueueFree();
				};
			}
		}
		scriptName.Text = availableScripts.GetItemText(index);
		availableScripts.Select(0);
	}

	public void SaveProgram()
	{
		string result = "";
		for (int i = 0; i < editorWindow.GetChildCount(); i++)
		{
			NodeDisplay nodeDisplay = editorWindow.GetChild<NodeDisplay>(i);
			nodeDisplay.node.ReadParameters(nodeDisplay);
			result += nodeDisplay.node.Save();
			result += ";\r\n";
		}
		if (result.Length <= 0) return;
		string filename = scriptName.Text.Length <= 0 ? $"Script{availableScripts.ItemCount}" : scriptName.Text;
		FileHandler.SaveProgram(filename, result);
		SetupScriptOptions();
	}
}
