using Godot;
using System;
using System.Collections.Generic;
using System.Numerics;
using StarSkyPresenter;
using YamlDotNet.Serialization;
using Vector2 = Godot.Vector2;

public partial class Projection : Window
{
	private double _sceneTime = 0;
	private int _lastScene = 0;
	private int _activeScene = 0;
	
	
	// Called when the node enters the scene tree for the first time.
	public override void _Ready()
	{
		
	}

	private double Fapply(double input, string fname)
	{
		switch (fname)
		{
			case "lin": return input;
			case "sin": return Math.Sin(input);
			case "cos": return 1.0 - Math.Cos(input);
			case "sin2": return Math.Sin(input)*Math.Sin(input);
			default: return 0;
		}
	}

	private double FadeTime(bool fadeIn, bool fadeOut, double t)
	{
		switch ((fadeIn, fadeOut))
		{
			case (true, true): return Math.Sin(t)*Math.Sin(t);
			case (true, false): return 1 - Math.Cos(t);
			case (false, true): return Math.Sin(t);
			case (false, false): return t;
		}
	}

	// Called every frame. 'delta' is the elapsed time since the previous frame.
	public override void _Process(double delta)
	{
		// get MainScript instance
		MainScript mainScript = (MainScript)this.GetTree().GetRoot().GetNode<MainScript>("Main");
		if (mainScript.GetScenes().Count == 0) return;
		
		// check for scene update and if so reset the scene time
		if (mainScript.GetScene() != this._activeScene)
		{
			this._lastScene = this._activeScene;
			this._activeScene = mainScript.GetScene();
			this._sceneTime = 0;
		}
		else
		{
			this._sceneTime += delta;
		}
		
		// TODO: calculate scene weight
		var transition = mainScript.GetScenes()[this._lastScene].transition;
		string xFade = transition.x;
		string yFade = transition.y;
		double t = (Math.Clamp(this._sceneTime / transition.t, 0, 1)) * 3.14159265 / 2;
		double weight = FadeTime(transition.fade_out, transition.fade_in, t);
		Vector2 posWeight = new Vector2((float)Fapply(weight * 3.14159265 / 2, yFade), (float)Fapply(weight * 3.14159265 / 2, xFade));
		
		//GD.Print("T: " + t + ", weight: " + weight + ", posWeight: " + posWeight);
		
		// update background
		var background = this.GetNode<TextureRect>("Background");
		float scaleX = background.Texture.GetWidth() / (float)this.GetSize().X;
		float scaleY = background.Texture.GetHeight() / (float)this.GetSize().Y;
		background.Scale = Vector2.One / Math.Min(scaleX, scaleY);
		
		
		// update all the stars
		foreach (Star star in mainScript.GetStars())
		{
			var oldStar = mainScript.GetScenes()[this._lastScene].stars[star.Id];
			var newStar = mainScript.GetScenes()[this._activeScene].stars[star.Id];

			float baseScale = ((float)this.GetSize().X / star.Sprite.GetTexture().GetWidth()) / 100;

			double activeRot = (1 - weight) * oldStar.Rot + weight * newStar.Rot;
			Vector2 activePos = (Vector2.One - posWeight) * new Vector2(oldStar.X, oldStar.Y) + posWeight * new Vector2(newStar.X, newStar.Y);
			float activeScale = ((1 - (float)weight) * oldStar.Scale + (float)weight * newStar.Scale) * baseScale;
				
			//GD.Print("Scene: " + this._activeScene + ", Time: " + this._sceneTime + ", Weight: " + weight + ", ActiveRot: " + activeRot + ", ActivePos: " + activePos + ", ActiveScale: " + activeScale);
			
			star.Sprite.Rotation += (float)(((2*3.14159265)*(activeRot / 365)) * delta);
			star.Sprite.Position = activePos * this.GetSize();
			star.Sprite.Scale = Vector2.One * activeScale;
			
		}
		

	}

}
