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



namespace Rokojori
{
  [Tool]
  [GlobalClass]
  public partial class MassRenderer:Node3D
  {
    public enum Mode
    {
      MultiMeshInstance3D,
      Combined,
      MeshInstance3D
    }

    [Export]
    public Mode mode = Mode.MultiMeshInstance3D;

    [Export]
    public int randomizeQueue = 1984;

    [ExportGroup("Mesh")]
    [Export]
    public Mesh mesh;

    [Export]
    public Material materialOveride;

    [ExportGroup("Layout")]
    [Export]
    public Vector3[] positions = new Vector3[ 0 ];

    [Export]
    public Vector4[] rotations = new Vector4[ 0 ];

    [Export]
    public Vector3[] scales = new Vector3[ 0 ];

    [ExportGroup("Outputs")]
    [Export]
    public LODMultiMeshInstance3D multiMeshNode;
    [Export]
    public Node3D meshesContainer;
    [Export]
    public MeshInstance3D combinedMesh;

    List<Transform3D> _queuedTransforms = new List<Transform3D>();



    public void QueueObject( Transform3D transform )
    {
      _queuedTransforms.Add( transform );
    }

    public void AddQueued()
    {
      if ( positions == null )
      {
        positions = new Vector3[0];
      }

      if ( rotations == null )
      {
        rotations = new Vector4[0];
      }

      if ( scales == null )
      {
        scales = new Vector3[0];
      }   

      if ( randomizeQueue >= 0 )
      {
        _queuedTransforms = RandomList<Transform3D>.Randomize( _queuedTransforms, LCG.WithSeed( randomizeQueue ) );
      }

      var queuedPositions = Lists.Map( _queuedTransforms, t => t.Origin );
      var queuedRotations = Lists.Map( _queuedTransforms, t => Math3D.QuaternionToVector4( t.Basis.GetRotationQuaternion() ) );
      var queuedScales    = Lists.Map( _queuedTransforms, t => t.Basis.Scale );

      positions = Arrays.Concat( positions, queuedPositions.ToArray() );
      rotations = Arrays.Concat( rotations, queuedRotations.ToArray() );
      scales = Arrays.Concat( scales, queuedScales.ToArray() );

      _queuedTransforms.Clear();
    }

    public void Create()
    {
      AddQueued();

      if ( Mode.MultiMeshInstance3D == mode )
      {
        CreateMultiMeshes();
      }
      else if ( Mode.MeshInstance3D == mode )
      { 
        CreateMeshes();
      } 
      else if ( Mode.Combined == mode )
      {
        CreateCombined();
      }
      
    }

    public void Clear()
    {
      multiMeshNode = Nodes.EnsureValid( multiMeshNode );
      meshesContainer = Nodes.EnsureValid( meshesContainer );
      combinedMesh = Nodes.EnsureValid( combinedMesh );


      if ( multiMeshNode != null )
      {
        multiMeshNode.Multimesh.InstanceCount = 0;
      }

      if ( meshesContainer != null )
      { 
        Nodes.RemoveAndDeleteChildren( meshesContainer );
      } 

      if ( combinedMesh != null )
      {
        combinedMesh.Mesh = null;
      }
    }

    void CreateMultiMeshes()
    {
      multiMeshNode = Nodes.EnsureValid( multiMeshNode );

      if ( multiMeshNode == null )
      {
        multiMeshNode = this.CreateChild<LODMultiMeshInstance3D>();
        multiMeshNode.Multimesh = new MultiMesh();
        multiMeshNode.Multimesh.TransformFormat = MultiMesh.TransformFormatEnum.Transform3D;
      }

      var mm = multiMeshNode.Multimesh;
 
      
      mm.Mesh = mesh;
      mm.InstanceCount = positions.Length;
      
      if ( materialOveride != null )
      {
        multiMeshNode.MaterialOverride = materialOveride;
      }

      for ( int i = 0; i < positions.Length; i++ )
      {
        var trsf = Math3D.TRS( positions[ i ], rotations[ i ], scales[ i ]  );
        mm.SetInstanceTransform( i, trsf );
      }

      
      
    }

    void CreateMeshes()
    {
      if ( meshesContainer == null )
      {
        meshesContainer = this.CreateChild<Node3D>();
      }

      for ( int i = 0; i < positions.Length; i++ )
      {
        var trsf = Math3D.TRS( positions[ i ], rotations[ i ], scales[ i ]  );

        var child = meshesContainer.CreateChild<MeshInstance3D>();
        child.Mesh = mesh;

        if ( materialOveride != null )
        {
          child.MaterialOverride = materialOveride;
        }

        child.GlobalTransform = trsf;
      }
    }

    void CreateCombined()
    {
      if ( meshesContainer == null )
      {
        combinedMesh = this.CreateChild<MeshInstance3D>();
      }

      var combinedMG = new MeshGeometry();

      var meshMG = MeshGeometry.From( mesh as ArrayMesh );

      for ( int i = 0; i < positions.Length; i++ )
      {
        var trsf = Math3D.TRS( positions[ i ], rotations[ i ], scales[ i ]  );
        combinedMG.Add( meshMG, trsf );
      }

      if ( materialOveride != null )
      {
        combinedMesh.MaterialOverride = materialOveride;
      }      

    }
    

  }
}