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

namespace Rokojori
{ 
  [Tool]
  [GlobalClass]
  public partial class GeometryNormalOffset:GeometryModifier
  {   
    public enum Mode
    {
      Object,
      World
    }

    [Export]
    public Mode mode;

    [ExportGroup("Texture")]
    [Export]
    public bool textureScaleEnabled = false;

    [Export]
    public string textureUVChannel = "UV";

    [Export]
    public bool textureRepeat = true;

    [Export]
    public TextureModule.TextureFilter textureFilter = TextureModule.TextureFilter.Linear_MipMap_Anisotropic;

    public override bool IsSameType( GeometryModifier modifier )
    {
      if ( modifier is GeometryNormalOffset no )
      {
        return no.mode == mode;
      }

      return false;
    }

    public override List<ShaderCode> GetGeometryCode( ShaderGenerationContext context, int offsetIndex )
    {
      if ( Mode.Object == mode )
      {
        return ObjectNormal( context, offsetIndex );
      }      
      else if ( Mode.World == mode )
      {
        return WorldNormal( context, offsetIndex );
      } 

      return null;
    }

    List<ShaderCode> ObjectNormal( ShaderGenerationContext context, int offsetIndex )
    {
      var suffix = GetSuffix( offsetIndex );      

      if ( ShaderPhase.Variables == context.phase )
      {   
        var hints = new List<string>();        

        hints.Add( "hint_default_white" );         
        hints.Add( textureRepeat ? "repeat_enable" : "repeat_disable" );
        hints.Add( "filter_" + ( textureFilter + "" ).ToLower() );
        
        var sampler = UniformSampler( $"objectNormalOffsetTexture{suffix}", hints.Join( ", " ) );

        if ( ! textureScaleEnabled )
        {
          sampler = "";
        }

        return AsUniformGroup( $"ObjectNormalOffset{suffix}",
          
        $@"
        uniform float objectNormalOffset{suffix} = 0.1;
        {sampler}
        
        "
          
        );
           
      }

      if ( ShaderPhase.Vertex == context.phase )
      {
        var samplerMultply = 
        $@"
        float sampledObjectNormalOffset{suffix} = texture( objectNormalOffsetTexture{suffix}, {textureUVChannel} ).r;
        objectNormalOffsetAmount{suffix} *= sampledObjectNormalOffset{suffix};
        ";

        if ( ! textureScaleEnabled )
        {
          samplerMultply = "";
        }

        return ToInnerBlock( $"ObjectNormalOffset{suffix}",
          
        $@" 
        
        {{

        float objectNormalOffsetAmount{suffix} = objectNormalOffset{suffix};
        {samplerMultply}
        VERTEX += NORMAL * objectNormalOffsetAmount{suffix};

        }}
        "
                  
        );
      }

      return null;
    }


    List<ShaderCode> WorldNormal( ShaderGenerationContext context, int offsetIndex )
    {
      var suffix = GetSuffix( offsetIndex );      

      if ( ShaderPhase.Includes == context.phase )
      { 
        return IncludeTransformLibrary();
      }
      else if ( ShaderPhase.Variables == context.phase )
      {   
        var hints = new List<string>();        

        hints.Add( "hint_default_white" );         
        hints.Add( textureRepeat ? "repeat_enable" : "repeat_disable" );
        hints.Add( "filter_" + ( textureFilter + "" ).ToLower() );
        
        var sampler = UniformSampler( $"worldNormalOffsetTexture{suffix}", hints.Join( ", " ) );

        if ( ! textureScaleEnabled )
        {
          sampler = "";
        }

        return AsUniformGroup( $"WorldNormalOffset{suffix}",
          
        $@"
        uniform float worldNormalOffsetScale{suffix} = 1.0;
        uniform float worldNormalWorldOffset{suffix} = 0.1;
        uniform float worldNormalViewOffset{suffix} = 0.1;
        uniform float worldNormalScaleType{suffix}:hint_range( 0.0, 1.0 ) = 1.0;
        {sampler}
        
        "
          
        );
           
      }

      if ( ShaderPhase.Vertex == context.phase )
      {
        var samplerMultply = 
        $@"
        float sampledWorldNormalOffset{suffix} = texture( worldNormalOffsetTexture{suffix}, {textureUVChannel} ).r;
        worldNormalOffsetAmount{suffix} *= sampledWorldNormalOffset{suffix};
        ";

        if ( ! textureScaleEnabled )
        {
          samplerMultply = "";
        }

        return ToInnerBlock( $"WorldNormalOffset{suffix}",
          
        $@" 
        
        {{

        float vertexDistance = length( localToWorld( VERTEX, MODEL_MATRIX ) - CAMERA_POSITION_WORLD );
        float worldNormalOffsetAmount{suffix} = mix( worldNormalWorldOffset{suffix}, vertexDistance * worldNormalViewOffset{suffix}, worldNormalScaleType{suffix} );

        {samplerMultply}
        
        
        vec3 worldNormal = localToWorldDirection( NORMAL, MODEL_MATRIX ) * worldNormalOffsetAmount{suffix} * worldNormalOffsetScale{suffix};
        vec3 localNormal = worldToLocalDirection( worldNormal, MODEL_MATRIX );

        VERTEX += localNormal;

        }}
        "
                  
        );
      }

      return null;
    }
  }
}