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


namespace Rokojori
{  
  [Tool]
  [GlobalClass, Icon("res://addons/rokojori_action_library/Icons/VirtualCameraManager.svg") ]
  public partial class VirtualCamera3DManager:NetworkNode
  { 
    [Export]
    public Camera3D camera;

    [Export]
    public bool refreshSlots = false;

    [Export]
    public bool active = false; 

    public static VirtualCamera3DManager Get()
    {
      return Unique<VirtualCamera3DManager>.Get();
    }

    public VirtualCamera3DSlot activeSlot 
    {
      get
      {
        return Lists.GetWithHighestValue( _cameraSlots, c => c.priority );
      }
    }

    public override void _Process( double delta ) 
    {     
      if ( ! active )
      {
        return;
      }

      LerpCameras( delta );
    }

    [Export]
    public float CameraPrioritySmoothingCoefficient = 0.1f;

    [Export]
    public float CameraPrioritySmoothingStepFPS = 120;

    public float smoothStepDelta => 1f / CameraPrioritySmoothingStepFPS;
    public float safeSmoothing => Mathf.Max( 0, CameraPrioritySmoothingCoefficient );

    List<VirtualCamera3DSlot> _cameraSlots = new List<VirtualCamera3DSlot>();

    public VirtualCamera3DSlot GetSlot( int index )
    {
      return _cameraSlots[ index ];
    }

    public void SetActiveSlot( VirtualCamera3DSlot slot )
    {
      _cameraSlots.ForEach( c => c.priority = c == slot ? 1 : 0 );
    }

    void LerpCameras( double delta )
    {
      if ( refreshSlots ||  _cameraSlots == null || _cameraSlots.Count == 0 )
      {
        refreshSlots = false;
        _cameraSlots = Nodes.GetDirectChildren<VirtualCamera3DSlot>( this ); 
      }

      var sumPriority = 0f;

      _cameraSlots.ForEach( 
        c => 
        { 
          c.Update( delta, this );
          sumPriority += MathF.Max( 0, c.smoothedPriority );
        }
      );

      if ( sumPriority == 0 )
      {
        return;
      }

      var position = new Vector3();
      var up       = new Vector3();
      var forward  = new Vector3();
      var fov = 0f;

      _cameraSlots.ForEach( 
        c => 
        {
          var priority = MathF.Max( 0, c.smoothedPriority );
          var rotation = c.GetCameraRotation(); 

          if ( ! rotation.IsFinite() || rotation.Length() == 0 )
          {
            rotation = new Quaternion();
            rotation.X = 0;
            rotation.Y = 0;
            rotation.Z = 0;
            rotation.W = 1;
            rotation = rotation.Normalized();
          }
        

          var vUp      = rotation * Vector3.Up;
          var vForward = rotation * Vector3.Forward;

          position += priority * c.GetCameraPosition();
          up       += priority * vUp;
          forward  += priority * vForward;      
          fov      += priority * c.GetCameraFOV();
        } 
      );

      position /= sumPriority;
      fov /= sumPriority;

      if ( forward.LengthSquared() == 0 )
      {
        forward = camera.Basis.Z; 
      }
      else
      {
        forward = forward.Normalized();
      }

      if ( up.LengthSquared() == 0 )
      {
        up = camera.Basis.Y; 
      }
      else
      {
        up = up.Normalized();
      }      

      // RJLog.Log( "Set Cam", position );

      camera.GlobalPosition = position;
      camera.LookAt( position - forward, up );
      camera.Fov = fov;

    }

    public VirtualCamera3D GetCamera( int index )
    {
      return _cameraSlots[ index ].camera;
    }

    public int GetCameraIndex( VirtualCamera3D camera3D )
    {
      return _cameraSlots.FindIndex( c => c.camera == camera3D );
    }

    public float GetCameraPriority( int index )
    {
      return _cameraSlots[ index ].priority;
    }

    public void SetCameraPriority( int index, float priority )
    {
      _cameraSlots[ index ].priority = priority;
    }

    

  }
}