using Godot;
using System.Reflection;
using System.Collections.Generic;
using System.IO;

namespace Rokojori
{ 
  [Tool]
  [GlobalClass]
  public partial class ShaderGenerator:Node
  { 
    [ExportToolButton( "Create" )]
    public Callable CreateButton => Callable.From(
      ()=>
      {
        UpdateShader();
      }
    );

    [Export]
    public string path;

    [Export]
    public string shaderName;

    [Export]
    public Shader shader;

    [Export]
    public bool sendGodotUpdateMessage = false;

    [Export]
    public int numVariants = -1;

    public virtual List<ShaderPhase> GetPhases()
    {
      return new List<ShaderPhase>();
    }

    public virtual List<ShaderGenerationModule> GetShaderGenerationModules( ShaderPhase phase )
    {
      return new List<ShaderGenerationModule>();
    }

    public virtual ShaderGenerationContext GenerateShaderVariants()
    {
      numVariants = 0;

      var context = new ShaderGenerationContext();

      var phases = GetPhases();      

      // this.LogInfo( context.variants );

      phases.ForEach( 
        ( phase )=>
        {
          context.phase = phase;

          var modules = GetShaderGenerationModules( phase );

          // this.LogInfo( "PHASE", phase, ">>", context.variants, "MODS:", modules == null ? "-" : ( modules.Map( m => m.GetType().Name ) ));

          if ( modules == null )
          {
            return;
          }

          modules.ForEach(
            ( m )=>
            {
              var variants =  m.GetVariants( context );
              
              if ( variants == null )
              {
                return;
              }

              variants.ForEach( 
                c => 
                { 
                  c.shaderCode.ForEach( 
                    cs =>
                    {
                      cs.phase = phase; 
                      cs.sortableCode = cs.sortableCode && m.CodeIsSortable( phase );  
                    }
                  );
                }
              );

              this.LogInfo( phase, m.GetType().Name, ">>", variants.Count );

              context.AddVariants( variants );
            }
          );
        }
      );

      


      return context;
    }

    public void UpdateShader()
    {
      numVariants = 0;

      var context = GenerateShaderVariants();

      numVariants = context.variants.Count;
      // this.LogInfo( "Created variants:", context.variants );

      var first = true;

      foreach ( var variant in context.variants )
      {
        var shaderCode = variant.GetCode( context );
        var absolutePath = path;

        if ( absolutePath.StartsWith( "res://" ) || absolutePath.StartsWith( "user://" ) ) 
        {
          absolutePath = ProjectSettings.GlobalizePath( path );
        }

        // this.LogInfo( shaderCode );
        var filePath = FilePath.Join( absolutePath, shaderName + variant.variantName + ".gdshader" );
        FilesSync.SaveUTF8( filePath, shaderCode );

        if ( sendGodotUpdateMessage )
        {
          GodotEditorHelper.UpdateFile( filePath );
        }
        
        if ( first )
        {
          if ( shader != null )
          {
            shader.Code = shaderCode;
          }
          else
          {
            shader = ResourceLoader.Load<Shader>( filePath );
          }
          
        }

        first =false;

      }
     
    }
  }
}