using Godot;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using StarSkyPresenter;
using YamlDotNet.Serialization;
using FileAccess = System.IO.FileAccess;

public partial class MainScript : Node2D
{

	private int _scene = 1;
	private bool _configIsLoaded = false;
	
	private Dictionary<string, Star> _stars = new Dictionary<string, Star>();
	private List<Scene> _scenes = new List<Scene>();
	private TextureRect _background = null;
	
	// Called when the node enters the scene tree for the first time.
	public override void _Ready()
	{
	}

	// Called every frame. 'delta' is the elapsed time since the previous frame.
	public override void _Process(double delta)
	{
	}

	
	public void SetFullScreen()
	{
		Window window = GetNode<Window>("Projection");
		GD.Print("Window: ", window);
		window.SetCurrentScreen(1);
		window.SetSize(new Vector2I(1920, 1080));
		
		DisplayServer.WindowSetMode(DisplayServer.WindowMode.ExclusiveFullscreen, window.GetWindowId());
	}

	public List<Star> GetStars()
	{
		return this._stars.Values.ToList();
	}

	public List<Scene> GetScenes()
	{
		return this._scenes;
	}

	public int GetScene()
	{
		return this._scene;
	}
	
	public void SetScene(int scene)
	{
		this._scene = scene;
	}

	public void UpdateConfig()
	{
		// check if config is already loaded
		if (this._configIsLoaded)
		{
			return;
		}
		
		// Open the config file
		FileStream configFile = File.Open("config.yaml", FileMode.Open, FileAccess.Read);
		FileStream scenesFile = File.Open("scenes.yaml", FileMode.Open, FileAccess.Read);
		
		Window projection = this.GetNode<Window>("/root/Main/Projection");
		
		// deserialize the values
		IDeserializer deser = new DeserializerBuilder().Build();
		Objects obj_conf = deser.Deserialize<Objects>(new StreamReader(configFile));
		List<SceneConfig> scene_conf = deser.Deserialize<List<SceneConfig>>(new StreamReader(scenesFile));

		this._stars.Clear();
		
		foreach (StarConfig star_config in obj_conf.stars)
		{
			Sprite2D sprite = new Sprite2D();
			Image image = Image.LoadFromFile("resources/" + star_config.image);
			ImageTexture texture = ImageTexture.CreateFromImage(image);
			sprite.SetTexture(texture);
			sprite.Position = new Vector2(star_config.x, star_config.y);
			
			Star star = new Star
			{
				Id = star_config.id,
				X = star_config.x,
				Y = star_config.y,
				Rot =  star_config.rot,
				Scale =  star_config.scale,
				Sprite = sprite
			};
			
			this._stars.Add(star_config.id, star);
		}

		var viewport = GetNode<Window>("/root/Projection").GetViewport();
		viewport.ScreenSpaceAA = Viewport.ScreenSpaceAAEnum.Smaa;
		viewport.SetMsaa2D(Viewport.Msaa.Msaa4X);
		
		var _background = projection.GetNode<Node>("Background");
		
		foreach (Node child in projection.GetChildren())
		{
			if (child == _background) continue;
			projection.RemoveChild(child);
			child.QueueFree();
		}
		
		
		
		foreach (KeyValuePair<string, Star> star in this._stars)
		{
			projection.AddChild(star.Value.Sprite);
			star.Value.Sprite.ZIndex = 5;
		}

		List<Scene> scenes = new List<Scene>();
		foreach (var scene in scene_conf)
		{
			Dictionary<string, Star> namedStars = new Dictionary<string, Star>();
			foreach (KeyValuePair<string, Star> kv in this._stars)
			{
				Star star;
				// check whether a given object is overridden in this scene
				if (scene.stars.ContainsKey(kv.Key))
				{
					// compute the combination of the scene with changes applied
					var sceneStar = scene.stars[kv.Key];
					star = new Star
					{
						Id = kv.Value.Id,
						X = sceneStar.x ?? kv.Value.X,
						Y = sceneStar.y ?? kv.Value.Y,
						Rot = sceneStar.rot ?? kv.Value.Rot,
						Scale = sceneStar.scale ?? kv.Value.Scale,
						Sprite = (Sprite2D)kv.Value.Sprite.Duplicate(),
					};
				}
				else
				{
					// copy the original if not
					star = new Star
					{
						Id = kv.Value.Id,
						X = kv.Value.X,
						Y = kv.Value.Y,
						Rot = kv.Value.Rot,
						Scale = kv.Value.Scale,
						Sprite = (Sprite2D)kv.Value.Sprite.Duplicate()
					};
				}

				namedStars.Add(kv.Key, star);
			}
			
			scenes.Add(new Scene
			{
				transition = scene.transition,
				stars = namedStars
			});
		}
		GD.Print(scenes.Count);
		
		configFile.Close();
		scenesFile.Close();
		
		this._configIsLoaded = true;
		this._scenes = scenes;
	}
}
