using Engine.Util;
using OpenTK.Mathematics;

namespace Engine.Scene.Component.BuiltIn;

public class PerspectiveCamera(
  float parFieldOfView = 60.0f,
  float parNearPlane = 0.01f,
  float parFarPlane = 1000f
)
  : Camera(parNearPlane, parFarPlane)
{
  public float FieldOfView { get; set; } = parFieldOfView;

  public Vector3 Forward => new Vector4(0, 1, 0, 1).MulProject(GameObject.Transform.TransformMatrix).Xyz;
  public override Matrix4 View
  {
    get
    {
      var transformMatrix = GameObject.Transform.TransformMatrix;
      var forward = new Vector4(0, 1, 0, 1).MulProject(in transformMatrix);
      var eye = new Vector4(0, 0, 0, 1).MulProject(in transformMatrix);
      var up = (new Vector4(0, 0, 1, 1).MulProject(in transformMatrix) - eye).Normalized();

      return Matrix4.LookAt(eye.Xyz, forward.Xyz, up.Xyz);
    }
  }

  public override Matrix4 Projection =>
    Matrix4.CreatePerspectiveFieldOfView(MathHelper.DegreesToRadians(FieldOfView), AspectRatio,
      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;
  }
}