using Engine.Util;
using OpenTK.Mathematics;

namespace Engine.Scene.Component.BuiltIn;

public class OrthographicCamera(
  float parNearPlane = -10000f,
  float parFarPlane = 10000f,
  float parSize = 10f,
  OrthographicCamera.Axis parAxis = OrthographicCamera.Axis.Y
)
  : Camera(parNearPlane, parFarPlane)
{
  public Axis FixedAxis { get; set; } = parAxis;
  public float Size { get; set; } = parSize;
  public bool UseScreenSize { get; set; } = false;

  public override Matrix4 View => GameObject.Transform.TransformMatrix.Inverted();

  public override Matrix4 Projection => GetProjectionMatrix();

  private Matrix4 GetProjectionMatrix()
  {
    var size = GameObject.Transform.Size.Xy;
    return Matrix4.CreateOrthographic(size.X, size.Y, -NearPlane, -FarPlane);
  }

  public override Vector3 ScreenToWorld(Vector2 parScreenPosition)
  {
    var normalized = parScreenPosition / ScreenSize - new Vector2(0.5f);
    normalized.X *= 2;
    normalized.Y *= -2;

    return new Vector4(normalized.X, normalized.Y, 0, 1)
      .MulProject(Projection.Inverted())
      .MulProject(GameObject.Transform.TransformMatrix)
      .Xyz;
  }

  public override Vector2 WorldToScreen(Vector3 parWorldPosition)
  {
    var normalized = new Vector4(parWorldPosition, 1)
      .MulProject(GameObject.Transform.TransformMatrix.Inverted())
      .MulProject(Projection)
      .Xy;

    normalized.X /= 2;
    normalized.Y /= -2;

    return (normalized + new Vector2(0.5f)) * ScreenSize;
  }

  public override void Update(double parDeltaTime)
  {
    if (UseScreenSize)
    {
      GameObject.Transform.Size.Xy = ScreenSize.ToVector2();
    }
    else
    {
      if (FixedAxis == Axis.X)
      {
        GameObject.Transform.Size.X = Size;
        GameObject.Transform.Size.Y = Size / AspectRatio;
      }
      else
      {
        GameObject.Transform.Size.X = Size * AspectRatio;
        GameObject.Transform.Size.Y = Size;
      }
    }
  }

  public enum Axis
  {
    X,
    Y
  }
}