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

namespace Rokojori
{

  public class Noise
  {
    public static int CreateSeed( Vector3 p )
    {
      var x = p.X;
      var y = p.Y;
      var z = p.Z;

      x += 20422.4225f;
      y += 12353.299336f;
      z += 2139.4f;

      x += 3254f;
      x = x % 157.3245f;

      y += 8994f;
      y = y % 17.33636634f;

      z += 3.4f;
      z = z % 15.99925f;
      

      var seed = 35891.323903f + x * 1000 + y * 11000 + z*20935;
      seed = seed % 0.12492f;
      seed = Mathf.Cos( seed * 2.235f ) + Mathf.Sin( z * 2102491054 ) + Mathf.Cos( y*48924.9042f);

      var intSeed = Mathf.RoundToInt( seed * 20898977 );
      intSeed = Mathf.RoundToInt( intSeed + x * 42.3f -z *235 );
      intSeed = intSeed << 2;
      seed += y % z;
      seed += intSeed;
      seed = seed % 0.0012f; 
      seed += y * 100203.3f;
      seed -= z * 0122f;

      seed += x * 13.35f;
      seed = seed % ( y *32535 + z + 0.1221f );
      seed -= x * 2f;
      seed = seed % (x * 0.02f );
      seed += 2005235;
      seed = seed % 1157.5f;

      return Mathf.RoundToInt( seed * 10000 );    
    }


    public static float Random( Vector2 v )
    {
      var d = Math2D.Dot( v, new Vector2( 12.9898f, 78.233f ) );
      return MathX.Fract( Mathf.Sin ( d ) * 43758.5453123f );
    }

    public static float Random( Vector3 v )
    {
      var d = Math3D.Dot( v, new Vector3( 12.9898f, 78.233f, 23.7219f ) );
      return MathX.Fract( Mathf.Sin ( d ) * 43758.5453123f );
    }

    public static float PerlinXZ( Vector3 position )
    {
      return Perlin( Math2D.XZ( position ) );
    } 

    public static Vector2 PerlinOffset( Vector2 position, float max, Vector2 scale, Vector2 offset, RandomEngine random )
    {
      var transformedPoint = position * scale + offset;
      
      var value = Perlin( transformedPoint ) * max;

      var angle = random.AngleRadians(); 
      var movement = Math2D.Circle( angle, value );

      return position + movement;
    }

    public static float Perlin( Vector2 position )
    {
      var index = position.Floor();
      var lerp  = Math2D.Fract( position );

      var uv = Math2D.SmoothStep( Vector2.Zero, Vector2.One, lerp );

      var a = Random( index );
      var b = Random( index + new Vector2( 1.0f, 0.0f ) );
      var c = Random( index + new Vector2( 0.0f, 1.0f ) );
      var d = Random( index + new Vector2( 1.0f, 1.0f ) );

      return MathX.Sample( a, b, c, d, uv );
    }
    
    public static float Perlin( Vector3 position )
    {
      var index = position.Floor();
      var lerp  = Math3D.Fract( position );

      var uvw = Math3D.SmoothStep( Vector3.Zero, Vector3.One, lerp );

      var ba = Random( index );
      var bb = Random( index + new Vector3( 1.0f, 0.0f, 0.0f ) );
      var bc = Random( index + new Vector3( 0.0f, 1.0f, 0.0f ) );
      var bd = Random( index + new Vector3( 1.0f, 1.0f, 0.0f ) );

      var ta = Random( index + new Vector3( 0.0f, 0.0f, 1.0f ) );
      var tb = Random( index + new Vector3( 1.0f, 0.0f, 1.0f ) );
      var tc = Random( index + new Vector3( 0.0f, 1.0f, 1.0f ) );
      var td = Random( index + new Vector3( 1.0f, 1.0f, 1.0f ) );

      return MathX.Sample( ba, bb, bc, bd, ta, tb, tc, td, uvw );

    }

    public static float PerlinPolar( Vector3 position )
    {
      return Perlin( position ) * 2 - 1;
    }

    static readonly Vector3 Perlin3OffsetY = new Vector3( 31789.23f, -2101.23f, 912990.21f );
    static readonly Vector3 Perlin3OffsetZ = new Vector3( 91290.0340f, 12921, -99122.21424f );

    public static Vector3 Perlin3( Vector3 position, Vector3? offsetY =null, Vector3? offsetZ = null )
    {
      var offY = ( Vector3 ) ( offsetY == null ? Perlin3OffsetY : offsetY );
      var offZ = ( Vector3 ) ( offsetZ == null ? Perlin3OffsetZ : offsetZ );

      var x = Perlin( position );
      var y = Perlin( position + offY );
      var z = Perlin( position + offZ );

      return new Vector3( x, y, z );

    }

    public static Vector3 PerlinPolar3( Vector3 position, Vector3? offsetY =null, Vector3? offsetZ = null )
    {
      return Perlin3( position, offsetY, offsetZ ) * 2 - Vector3.One;
    }
  }

}