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



namespace Rokojori
{
  [Tool]
  [GlobalClass, Icon("res://addons/rokojori_action_library/Icons/Spline.svg") ]
  public partial class LeafMesh:Node3D
  {
    [Export]
    public MeshInstance3D output;

    [Export]
    public int seed;
    
    [Export]
    public bool update;

    [Export]
    public bool updateAlways;

    [Export]
    public float height;

    [Export]
    public Curve centerStrandShape;

    [Export]
    public float centerStrandShapeTopOffset;

    [Export]
    public float centerStrandShapeBottomOffset;

    [Export]
    public int numStrands;

    [Export]
    public Curve strandHeightPositions;

    [Export]
    public Curve strandShape;

    [Export]
    public Curve strandSize;

    [Export]
    public Curve strandWidth;

    [Export]
    public Curve strandLength;

    [Export]
    public Curve strandAngles;

    [Export]
    public int strandResolution = 20;

    [Export]
    public float positionJitterMax;

    [Export]
    public Vector2 positionJitterScale;

    [Export]
    public Vector2 positionJitterOffset;
    


    public override void _Process( double delta )
    {
      if ( ! ( update || updateAlways ) )
      {
        return;
      }

      update = false;

      try
      {
        Create();
      }
      catch ( System.Exception e )
      {
        RJLog.Error( e );
        updateAlways = false;
      }
    }

    void Create()
    {
      var paths = new List<Path2>();

      for ( int i = 0; i < numStrands; i++ )
      {
        var part = CreateStrandPart( i );

        var t = i / (float) ( numStrands - 1 );
        var ht = strandHeightPositions.Sample( t );
        var y = ht * height;
        
        var turns = strandAngles.Sample( t );
        var rotation = Mathf.DegToRad( turns * 360f );
        
        var trsf = new Transform2D( rotation, new Vector2( 0, y ) );

        part.ApplyTransform( trsf );

        paths.Add( part );

        var mirrored = part.Clone();
        mirrored.MirrorX();

        paths.Add( mirrored );
      }

      var centerPathPoints = new List<Vector2>();

      for ( int i = 0; i < strandResolution; i++ )
      { 
        var t = i / (float)( strandResolution - 1 );
        var y = strandHeightPositions.Sample( t );
        var x = centerStrandShape.Sample( t );

        var size = ( height + centerStrandShapeTopOffset + centerStrandShapeBottomOffset );
        var p = new Vector2( x, y * size - centerStrandShapeBottomOffset );

        centerPathPoints.Add( p );
      }

      for ( int i = 0; i < strandResolution; i++ )
      { 
        var index = ( strandResolution - 1 ) - i;

        var p = centerPathPoints[ index ];
        p.X = -p.X;
        centerPathPoints.Add( p );
      }


      var centerPath = new Path2( centerPathPoints );
      paths.Add( centerPath );

      var random = new LCG();
      random.SetSeed( seed );
      paths.ForEach( p => p.PositionJitter( positionJitterMax, positionJitterScale, positionJitterOffset, random ) );

      var shape = Shape2.UnionAll( paths );

      // RJLog.Log( shape.ToSVGPath() );


      var fillGeometry = shape.CreateFillMeshGeometry();
      var mesh = fillGeometry.GenerateMesh();
      // RJLog.Log( "First Path:", shape.paths[ 0 ].points, shape.paths, "mesh", fillGeometry.indices.Count );
      output.Mesh = mesh;

    }

    Path2 CreateStrandPart( int index )
    {
      var points = new List<Vector2>();
      var u = index / (float)( numStrands - 1 );
      var s = strandSize.Sample( u );
      var l = strandLength.Sample( u ) * s;
      var w = strandWidth.Sample( u ) * s;

      for ( int i = 0; i < strandResolution; i++ )
      {
        var t = i / (float)( strandResolution - 1 );        
        var p = new Vector2( t * l, strandShape.Sample( t ) * w );

        points.Add( p );
      }

      for ( int i = 0; i < strandResolution; i++ )
      {
        var reverseIndex = ( strandResolution - 1 ) - i;
        var lowerPoint = points[ reverseIndex ];
        lowerPoint.Y = -lowerPoint.Y;
        points.Add( lowerPoint );
      }

      var path = new Path2( points );
      return path;
    }

  }
}