
using Godot;

namespace Rokojori
{  
  [Tool]
  [GlobalClass]
  public partial class GradientFogEffect:FogCompositorEffect
  {
    public GradientFogEffect():base()
    {
      Initialize();
    } 

    
    [ExportGroup( "Main")]
    [Export( PropertyHint.Range, "0,1") ]
    public float amount = 1f;

    [Export( PropertyHint.Range, "0,1") ]
    public float skyAmount = 0f;

    [Export]
    public float zStart = 1f;

    [Export]
    public float zEnd = 1f;

    [Export( PropertyHint.Range, "-1,1") ]
    public float zSpread = 0f;

    [Export]
    public GradientTexture1D gradient = new GradientTexture1D();

    [Export]
    public float gradientUVScale = 1f;

    [Export]
    public float gradientUVOffset = 0f;

    [Export]
    public float gradientUVScroll = 0f;

    [Export]
    public float gradientUVScrollFPS = 0f;

    [Export]
    public TimeLine gradientUVScrollTimeline = null;

    public enum UVGradientSampleMode
    {
      Fract,
      Clamp
    }

    [Export]
    public UVGradientSampleMode gradientSampleMode = UVGradientSampleMode.Fract;



    [Export]
    public CurveTexture opacity = new CurveTexture().WithCurve( new Curve().WithValues( 1, 1 ) ); 

    [ExportToolButton("Z Distance Only")]
    public Callable zWeightsButton => Callable.From( ()=> directionWeighting = new Vector3( 0, 0, -1 ) );

    [ExportToolButton("XZ Distance")]
    public Callable xzWeightsButton => Callable.From( ()=> directionWeighting = new Vector3( 1, 0, -1 ) );

    [ExportToolButton("Distance to Camera")]
    public Callable cameraWeightsButton => Callable.From( ()=> directionWeighting = new Vector3( 1, 1, -1 ) );

    [Export]
    public Vector3 directionWeighting = new Vector3( 1, 1, 1 );

    [Export]
    public float directionScalingX = 1f;

    [Export]
    public float directionScalingY = 1f;

    [Export]
    public Vector3 directionOffset = Vector3.Zero;


    RG_ScreenColorTexure screenColorTexture;
    RG_ScreenDepthTexture screenDepthTexture;

    RG_ImageTexture gradientTexture;
    RG_ImageTexture opacityTexture;

    RG_GradientFog gradientFog;


    void Initialize()
    { 
      screenColorTexture = new RG_ScreenColorTexure( graph );
      screenDepthTexture = new RG_ScreenDepthTexture( graph );

      gradientTexture = new RG_ImageTexture( graph );
      opacityTexture = new RG_ImageTexture( graph );

      gradientFog = new RG_GradientFog( graph );


      graph.InitializeNodes();

      gradientFog.SetTextureSlotInputs( screenColorTexture, screenColorTexture );
      gradientFog.AddTextureSlotInput( screenDepthTexture ).UseLinearSamplerEdgeClamped();
      gradientFog.AddTextureSlotInput( gradientTexture ).UseLinearSamplerEdgeClamped();
      gradientFog.AddTextureSlotInput( opacityTexture ).UseLinearSamplerEdgeClamped();
   
      graph.SetProcessOrder(
        screenColorTexture, 
        screenDepthTexture,

        gradientTexture,
        opacityTexture,

        gradientFog
      );
      
    }


    protected override void ForAllViews()
    { 
      gradientTexture.SetImageTexture( gradient );
      opacityTexture.SetImageTexture( opacity );

      var projection = context.GetCameraProjection().Inverse();      

      var tl = TimeLineManager.Ensure( gradientUVScrollTimeline );

      float tlPosition = tl.position;

      if ( gradientUVScrollFPS > 0f )
      {
        float stepTime = 1.0f / gradientUVScrollFPS;
        tlPosition = MathX.SnapFloored( tlPosition, stepTime );
      }

      var gradientOffset = ( tlPosition * gradientUVScroll + gradientUVOffset ) % 1f;

      gradientFog.constants.Set(
        projection.X, 
        projection.Y,
        projection.Z,
        projection.W,

        amount, 
        MathX.PowerValue( zSpread ),
        zStart,
        zEnd,

        gradientUVScale,
        gradientOffset,
        directionWeighting.X,
        directionWeighting.Y,

        directionWeighting.Z,
        (float) (int) gradientSampleMode,
        skyAmount,
        directionOffset.X,

        directionOffset.Y,
        directionOffset.Z,
        directionScalingX,
        directionScalingY

      );
      
    }

  }
}