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

namespace Rokojori
{ 
  [Tool]
  [GlobalClass]
  public partial class SpatialShaderGenerator:ShaderGenerator
  { 
    [Export]
    public SpatialShaderData data = new SpatialShaderData();

    public override List<ShaderPhase> GetPhases()
    {
      return new List<ShaderPhase>()
      {
        ShaderPhase.Header,
        ShaderPhase.RenderMode,
        ShaderPhase.Includes,
        ShaderPhase.Variables,
        ShaderPhase.Functions,
        ShaderPhase.Vertex,
        ShaderPhase.Fragment,
        ShaderPhase.Light
      };
    }

    public string RokojoriInfo()
    {
      var allModules = GetAllModules();
      var hashValues = allModules.Map( a => a.GetShaderCacheHash() );
      hashValues.Sort();
      var hash = hashValues.Join( "-" );
      var allInfos = allModules.Map( a => a.GetShaderCacheData() );

      return $"// Rokojori Shader @{hash}: \n/*\n" + allInfos.Join( "\n" ) + "\n*/\n";
    }

    List<ShaderGenerationModule> GetAllModules()
    {
      return GetHeaderModules().Concat( GetMainModules() );
    }

    List<ShaderGenerationModule> GetHeaderModules()
    { 
      List<ShaderGenerationModule> list =  [ 
        data.transparency,
        data.shading,
        data.stencil
      ];

      return list.FilterNulls();
    }

    List<ShaderGenerationModule> GetMainModules()
    { 
      List<ShaderGenerationModule> list = [         
        data.uv,
        data.geometry,
        data.varying,
        data.masks,
        data.albedo,
        data.transparency,
        data.alpha,
        data.fading,
        data.normalMap,
        data.occlusion,
        data.roughness,
        data.metallic,
        data.specular,
        data.emission,
        data.backlight,
        data.subsurfaceScattering
      ];
      
      return list.FilterNulls();
    }

    public override List<ShaderGenerationModule> GetShaderGenerationModules( ShaderPhase phase )
    {
      if ( ShaderPhase.Header == phase )
      {
        var info = RokojoriInfo();
        return
        [
          new ShaderRawModule( "\n\nshader_type spatial;\n" ),
          new ShaderRawModule( $"\n{info}\n")
        ];
      }

      if ( ShaderPhase.RenderMode == phase )
      {
        List<ShaderGenerationModule> modules = 
        [
          new ShaderRawModule( "render_mode " ),
          data.transparency,
          new ShaderRawModule( ", " ),
          data.shading,
          new ShaderRawModule( ";\n" ),
        ];

        if ( data.stencil != null )
        {
          modules.Add( new ShaderRawModule( data.stencil.ToShaderCode() + "\n" ) );
        }

        modules.Add( new ShaderRawModule( "\n" ) );

        return modules;
      }

      var mainModules = GetMainModules();

      if ( ShaderPhase.Includes == phase || ShaderPhase.Variables == phase || ShaderPhase.Functions == phase )
      {
        List<ShaderGenerationModule> preModules = [         
          data.transparency,
          data.shading ]
        ;

        return preModules.Concat( mainModules.CloneWithout( [ data.transparency ] ) );
      }

      if ( ShaderPhase.Vertex == phase )
      {
        return new List<ShaderGenerationModule>()
        {
          new ShaderRawModule( "\nvoid vertex()\n{\n\n" )
        }
        .Concat( mainModules )
        .Concat
        (
          new ShaderRawModule( "\n}\n" )
        );
      }

      if ( ShaderPhase.Fragment == phase )
      {
        return new List<ShaderGenerationModule>()
        {
          new ShaderRawModule( "\nvoid fragment()\n{\n\n" ) 
        }
        .Concat( mainModules )
        .Concat
        (
          new ShaderRawModule( "\n}\n" )
        );
      }


      return null;

    }
  }
}