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


namespace Rokojori
{  
  [Tool]
  [GlobalClass]
  public partial class ThirdPersonCamera:VirtualCamera3D
  {
    [Export]
    public Node3D target;
    
    [Export]
    public Smoothing targetFollowSmoothing = new FrameSmoothing();

    [ExportGroup("Yaw")]
    
    [Export]
    public float yawSpeed;

    [Export]
    public Sensor yawPositiveAxis;

    [Export]
    public Sensor yawNegativeAxis;

    [Export]
    public bool roundYaw = false;

    [Export]
    public int roundedYawResolution = 64;
    
    [Export]
    public float yaw = 0;

    [Export]
    public Smoothing yawSmoothing = new FrameSmoothing();

    [Export]
    public bool yawGoesBehindPlayer = true;

    [Export]
    public Smoothing yawToBehingSmoothing = new FrameSmoothing();

    [Export]
    public float yawBehindDelayDuration = 5; 

    [Export]
    public float yawGoesBehindActivation = 0.1f;

    [Export]
    public float behindYaw = 0;

    [Export]
    public float currentYaw = 0;

    [Export]
    public float bcDelta = 0;


    [ExportGroup("Pitch")]

    [Export]
    public float pitchSpeed;

    [Export]
    public Sensor pitchPositiveAxis;

    [Export]
    public Sensor pitchNegativeAxis;

    [Export]
    public bool pitchIsRelative = false;

    [Export]
    public float pitch = 0; 
    
    [Export]
    public float minPitch = -10;

    [Export]
    public float maxPitch = 80;

    public float normalizedPitch => MathX.NormalizeClamped( pitch, minPitch, maxPitch );

    [Export]
    public Smoothing pitchSmoothing = new FrameSmoothing();

    [Export]
    public bool pitchGoesBackToCenter = true;

    [Export]
    public Smoothing toCenterPitchSmoothing = new FrameSmoothing();

    [Export]
    public float centerPitchDelayDuration = 5; 

    [Export]
    public float centerPitch = 30;

    [Export]
    public float centerPitchActivation = 0.1f;

    [ExportGroup("Distance")]
    [Export]
    public Curve distanceForPitch = MathX.Curve( 1, 1 );

    [Export]
    public float distanceScale = 1;

    [ExportGroup("Camera Offset")]
    [Export]
    public Vector3 offset;


    float smoothedYaw      = 0;
    float smoothedPitch    = 0;
    float smoothedDistance = 0;

    float _centerPitchActivation = 0;
    float _yawBehindActivation = 0;

    public override void _Process( double delta )
    {
      if ( target == null )
      {
        return;
      }

      behindYaw = Math3D.GlobalYawDegrees( target.GlobalForward() * -1 );
      behindYaw = MathX.NormalizeAngle( behindYaw );

      currentYaw = MathX.NormalizeAngle( yaw );

      bcDelta = MathX.NormalizeAngle( MathX.AngleDelta( behindYaw, currentYaw ) );

      var targetPosition = Smoothing.Apply( targetFollowSmoothing, target.GlobalPosition, (float) delta );


      var yawAxis   = Sensors.PolarAxis( yawNegativeAxis, yawPositiveAxis );
      var pitchAxis = Sensors.PolarAxis( pitchNegativeAxis, pitchPositiveAxis );

      
      yaw += yawAxis * yawSpeed * (float)delta;      

      if ( yawGoesBehindPlayer )
      {
        if ( Mathf.Abs( yawAxis ) < yawGoesBehindActivation )
        {          
          _yawBehindActivation += (float)delta;

          if ( yawGoesBehindPlayer && _yawBehindActivation > yawBehindDelayDuration )
          {          
            yaw = Smoothing.ApplyDegreesWith( yawToBehingSmoothing, yaw, behindYaw, (float) delta );
          }
          
        }
        else
        {
          _yawBehindActivation = 0;
        }
      }
      
      // yaw = MathX.Repeat( yaw, 360f );

      if ( pitchIsRelative )
      {
        pitch += pitchAxis * pitchSpeed * (float)delta;     
        pitch = Mathf.Clamp( pitch, minPitch, maxPitch );

        if ( Mathf.Abs( pitchAxis ) <  centerPitchActivation )
        {          
          _centerPitchActivation += (float)delta;

          if ( _centerPitchActivation > centerPitchDelayDuration )
          {
            pitch = Smoothing.ApplyWith( toCenterPitchSmoothing, pitch, centerPitch, (float) delta );
          }
        }
        else
        {
          _centerPitchActivation = 0;
        }
      }
      else
      { 
        pitch = Mathf.Remap( pitchAxis, -1, 1, minPitch, maxPitch );
      }

      
          
      // if ( Mathf.Abs( yaw - smoothedYaw ) > 180 )
      // {
      //   if ( yaw > smoothedYaw )
      //   {
      //     smoothedYaw += 360;
      //   }
      //   else if ( yaw < smoothedYaw )
      //   {
      //     smoothedYaw -= 360;
      //   }
      // }

      var appliedYaw = yaw;

      // if ( roundYaw )
      // {
      //   appliedYaw = MathX.SnapRounded( yaw, 360f/roundedYawResolution );
      // }

      smoothedYaw = Smoothing.ApplyDegrees( yawSmoothing, appliedYaw, (float) delta );


      smoothedPitch = Smoothing.Apply( pitchSmoothing, pitch, (float) delta );

      // if ( pitchGoesBackToCenter )
      // {
      //   pitch = toCenterPitchSmoothing.Smooth( centerPitch, (float) delta );
      // }


      // RJLog.Log( "Pitch", smoothedPitch );

      // smoothedYaw = yaw;
      // smoothedPitch = pitch;
      var distance = distanceForPitch.Sample( MathX.NormalizeClamped( pitch, minPitch, maxPitch ) ) * distanceScale;

      GlobalPosition =  targetPosition + Math3D.YawPitchRotation( smoothedYaw, smoothedPitch ) * Vector3.Forward * distance;

      LookAt( targetPosition, Vector3.Up, true );

      GlobalPosition += this.GetOrientationBasedGlobalOffset( offset );

    }

    
  }
}