using Godot;
using System.Text;
using System.Collections.Generic;

namespace Rokojori
{  
  public class Cameras
  {
    public static float ComputeCameraFrameFittingDistance( Camera3D camera, float radius )
    {
      var fovRadians = Mathf.DegToRad( camera.Fov );
      return ( radius * 2 ) / Mathf.Tan( fovRadians / 2.0f ); 
    }

    public static float ComputeCameraFrameFittingDistance( float fovDegrees, float radius )
    {
      var fovRadians = Mathf.DegToRad( fovDegrees );
      return ( radius * 2 ) / Mathf.Tan( fovRadians / 2.0f ); 
    }

    public static float ComputeCameraFittingScale( float fovDegrees, float distance )
    {
      var fovRadians = Mathf.DegToRad( fovDegrees );
      return distance / ( 0.5f / Mathf.Tan( fovRadians / 2.0f ) ); 
    }

    public static float ComputeFOVForBillboard( float fovDegrees, float radius, float placingDistance )
    {
      var fovRadians = Mathf.DegToRad( fovDegrees );
      var d = ( radius * Mathf.Tan( fovRadians / 2f ) ) / placingDistance; 
      var rads = 2f * Mathf.Atan( d );

      return Mathf.RadToDeg( rads ); 
    }

    public static float ComputePixelDensityVertical( float fovDegrees, float distance, Vector2 screenSize )
    {
      float fovRadians = Mathf.DegToRad( fovDegrees );

      float verticalViewSize = 2.0f * distance * Mathf.Tan( fovRadians / 2.0f );

      float screenHeightPixels = screenSize.Y;

      float pixelDensity = screenHeightPixels / verticalViewSize;

      return pixelDensity;    
    } 

    public static float ComputeSizeOfPixelVertical( float fovDegrees, float distance, Vector2 screenSize )
    {
      return ComputePixelDensityVertical( fovDegrees, distance, screenSize );
    }


    public static float ComputePixelDistanceForSizeVertical( float fovDegrees, float size, Vector2 screenSize )
    {
      float fovRadians = Mathf.DegToRad(fovDegrees);
      float screenHeightPixels = screenSize.Y;
      
      float pixelDensity = 1.0f / size;

      float distance = screenHeightPixels / (2.0f * pixelDensity * Mathf.Tan(fovRadians / 2.0f));

      return distance;

    }

    public static float ComputePixelDensityHorizontal(float fovDegrees, float distance, Vector2 screenSize)
    {
      float aspectRatio = screenSize.X / screenSize.Y;

      float verticalFovRad = Mathf.DegToRad( fovDegrees );

      float horizontalFovRad = 2.0f * Mathf.Atan(Mathf.Tan( verticalFovRad / 2.0f ) * aspectRatio);

      float horizontalViewSize = 2.0f * distance * Mathf.Tan( horizontalFovRad / 2.0f );

      float pixelDensity = screenSize.X / horizontalViewSize;

      return pixelDensity;
    }

    public static Vector3 GlobalToView( Transform3D transform, Vector3 globalPosition )
    {
      return globalPosition * transform;
    }

    public static Vector3 ViewToClip( Projection cameraProjection, Vector3 viewPosition )
    {
      var clip = cameraProjection * viewPosition;
      return clip;
    }

    public static Vector2 ClipToScreen( Vector3 clipPosition )
    {
      var size = Vector2.One;
      var clip = clipPosition.XY() * 0.5f + new Vector2( 0.5f, 0.5f );
      return clip * size; 
    }

    public static Vector2 ClipToScreen( Vector3 clipPosition, Vector2 screenPixelSize )
    {
      var clip = clipPosition.XY() * 0.5f + new Vector2( 0.5f, 0.5f );
      return clip * screenPixelSize; 
    }

  
    public static Vector2 GlobalToScreen( Vector3 globalPosition, Transform3D cameraTransform, Projection cameraProjection, Vector2 screenPixelSize )
    {
      var view = GlobalToView( cameraTransform, globalPosition );
      var clip = ViewToClip( cameraProjection, view );
      var screen = ClipToScreen( clip, screenPixelSize );

      return screen;
    }


  }
}