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

namespace Rokojori
{ 
  [Tool]
  [GlobalClass]
  public abstract partial class SpatialOverlayLayer:ShaderGenerationModule
  { 
    [Export]
    public SpatialMask[] masks;

    [Export]
    public SpatialMaskFormulaType maskFormulaType = SpatialMaskFormulaType.Multiply;

    [Export]
    public string customMaskFormula;

    public enum OverlayMode
    {
      Mix,
      Blend_Alpha
    }

    [Export]
    public OverlayMode overlayMode;

    public enum ChannelType
    {
      RGBA,
      RGB,
      RG,
      R,
      G,
      B,
      A,
      Float
    }

    public ChannelType channelType = ChannelType.RGB; 

    public string GetDataType()
    {
      if ( ChannelType.RGBA == channelType )
      {
        return "vec4";
      }

      if ( ChannelType.RGB == channelType )
      {
        return "vec3";
      }

      if ( ChannelType.RG == channelType )
      {
        return "vec2";
      }

      return "float";
    }


    public bool IsColorBlendMode()
    {
      List<OverlayMode> modes = 
      [
        OverlayMode.Blend_Alpha
      ];

      return modes.IndexOf( overlayMode ) != -1;
    }


    public List<ShaderCode> GetOverlayCode( ShaderGenerationContext context, string name, string oldValue, string targetValue )
    { 
      var list = new List<ShaderCode>();
      var currentValueName = $"{name}Value";

      if ( context.isFragmentPhase )
      {
        var dataType = GetDataType();
        var initData = $"{dataType} {currentValueName};\n";
        list.Add( ToCode( initData.Indent( "  " ) ) );

        var valueCode = GetLayerCode( context, name, currentValueName );
        list.Add( valueCode );
        var maskName = $"{name}CombinedMask";
        var maskCode = SpatialMaskFormulaTypeUtility.GetFragmentMaskCode( 
          context, maskFormulaType, customMaskFormula, masks, name, maskName );
        list.Add( maskCode );

        

        
        if ( OverlayMode.Mix == overlayMode )
        {
          var blendCode = 
          @$"
        
          {targetValue} = mix( {oldValue}, {currentValueName}, {maskName} );

          ";

          list.Add( ToCode( blendCode.Indent( "  " ) ) );
        }
        else if ( IsColorBlendMode() )
        {
          var blendModeFunction = ( overlayMode + "" ).Replace( "Blend_", "" ).ToLower();

          var blendCode = 
          @$"
        
          {targetValue} = blend_{blendModeFunction}( {oldValue}, fade( {currentValueName} * {maskName} ) );

          ";

          list.Add( ToCode( blendCode.Indent( "  " ) ) );
        }        
      }
      else
      {
        var maskIndex = 0;

        masks.ForEach( 
          ( m )=>
          {
            var maskName = $"{name}Mask{maskIndex}";
            var maskCode = m.GetMaskCode( context, name, maskName );

            if ( maskCode != null )
            {
              list.Add( maskCode );
            }

            maskIndex++;
          }
        );

        var layerCode = GetLayerCode( context, name, currentValueName );

        if ( layerCode != null )
        {
          list.Add( layerCode );
        }

        
      }

      return list;
    }

    public abstract List<ShaderCode> GetLayerCode( ShaderGenerationContext context, string name, string currentValueName );

  }
}