
using Godot;
using System.Collections.Generic;

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

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

    [Export( PropertyHint.Range, "1,16") ]
    public int num = 2;

    public enum RotationMode
    {
      Half_for_Crossings,
      Full_for_Uneven
    } 

    [Export]
    public RotationMode rotationMode = RotationMode.Half_for_Crossings;

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

    [ExportGroup( "Distortion")]

    [Export( PropertyHint.Range, "0,5") ]
    public float distortionAmount = 0.5f;

    [Export( PropertyHint.Range, "0,360") ]
    public float distortionAngle = 0.5f;

    [Export( PropertyHint.Range, "0,360") ]
    public float distortionAngleSpeed = 0.5f;

    [Export]
    public TimeLine distortionAngleTimeLine;

    [Export( PropertyHint.Range, "-1,1") ]
    public Vector2 distortionOffset = Vector2.Zero;

    [ExportGroup( "Smearing")]
    [Export( PropertyHint.Range, "1,32") ]
    public int smearingSteps = 5;

    [Export( PropertyHint.Range, "1,1.5") ]
    public float smearing = 1.1f;

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

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

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

    [ExportGroup( "Luma")]

    [Export( PropertyHint.Range, "0,10") ]
    public int blurRadiusX = 0;

    [Export( PropertyHint.Range, "0,10") ]
    public int blurRadiusY = 0;

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

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

    [Export]
    public Color lumaTint = Colors.White;

    [Export]
    public Vector3 lumaWeights = new Vector3( 0.3f, 0.5f, 0.2f );

    [Export]
    public float lumaWeightPower =1f;

    [Export]
    public Vector2 lumaInputRange = new Vector2( 0.5f, 1.5f ); 

    [Export]
    public float lumaNormalizationPower = 1f;

    [Export]
    public Vector2 lumaOutputRange = new Vector2( 0f, 1.5f ); 

    [Export]
    public float desaturation = 1f;


    int _downSampling = 16;

    public enum DownSampling
    {
      _1x_,
      _2x_,
      _4x_,
      _8x_,
      _16x_,
      _32x_            
    }

    public int downSamplingRate => Mathf.RoundToInt( Mathf.Pow( 2, (int) downSampling ) );

    [Export]
    public DownSampling downSampling = DownSampling._4x_;


    RG_ScreenColorTexure screenColorTexture;
    RG_BufferTexture bufferTexture;  
    RG_BufferTexture bufferTexture2;  
    
    RG_ExtractGlow extractGlow;
    RG_GaussBlur lumaBlurX;
    RG_GaussBlur lumaBlurY;

    int maxBlooms = 16;


    List<RG_ChromaticBloom> _blooms = [];  


    void Initialize()
    { 
      screenColorTexture = new RG_ScreenColorTexure( graph );
      bufferTexture = RG_BufferTexture.ScreenSize( graph, Vector2.One / (float) _downSampling  );
      bufferTexture2 = RG_BufferTexture.ScreenSize( graph, Vector2.One / (float) _downSampling  );

      lumaBlurX = new RG_GaussBlur( graph );
      lumaBlurY = new RG_GaussBlur( graph );

      extractGlow = new RG_ExtractGlow( graph );

      _blooms = new List<RG_ChromaticBloom>();

      for ( int i = 0; i < maxBlooms; i++ )
      {
        var b = new RG_ChromaticBloom( graph );
        _blooms.Add( b );
      }

      graph.InitializeNodes();

      extractGlow.SetTextureSlotInputs( screenColorTexture, bufferTexture );
      extractGlow.input.UseSampler();

      lumaBlurX.SetTextureSlotInputs( bufferTexture, bufferTexture2 );
      lumaBlurX.input.UseSampler();

      lumaBlurY.SetTextureSlotInputs( bufferTexture2, bufferTexture );
      lumaBlurY.input.UseSampler();


      _blooms.ForEach(
        ( b )=>
        {
          b.SetTextureSlotInputs( bufferTexture, screenColorTexture );
          b.input.UseSampler();
        }
      );
   
      graph.SetProcessOrder(
        screenColorTexture, 
        bufferTexture,
        bufferTexture2,         
        extractGlow,
        lumaBlurX,
        lumaBlurY
      );
      
    }

    int _lastNum = -1;

    protected override void ForAllViews()
    { 
      if ( downSamplingRate != _downSampling )
      {
        _downSampling = downSamplingRate;
        ( bufferTexture.GetCreator() as CEG_TextureCreator_ScreenSize ).scale = Vector2.One / (float) _downSampling;
        ( bufferTexture2.GetCreator() as CEG_TextureCreator_ScreenSize ).scale = Vector2.One / (float) _downSampling;
      }

      extractGlow.SetCustomComputeSize( context.internalSize / _downSampling  );
      lumaBlurX.SetCustomComputeSize( context.internalSize / _downSampling  );
      lumaBlurY.SetCustomComputeSize( context.internalSize / _downSampling  );

      extractGlow.constants.Set(
        lumaWeights.X, 
        lumaWeights.Y,
        lumaWeights.Z,
        lumaWeightPower,

        lumaInputRange.X, 
        lumaInputRange.Y,
        lumaNormalizationPower,
        lumaOutputRange.X,

        lumaOutputRange.Y,
        desaturation,
        lumaTint.R,
        lumaTint.G,

        lumaTint.B,
        0f,
        0f,
        0f
      );

      lumaBlurX.constants.Set(
        1f, 
        blurNoise,
        blurSpread,
        0f,

        (float)Mathf.Max( 1.0, blurRadiusX ), 
        0f, 
        0f,
        0f
      );

      lumaBlurY.constants.Set(
        1f, 
        blurNoise,
        blurSpread,
        0f,

        0f,
        (float)Mathf.Max( 1.0, blurRadiusY ), 
        0f,
        0f
      );

      if ( _lastNum != num )
      {
        _lastNum = num;

         var list = new List<RGGraphProcessor>(){
          screenColorTexture, 
          bufferTexture,     
          bufferTexture2,    
          extractGlow,
          lumaBlurX,
          lumaBlurY
         };
         
         for ( int i = 0; i < num; i++ )
         {
           list.Add( _blooms[ i ] );
         }

         graph.SetProcessOrder( list.ToArray() );
      }

      var combinedRotation = distortionAngle;

      var tl = TimeLineManager.Ensure( distortionAngleTimeLine );
      var animationRotation = MathX.Repeat( tl.position * distortionAngleSpeed / 360, 1f ) * 360f;
      combinedRotation = MathX.Repeat( animationRotation + combinedRotation, 360f );

      
      for ( int i = 0; i < num; i++ )
      {

        float angleOffset = 180f;

        if ( rotationMode == RotationMode.Full_for_Uneven )
        {
          angleOffset = 360f;
        }
        
        angleOffset /= (float) num;
     
        _blooms[ i ].constants.Set(
          amount,
          1.0f + redShift,
          1.0f + greenShift,
          1.0f + blueShift,

          (float) smearingSteps,
          smearing,
          distortionAmount,
          ( combinedRotation + i * angleOffset ) / 180f * Mathf.Pi,

          distortionOffset.X,
          distortionOffset.Y,
          blendMode,
          0f
        );
      }

      
    }

   
  }
}