
using Godot;
using System.Collections.Generic;

namespace Rokojori
{  
  [Tool]
  [GlobalClass]
  public abstract partial class RokojoriCompositorEffect:CompositorEffect
  {
    [ExportToolButton( "Clear Caches" )]
    public Callable clearCachesButton => Callable.From( ()=> ClearCaches() );

    public static readonly string effectsPath = "res://addons/rokojori_action_library/Runtime/Rendering/Compositor/CompositorEffects";
    public static string Path( string path )
    {
      return effectsPath + "/" + path;
    }

    RDContext _context;
    public RDContext context => _context;

    protected virtual void OnConfigure(){}
    protected virtual void OnInitialize(){}
    protected virtual void ForAllViews(){}    
    protected virtual void RenderView(){}

    public RokojoriCompositorEffect():base()
    {
      RenderingServer.CallOnRenderThread( Callable.From( _InitializeCompositorEffect ) );    
    }

    public virtual void ClearCaches()
    {

    }

    protected void _InitializeCompositorEffect()
    {            
      OnConfigure();      

      _context = new RDContext();
      _context.Initialize();

      OnInitialize();    

    }

    bool _hasError = false;

    public override void _RenderCallback( int effectCallbackType, RenderData renderData )
    {  
      if ( _hasError || context.HasError() )
      {
        var errors = context.messages.Filter( m => m.type == MessageType.Error );
        this.LogInfo( "_hasError", _hasError, "context.HasError()", context.HasError(), errors.Join("\n") );
        return; 
      }

      context.messages.Clear(); 

      try
      {

        if ( context.renderingDevice == null )
        {
          context.Error( "No render device" );
          return;
        }
        
        var sceneBuffers = ( RenderSceneBuffersRD ) renderData.GetRenderSceneBuffers();
        var sceneData =    ( RenderSceneDataRD ) renderData.GetRenderSceneData();
      
        if ( sceneBuffers == null && sceneData == null )
        {
          context.Error( "sceneBuffers == null && sceneData == null" );
          return;
        }
        
      

        var size = sceneBuffers.GetInternalSize();

        if ( size.X == 0 || size.Y == 0 )
        {
          context.Warning( "InternalSize.X == 0 || InternalSize.Y == 0" );
          return;
        }
        
        _context.SetRenderData( renderData, sceneBuffers, sceneData );
        _context.SetView( -1 );

        ForAllViews();        

        int viewCount = ( int ) sceneBuffers.GetViewCount();

        for ( int i = 0; i < viewCount; i++ )
        {
          _context.SetView( i );
          RenderView();
        }

      }
      catch( System.Exception e )
      {
        this.LogError( e );
        _hasError = true;
        context.Error( e );
      }

      if ( context.HasError() )
      {
        _hasError = true;
      }

    }

    public override void _Notification( int what )
    {  
      var _shader = context.shader;

      context.Verbose( "Got notification: ", what ); 

      if ( what != NotificationPredelete || ( _shader == null || !_shader.valid ) || context.renderingDevice == null )
      {
        context.Verbose( 
          "what != NotificationPredelete", what != NotificationPredelete,
          "( _shader == null || !_shader.valid )", ( _shader == null || !_shader.valid ),
          "rd == null", context.renderingDevice == null 
        );
        return;
      }

      context.Free( _shader, "Shader" );
      context.CleanUp();
      
    }
    
  }
}