
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
using System;
using Godot;
using System.Threading.Tasks;
using System.Text;

namespace Rokojori.Reallusion
{  
  [Tool]
  [GlobalClass]
  public partial class CCImporter:Node
  {  
    [Export]
    public Node3D characterRoot;

    [Export]
    public string path;

    [Export]
    public CCImportConfiguration configuration;

    [Export]
    public bool addCCMaterialsNode = true;

    [Export]
    public CCImportSettings importSettings;

    [Export]
    public CCMaterialGenerator materialGenerator;

    


    [ExportToolButton( "Import")]
    public Callable ImportButton => Callable.From( 
      () =>
      {
        Import();
      }
    );

    [ExportToolButton( "Set Materials")]
    public Callable SetMaterialsButton => Callable.From( 
      () =>
      {
        SetMaterials();
      }
    );

    CCImportFile importFile;

    public void Import()
    {
      this.LogInfo( "Loading:", path );

      importFile = new CCImportTool().Read( path );

      var sb = new StringBuilder();

      importFile.messages.ForEach(
        ( m )=>
        {
          sb.Append( m.type == MessageType.Error ? "Error: " : "Info: " );
          sb.Append( m.content );
          sb.Append( "\n" );
        }
      );

      this.LogInfo( sb );

      importSettings = new CCImportSettings();
      importSettings.configuration = configuration;
      

      var meshIndex = 0;

      importFile.ccObject.meshes.ForEach(
        ( m )=>
        { 
          var meshSettings = new CCMeshSettings();
          meshSettings.meshName = m.name;


          for ( int i = 0; i < m.materials.Count; i++ )
          {
            var materialSetting = new CCMaterialSettings();
            materialSetting.meshName = m.name;
            materialSetting.materialName =  m.materials[ i ].name;
            materialSetting.materialIndex = i;
            materialSetting.materialType = m.materials[ i ].GetMaterialType();

            meshSettings.materials = Arrays.AddEntry( meshSettings.materials, materialSetting );
          }

          importSettings.meshes = Arrays.AddEntry( importSettings.meshes, meshSettings );

          meshIndex ++;
        }
      );
      
    }
  

    public void SetMaterials()
    {
      var meshIndex = -1;

      var ccmaterials = addCCMaterialsNode ? characterRoot.GetOrCreateChild<CCMaterials>( "CC Materials" ) : null;

      if ( ccmaterials != null )
      {
        ccmaterials.Clear();
        ccmaterials.root = characterRoot;
      }

      for ( int i = 0; i < importSettings.meshes.Length; i++ )
      {
        var ms = importSettings.meshes[ i ];

        for ( int j = 0; j < ms.materials.Length; j++ )
        { 
          ms.materials[ j ].configuration = importSettings.configuration;
        }
      }

      if ( importSettings.configuration.setRenderPrioritiesForHair )
      {
        var renderIndex = 0;

        for ( int i = 0; i < importSettings.configuration.preRenderPriorityTargets.Length; i++ )
        {
          var target = importSettings.configuration.preRenderPriorityTargets[ i ];
          var targetName = target.meshName;
          var targetIndex = target.materialSurfaceIndex;

          var mesh = importSettings.meshes.Find( m => m.meshName == targetName );
          
          if ( mesh == null || targetIndex < 0 || targetIndex > mesh.materials.Length )
          {
            this.LogInfo( "Mesh not found:",
             targetName, ":", targetIndex
            );
            continue;
          }


          var material = mesh.materials[ targetIndex ];

          material.assignRenderPriority = true;
          material.materialRenderPriority = renderIndex;

          renderIndex++;
        }

        for ( int i = 0; i < importSettings.meshes.Length; i++ )
        {
          var ms = importSettings.meshes[ i ];

          for ( int j = 0; j < ms.materials.Length; j++ )
          { 
            if ( ms.materials[ j ].materialType.shaderType == CCMaterialType.CCShaderType.RLHair )
            {
              ms.materials[ j ].materialRenderPriorityIndexBack = renderIndex;
              renderIndex ++;
            }
          }
        }

        for ( int i = 0; i < importSettings.meshes.Length; i++ )
        {
          var ms = importSettings.meshes[ i ];

          for ( int j = 0; j < ms.materials.Length; j++ )
          { 
            if ( ms.materials[ j ].materialType.shaderType == CCMaterialType.CCShaderType.RLHair )
            {
              ms.materials[ j ].materialRenderPriorityIndexFront = renderIndex;
              renderIndex ++;
            }
          }
        }
      }

      importFile.ccObject.meshes.ForEach(
        ( m )=>
        { 
          meshIndex ++;
          var meshSettings = importSettings.meshes[ meshIndex ];

          var mesh = characterRoot.FindChild( m.name ) as MeshInstance3D;   

          if ( mesh == null )
          {
            return;
          }       
         

          for ( int i = 0; i < m.materials.Count; i++ )
          {
            var settings = meshSettings.materials[ i ];
            var material = materialGenerator == null ? m.materials[ i ].CreateMaterial( settings ) : 
                                                       materialGenerator.CreateMaterial( m.materials[ i ], settings );


            if ( settings.assignRenderPriority )
            { 
              material.RenderPriority = settings.materialRenderPriority;
            }

            var overwriteMaterial = settings.configuration.GetOverwriteMaterial( m.name, i );

            if ( overwriteMaterial != null )
            {
              material = overwriteMaterial;
            }
            
            mesh.SetSurfaceOverrideMaterial( i, material );

            if ( ccmaterials == null )
            {
              continue;
            }

            if ( settings.materialType.shaderType == CCMaterialType.CCShaderType.RLHair )
            {
              var hairMaterial  = (CustomMaterial) material;
              var materials = Materials.ResolveNextPasses( material );

              materials.ForEach( m => 
                {
                  ccmaterials.hairMaterials = Arrays.AddEntry( ccmaterials.hairMaterials, m );
                }
              );

              ccmaterials.hairSpecular.ReadFrom( hairMaterial );
              ccmaterials.hairMetallic.ReadFrom( hairMaterial );
              ccmaterials.hairRoughness.ReadFrom( hairMaterial );

              
            }
            else if ( 
              settings.materialType.shaderType == CCMaterialType.CCShaderType.RLHead ||
              settings.materialType.shaderType == CCMaterialType.CCShaderType.RLSkin 
            )
            {
              var skinMaterial  = (CustomMaterial) material;
              var materials = Materials.ResolveNextPasses( material );
              materials.ForEach( m => 
                {
                  ccmaterials.skinMaterials = Arrays.AddEntry( ccmaterials.skinMaterials, m );
                }
              );

              ccmaterials.skinAlbedoNoise.ReadFrom( skinMaterial );
              ccmaterials.skinAlbedoNoiseOffset.ReadFrom( skinMaterial );
            }
            
          }

          
        }
      );
    }
  }
    
}