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


namespace Rokojori
{  
  
  [Tool]
  [GlobalClass, Icon("res://addons/rokojori_action_library/Icons/RenderingManager.svg") ]
  public partial class RenderingManager:Node
  {   
    [ExportToolButton( "Ensure Global Shader Properties")]
    public Callable ensureGlobalShaderPropertiesButton => Callable.From(
      ()=>
      {
        EnsureGlobalShaderProperties();
      }
    );

    
    [ExportToolButton( "Update Shaders")]
    public Callable updateShadersButton => Callable.From(
      ()=>
      {
        UpdateShaders();
      }
    );

    void UpdateShaders()
    {
      shaders.ForEach(
        ( s )=>
        {
          var code = RenderingServer.Singleton.ShaderGetCode( s.GetRid() );
          code += "\n ";

          RenderingServer.Singleton.ShaderSetCode( s.GetRid(), code );
        }
      );
    }

    [Export]
    public Shader[] shaders;

    [ExportToolButton( "Get Material Code")]
    public Callable getMaterialCodeButton => Callable.From(
      ()=>
      {
        var shaderRID = material._GetShaderRid();
        var code = RenderingServer.Singleton.ShaderGetCode( shaderRID );        

        this.LogInfo( "Shader:", code );
      }
    );

    [Export]
    public Material material;


    
    

    void EnsureGlobalShaderProperties()
    {
      var globalProperties = RenderingServer.GlobalShaderParameterGetList();
      var map = new HashSet<string>();

      foreach ( var g in globalProperties )
      {
        map.Add( g );
      }

      for ( int i = 0; i < data.numGlobalShaderProperties; i++ )
      {
        var p = data.GetGlobalShaderPropertyAt( i );

        this.LogInfo( "Prop:", i );
        
        
        if ( p == null || map.Contains( p.GetPropertyName().propertyName ) )
        {
          continue;
        } 

        
        this.LogInfo( i, ">>", p.GetPropertyName().propertyName );

        p.AddAsGlobalUniform();

      }
    }

    public T GetShaderPropertyByName<T>( ShaderPropertyName name ) where T:ShaderProperty
    {
      if ( data == null )
      {
        return null;
      }

      if ( data.globalShaderPropertyBlocks != null )
      {
        foreach ( var b in data.globalShaderPropertyBlocks )
        {
          var p = GetShaderPropertyByName( b.shaderProperties, name );

          if ( p != null )
          {
            return p as T;
          }
        }
      }

      return GetShaderPropertyByName( data.globalShaderProperties, name ) as T;
    }

    public ShaderProperty GetShaderPropertyByName( ShaderProperty[] properties, ShaderPropertyName name )
    {
      if ( properties == null )
      {
        return null;
      }

      return properties.Find( p => p.GetPropertyName().propertyName == name.propertyName );
    }
      

    [Export]
    public bool ensureGlobalShaderPropertiesOnReady = true;

    [Export]
    public bool updateShadersOnReady = true;

    [Export]
    public RenderingManagerData data = new RenderingManagerData();

    List<AssignedShaderProperty> _assignedProperties = new List<AssignedShaderProperty>();

    public override void _Ready()
    {
      if ( ensureGlobalShaderPropertiesOnReady )
      {
        EnsureGlobalShaderProperties();
      }

      if ( updateShadersOnReady )
      {
        UpdateShaders();
      }      

    }

    public override void _Process( double delta )
    {
      UpdateProperties();
    }

    void UpdateProperties()
    {
     
      if ( data == null )
      {
        return;
      }

      if ( _assignedProperties.Count != data.numGlobalShaderProperties )
      {
        CreatePropertyList();
      } 

      
      
      for ( int i = 0; i < data.numGlobalShaderProperties; i++ )
      {
        var p = data.GetGlobalShaderPropertyAt( i );
        
        if ( p != _assignedProperties[ i ].property  )
        {
          _assignedProperties.Clear();
          CreatePropertyList();
          UpdateProperties();
          return;
        }

        _assignedProperties[ i ].Update();
      }
    }

    void CreatePropertyList()
    {
      _assignedProperties.Clear();

      for ( int i = 0; i < data.numGlobalShaderProperties; i++ )
      {
        _assignedProperties.Add( AssignedShaderProperty.From( data.GetGlobalShaderPropertyAt( i ) ) );
      }
    }

    public static int GetStencilReferenceIndex( StencilLayer stencilLayer )
    {
      if ( stencilLayer == null )
      {
        return -1;
      }

      var rm = Unique<RenderingManager>.Get();

      if ( rm == null )
      {
        return stencilLayer.fallBackIndex;
      }

      var rmIndex = rm.data.stencilLayers.IndexOf( stencilLayer );
      
      return rmIndex == -1 ? stencilLayer.fallBackIndex : ( rmIndex + 1 );
    }
  }
}