
using Godot;
using System.Collections.Generic;

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

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

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

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

    [Export ]
    public Vector2 direction = new Vector2( 1f, 0f );

    [Export( PropertyHint.Range, "-360,360") ]
    public float rotationOffset = 0;

    [Export( PropertyHint.Range, "-360,360") ]
    public float rotationSpeed = 0;
    
    [Export]
    public TimeLine rotationSpeedTimeLine;

    [Export( PropertyHint.Range, "1,8") ]
    public int numStreaks = 3;

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

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

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

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

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

    [Export( PropertyHint.Range, "1,5") ]
    public int blurRadius = 1;

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

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

    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_Resize downScale;
    CEG_Copy copy;

    RG_GlowAdd upScale;

    RG_Streaks streaks1; 
    RG_Streaks streaks2;
    RG_Streaks streaks3;    

    List<RG_Streaks> _streaks;

    int _downSampling = 16;

    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  );

      downScale = new RG_Resize( graph );
      upScale = new RG_GlowAdd( graph );

      copy = new CEG_Copy( graph );

      streaks1 = new RG_Streaks( graph );
      streaks2 = new RG_Streaks( graph );
      streaks3 = new RG_Streaks( graph );
      
      _streaks = 
      [ 
        streaks1, 
        streaks2, 
        streaks3
      ];

      graph.InitializeNodes();

      downScale.SetTextureSlotInputs( screenColorTexture, bufferTexture );
      downScale.input.UseSampler();
     
      copy.SetTextureSlotInputs( bufferTexture, bufferTexture2 );

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

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

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

      upScale.SetTextureSlotInputs( bufferTexture2, screenColorTexture );
      upScale.input.UseSampler();


      graph.SetProcessOrder(
        screenColorTexture, 
        bufferTexture, 
        bufferTexture2,
        
        downScale,

        copy,

        streaks1,
        streaks2,
        streaks3,

        upScale
      );
      
    }


    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;
      }

      downScale.SetCustomComputeSize( context.internalSize / _downSampling  );
      copy.SetCustomComputeSize( context.internalSize / _downSampling );
      
      var iteration = 0;
      var lumaScale = Mathf.Pow( 5f, lumaScale5 );

      var combinedRotation = rotationOffset;

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

      _streaks.ForEach(
        ( s )=>
        {
          s.SetCustomComputeSize( context.internalSize / _downSampling );

          s.constants.Set(        
            direction,
            new Vector2( attenuation, amount ),
            new Vector2( samples, iteration ),
            new Vector2( lumaTreshold, lumaScale ),
            new Vector2( numStreaks, blurRadius ),
            new Vector2( combinedRotation / 180 * Mathf.Pi, desaturation )
          );

          iteration++;
        }
      );

      upScale.constants.Set(
        intensity * intensityScale,
        blurNoise,        
        (float)blurSpread,
        (float)blurRadius,

        lumaTreshold,
        lumaScale,
        0f,
        0f
      );

      
    }

   
  }
}