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

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

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

    [Export]
    public RG_BlendModeType blendMode = RG_BlendModeType.Alpha;

    public enum UVMode
    {
      Rectangle,
      Ellipse
    }

    [Export]
    public UVMode uvMode = UVMode.Rectangle;

    [ExportGroup("Overlay")]

    [Export]
    public Texture2D overlayTexture;

    [Export]
    public Color overlayTint = Colors.White;

    [Export]
    public ColorSpaceConversion overlayConversion = ColorSpaceConversion.sRGB_to_Linear;

    [Export]
    public Vector2 tiling = Vector2.One;

    [Export]
    public Vector2 offset = Vector2.Zero;

    [Export]
    public Vector2 scrolling = Vector2.Zero;

    [Export]
    public float scrollingFPS = 60f;

    [Export]
    public TimeLine scrollTimeline;

    [Export]
    public RenderingDevice.SamplerFilter filter = RenderingDevice.SamplerFilter.Linear;

    [Export]
    public RenderingDevice.SamplerRepeatMode repeatMode = RenderingDevice.SamplerRepeatMode.Repeat;

    [ExportGroup("Mask")]
    [Export]
    public Texture2D maskTexture;

    [Export]
    public CurveTexture alphaMapping = new CurveTexture().WithCurve( new Curve().WithValues( 0, 1 ) )
                                                         .WithResolution( 256 )
                                                         .WithTextureMode( CurveTexture.TextureModeEnum.Red );

    [Export]
    public ColorSpaceConversion maskOverlayConversion = ColorSpaceConversion.sRGB_to_Linear;


    [Export]
    public Vector2 maskTiling = Vector2.One;

    [Export]
    public Vector2 maskOffset = Vector2.Zero;

    [Export]
    public Vector2 maskScrolling = Vector2.Zero;

    [Export]
    public float maskScrollingFPS = 60f;

    [Export]
    public TimeLine maskScrollTimeline;

    [Export]
    public RenderingDevice.SamplerFilter maskFilter = RenderingDevice.SamplerFilter.Linear;

    [Export]
    public RenderingDevice.SamplerRepeatMode maskRepeatMode = RenderingDevice.SamplerRepeatMode.Repeat;

    [ExportGroup("Advanced")]
    [Export]
    public ColorSpaceConversion screenConversion = ColorSpaceConversion.No_Conversion;

    [Export]
    public ColorSpaceConversion outputConversion = ColorSpaceConversion.No_Conversion;

    RG_ImageTexture _topTexture;
    RG_ImageTexture _maskTexture;
    RG_ImageTexture _alphaMappingTexture;
    RG_ScreenColorTexure _bottomTexture;
    RG_ScreenColorTexure _outputTexture;
    
    
    List<RG_BlendModeBase> _blendModes;

    RenderingDevice.SamplerFilter assignedfilterTop = RenderingDevice.SamplerFilter.Linear;
    RenderingDevice.SamplerRepeatMode assignedRepeatModeTop = RenderingDevice.SamplerRepeatMode.Repeat;

    RenderingDevice.SamplerFilter assignedfilterMask = RenderingDevice.SamplerFilter.Linear;
    RenderingDevice.SamplerRepeatMode assignedRepeatModeMask = RenderingDevice.SamplerRepeatMode.Repeat;

    RG_BlendModeType _assignBlendMode = RG_BlendModeType.Alpha;

    void Initialize()
    {       
      _topTexture = new RG_ImageTexture( graph );
      _maskTexture = new RG_ImageTexture( graph );
      _alphaMappingTexture = new RG_ImageTexture( graph );
      _bottomTexture = new RG_ScreenColorTexure( graph );
      _outputTexture = new RG_ScreenColorTexure( graph );

      _blendModes = RG_BlendModesTool.CreateAll( graph );

      graph.InitializeNodes();

      _blendModes.ForEach(
        ( bm )=>
        {
          bm.SetTextureSlotInputs( _topTexture, _maskTexture, _alphaMappingTexture, _bottomTexture, _outputTexture );      
      
          bm.top.sampler = context.Sampler( assignedfilterTop, assignedRepeatModeTop );
          bm.mask.sampler = context.Sampler( assignedfilterMask, assignedRepeatModeMask );
          bm.alphaMapping.sampler = context.Sampler( RenderingDevice.SamplerFilter.Linear, RenderingDevice.SamplerRepeatMode.ClampToEdge );
          bm.bottom.sampler = context.Sampler( RenderingDevice.SamplerFilter.Nearest, RenderingDevice.SamplerRepeatMode.ClampToEdge );

        }
      );
      
      _assignBlendMode = blendMode;

      graph.SetProcessOrder(
        _topTexture,
        _maskTexture,
        _alphaMappingTexture,
        _bottomTexture,
        _outputTexture,
        RG_BlendModesTool.GetCurrentBlendMode( _blendModes, _assignBlendMode ) 
      );
      
    }
    
    
    
    protected override void ForAllViews()
    {
      if ( _assignBlendMode != blendMode )
      {
        _assignBlendMode = blendMode;

        var nextBlendMode = RG_BlendModesTool.GetCurrentBlendMode( _blendModes, _assignBlendMode );
         graph.SetProcessOrder(
          _topTexture,
          _maskTexture,
          _alphaMappingTexture,
          _bottomTexture,
          _outputTexture,
          RG_BlendModesTool.GetCurrentBlendMode( _blendModes, _assignBlendMode ) 
        );

        nextBlendMode.top.sampler = context.Sampler( assignedfilterTop, assignedRepeatModeTop );
        nextBlendMode.mask.sampler = context.Sampler( assignedfilterMask, assignedRepeatModeMask );
      }

      var currentBlendMode = RG_BlendModesTool.GetCurrentBlendMode( _blendModes, blendMode );

      if ( assignedfilterTop != filter || assignedRepeatModeTop != repeatMode )
      {
        assignedfilterTop = filter;
        assignedRepeatModeTop = repeatMode;

        currentBlendMode.top.sampler = context.Sampler( assignedfilterTop, assignedRepeatModeTop );
      }

      if ( assignedfilterMask != maskFilter || assignedRepeatModeMask != maskRepeatMode )
      {
        assignedfilterMask = maskFilter;
        assignedRepeatModeMask = maskRepeatMode;

        currentBlendMode.mask.sampler = context.Sampler( assignedfilterMask, assignedRepeatModeMask );
      }
      
      _topTexture.SetImageTexture( overlayTexture );
      _maskTexture.SetImageTexture( maskTexture );
      _alphaMappingTexture.SetImageTexture( alphaMapping );

      var timeLine = TimeLineManager.Ensure( scrollTimeline );
      var timelinePosition = timeLine.position;

      if ( scrollingFPS > 0f )
      {
        var step = 1f / scrollingFPS;
        timelinePosition = MathX.SnapFloored( timelinePosition, step );
      }

      var scrollPosition = timelinePosition * scrolling;
      scrollPosition.X = MathX.Repeat( scrollPosition.X, 1.0f );
      scrollPosition.Y = MathX.Repeat( scrollPosition.Y, 1.0f );

      var timeLineMask = TimeLineManager.Ensure( maskScrollTimeline );

      float maskPosition = timeLineMask.position;

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

      var scrollPositionMask = maskPosition * maskScrolling;
      scrollPositionMask.X = MathX.Repeat( scrollPositionMask.X, 1.0f );
      scrollPositionMask.Y = MathX.Repeat( scrollPositionMask.Y, 1.0f );

      currentBlendMode.constants.Set(
       [ 
        new Vector4( tiling.X, tiling.Y, offset.X + scrollPosition.X, offset.Y + scrollPosition.Y ),
        new Vector4( maskTiling.X, maskTiling.Y, maskOffset.X + scrollPositionMask.X, maskOffset.Y + scrollPositionMask.Y ),
        new Vector4( 1f, 1f, 0f, 0f ),
        overlayTint,

        opacity,
        (float)(int)overlayConversion,
        (float)(int)maskOverlayConversion,
        (float)(int)screenConversion,

        (float)(int)outputConversion,
        1f,
        (float)(int)uvMode
       ]
      );
     
    }

   
  }
}