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


namespace Rokojori
{  
  [Tool]
  [GlobalClass]
  public partial class MouseEditorCamera:VirtualCamera3D
  {
    [Export]
    public Vector3 target;
    
    [Export]
    public float yaw = 0;

    [Export]
    public float pitch = 0; 

    [Export]
    public float distance = 10; 
    float smoothDistance = 10;

    [ExportGroup("Orbit")]

    [Export]
    public float yawSpeed = 1;
    
    [Export] 
    public float pitchSpeed = 1;

    [Export] 
    public float minPitch = -89;

    [Export] 
    public float maxPitch = 89;

    [Export]
    public Sensor mouseMovementOrbitButton;

    [Export]
    public Sensor[] orbitModifierButtons = new Sensor[ 0 ];
    
    [Export]
    public float yawButtonsSpeed = 1;

    [Export]
    public Sensor yawDecreaseButton;

    [Export]
    public Sensor yawIncreaseButton;

    

    [Export]
    public float pitchButtonsSpeed = 1;

    [Export]
    public Sensor pitchDecreaseButton;

    [Export]
    public Sensor pitchIncreaseButton;

    
    

    [ExportGroup("Pan")]

    [Export]
    public float panSpeedX = 0.01f;

    [Export]
    public float panSpeedY = 0.01f;

    [Export]
    public Sensor mouseMovementPanButton;
    
    [Export]
    public Sensor[] panModifierButtons = new Sensor[ 0 ];

    [ExportGroup("Zoom")]

    [Export]
    public float zoomStepInPercentage = 10;
  
    [Export]
    public float minDistance = 0.001f;

    [Export]
    public float maxDistance = 200f;


    [Export]
    public Sensor zoomInButton;

    [Export]
    public Sensor[] zoomInModifierButtons = new Sensor[ 0 ];

    [Export]
    public Sensor zoomOutButton;

    [Export]
    public Sensor[] zoomOutModifierButtons = new Sensor[ 0 ];


    [Export]
    public Sensor continousZoomInButton;

    [Export]
    public Sensor continousZoomOutButton;

    [Export]
    public float continousZoomStepInPercentage = 1;

    [Export]
    public float zoomSmoothingCoefficient = 0.1f;
    Smoother smoother = new Smoother();

    [ExportGroup("Move")]
    [Export]
    public Sensor forwardButton;
    [Export]
    public Sensor backwardsButton;
    [Export]
    public Sensor leftButton;
    [Export]
    public Sensor rightButton;
    [Export]
    public Sensor upButton;
    [Export]
    public Sensor downButton;

    [Export]
    public float moveSpeed = 1;

    
    public override void _Process( double delta )
    {
      // RJLog.Log( leftButton.value, orbitButton.value );
      Orbit();
      Pan();      
      Zoom();
      Move();

      Apply( (float) delta );

      if ( ! hasMotionDelta )
      {
        motionDelta.X = 0;
        motionDelta.Y = 0;
      }

      hasMotionDelta = false;   
    }

    bool hasMotionDelta = false;
    Vector2 motionDelta = Vector2.Zero;
    
    public override void _Input( InputEvent inputEvent )
    {     
      var mouseMotionEvent = inputEvent as InputEventMouseMotion;

      if ( mouseMotionEvent == null )
      {
        return;
      }

      motionDelta = mouseMotionEvent.ScreenRelative;
      hasMotionDelta = true;      
      
    }

    void OrbitByMouse()
    {
      if ( ! Sensors.IsActive( mouseMovementOrbitButton ) )
      {
        return;
      }

      yaw   += motionDelta.X * yawSpeed; 
      pitch += motionDelta.Y * pitchSpeed;

      pitch = Mathf.Clamp( pitch, minPitch, maxPitch );
      
    }

    void Orbit()
    {
      OrbitByMouse();
      yaw += Sensors.PolarAxis( yawDecreaseButton, yawIncreaseButton ) * yawButtonsSpeed;
      pitch += Sensors.PolarAxis( pitchDecreaseButton, pitchIncreaseButton ) * pitchButtonsSpeed;
    }

    void Pan()
    {
      if ( ! Sensors.IsActive( mouseMovementPanButton ) )
      {
        return;
      }

      var xAmount = motionDelta.X * smoothDistance * GlobalBasis.X * panSpeedX;
      var yAmount = motionDelta.Y * smoothDistance * GlobalBasis.Y * panSpeedY;

      target += xAmount + yAmount;
      
    }

    void Zoom()
    {
      if ( Sensors.IsActive( zoomInButton ) )
      {
        distance *= Mathf.Pow( 1 + zoomStepInPercentage / 100f, 1 );
      }

      if ( Sensors.IsActive( zoomOutButton ) )
      {
        distance *= Mathf.Pow( 1 + zoomStepInPercentage / 100f, -1 );
      }

      if ( Sensors.IsActive( continousZoomInButton ) )
      {
        distance *= Mathf.Pow( 1 + continousZoomStepInPercentage / 100f, 1 );
      }

      if ( Sensors.IsActive( continousZoomOutButton ) )
      {
        distance *= Mathf.Pow( 1 + continousZoomStepInPercentage / 100f, -1 );
      }

      distance = Mathf.Clamp( distance, minDistance, maxDistance );
    }

    Vector3 moveDirection = Vector3.Zero;

    void Move()
    {
      moveDirection = Vector3.Zero;

      if ( Sensors.IsActive( forwardButton ) || Sensors.IsActive( backwardsButton ) )
      {
        moveDirection = ( Sensors.GetValue( forwardButton ) - Sensors.GetValue( backwardsButton ) ) * this.GlobalForward();
      }
      
      if ( Sensors.IsActive( rightButton ) || Sensors.IsActive( leftButton ) )
      {
        moveDirection = ( Sensors.GetValue( rightButton ) - Sensors.GetValue( leftButton ) ) * this.GlobalRight();
      }

      if ( Sensors.IsActive( upButton ) || Sensors.IsActive( downButton ) )
      {
        moveDirection = ( Sensors.GetValue( downButton ) - Sensors.GetValue( upButton ) ) * this.GlobalUp();
      }
      
      moveDirection = moveDirection.Normalized() * moveSpeed;
    }


    void Apply( float delta )
    {
      smoothDistance = smoother.SmoothWithCoefficient( smoothDistance, distance, zoomSmoothingCoefficient, delta );
      GlobalRotation = new Vector3( Mathf.DegToRad( pitch ), Mathf.DegToRad( yaw ), 0 );

      var forward = Math3D.GetGlobalForward( this ) * smoothDistance;
      target -= moveDirection * delta;
      GlobalPosition = target + forward;
    }
  }
}