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

namespace Rokojori
{ 
  public abstract partial class ShaderPropertyName:Resource
  { 
    [Export]
    public string propertyName;

    string _cSharpName;

    MultiMap<Type,string,PropertyInfo> _propertyInfoCache = new MultiMap<Type,string,PropertyInfo>();
    
    public abstract RenderingServer.GlobalShaderParameterType GetParameterType();


    public void _Set<[MustBeVariant] T>( Material m, T value )
    {
      if ( m is ShaderMaterial shaderMaterial )
      {

        // RJLog.Log( "_Set", value, value.GetType() );
        var variant = Variant.From<T>( value );
        shaderMaterial.SetShaderParameter( propertyName, variant );
        return;
      }

      _SetStandardValue( m, value );
    }

    public T _Get<[MustBeVariant] T>( Material material, T fallback ) 
    {
      if ( material is ShaderMaterial shaderMaterial )
      {
        return shaderMaterial.GetShaderParameter( propertyName ).As<T>();
      }

      return _GetStandardValue<T>( material, fallback );
    }

    protected void _SetStandardValue( Material material, object value )
    {
      var p = _GetProperty( material );

      if ( p == null )
      {
        return;
      }

      p.SetValue( material, value );
    }

   

    protected T _GetStandardValue<T>( Material material, T fallback )
    {
      var p = _GetProperty( material );

      if ( p == null )
      {
        return fallback;
      }

      var value = p.GetValue( material );

      // RJLog.Log( "GetStandardValue", _cSharpName, p.Name, ">>", value );

      return (T) p.GetValue( material );
    }

    protected PropertyInfo _GetProperty( Material material )
    {
      var type =  material.GetType();

      if ( _cSharpName != null && _cSharpName != "" && _propertyInfoCache.Has( type, _cSharpName ) )
      {
        // RJLog.Log( type, ">>", material, "_cSharpName", _cSharpName );

        return _propertyInfoCache[ type ][ _cSharpName ];
      }

      if ( ! RegexUtility.StartsWithUpperCase( propertyName ) )
      {
        _cSharpName = GDScriptNames.ToCS( propertyName );

        if ( propertyName.StartsWith( "ao" ) )
        {
          _cSharpName = _cSharpName[ 0 ] + ( _cSharpName[ 1 ] + "" ).ToUpper() + _cSharpName.Substring( 2 );
        }
        
      }
      else
      {
        _cSharpName = propertyName;
      }

      var p = type.GetProperty( _cSharpName );
      // RJLog.Log( "_cSharpName", _cSharpName, material );
      _propertyInfoCache.Set( type, _cSharpName, p );

      return _propertyInfoCache[ type ][ _cSharpName ];
    }

  }

  public partial class GetSetShaderPropertyName:ShaderPropertyName
  {
    public override RenderingServer.GlobalShaderParameterType GetParameterType()
    {
      return RenderingServer.GlobalShaderParameterType.Bool;
    }
  }

}