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



namespace Rokojori
{
  [Tool]
  [GlobalClass]
  public partial class Baker:Node
  {
    public enum CameraDistanceDetectionType
    {
      Automatic_Distance_Detection,
      Custom_Distance
    }
    
    public enum CameraFOVMode
    {
      Keep_Fov,
      Compute_Fov_With_Distance,
      Custom_Fov
    }

    public enum MeshMode
    {
      World_Scale,
      Custom_Scale
    }
    
    [Export]
    public bool update = false;

    [Export]
    public bool updateAlways = false;

    [Export]
    public Viewport viewport;

    [Export]
    public Node3D target;

    [Export]
    public Camera3D camera;


    [Export]
    public float originalFOV = 75;

    
    [Export]
    public bool assignFOV = false;

    [Export]
    public float placingDistance = 500;

    [Export]
    public float computedFOV = 75;

    [Export]
    public bool useCustomFOV = false;

    [Export]
    public float customFOV = 75;

    [Export]
    public bool useCustomDistance = false;

    [Export]
    public float customDistance = 50;

    [Export]
    public float outputScale = 1;

    [Export]
    public Node3D outputTexture;

    [Export]
    public float outputTextureSize = 1;

    public enum RotationMode
    {
      Yaw_Pitch,
      Quaternion
    }

    [ExportGroup("Rotation")]

    [Export]
    public RotationMode rotationMode = RotationMode.Yaw_Pitch;

    [Export( PropertyHint.Range, "-180,180")]
    public float yaw = 0;

    [Export( PropertyHint.Range, "-180,180")]
    public float pitch = 0;

    [Export]
    public Quaternion rotationQuaternion;


    public Quaternion bakingRotation => RotationMode.Yaw_Pitch == rotationMode ? 
                                          Math3D.YawPitchRotation( yaw, pitch ) :
                                          rotationQuaternion;
    
    [Export]
    public float zoom = 1;

    [Export]
    public float distance = 1;

    public override void _Process( double delta )
    {
      if ( ! ( update || updateAlways ) )
      {
        return;
      }

      update = false;

      Bake();
      
    } 

    void Bake()
    {
      if ( viewport == null || target == null || camera == null )
      {
        return;
      }

      var box = target.GetWorldBounds();

      if ( box == null )
      {
        RJLog.Log( "No target" );
        return;
      }

      var sphere = Sphere.ContainingBox( box );
      camera.Fov = originalFOV;

      var billboardFOV = camera.Fov;

      
      if ( assignFOV )
      { 
        computedFOV = Cameras.ComputeFOVForBillboard( originalFOV, sphere.radius, placingDistance );
        billboardFOV = computedFOV;
        camera.Fov = billboardFOV;
      }

      if ( useCustomFOV )
      {
        billboardFOV = customFOV;
        camera.Fov = billboardFOV;
      }

      var newDistance = useCustomDistance ? customDistance : Cameras.ComputeCameraFrameFittingDistance( billboardFOV, sphere.radius / zoom );

      if ( newDistance != distance )
      {
        distance = newDistance;
        // RJLog.Log( "New Distance:", box, sphere, distance );
      }

      var cameraRotation = bakingRotation; 
      var offset = ( Vector3.Back * distance ) * cameraRotation ;
      camera.GlobalPosition = target.GlobalPosition + offset;

      camera.SetGlobalQuaternion( cameraRotation.Inverse() );

      // RJLog.Log( "Set Rotation", cameraRotation, ">>", camera.GetGlobalQuaternion()  );

      outputScale = Cameras.ComputeCameraFittingScale( camera.Fov, distance );

      if ( outputTexture != null )
      {
        outputTexture.Scale = Vector3.One * outputScale;
      }
    }

  }
}
