using Godot;
using System.Reflection;
using System.Collections.Generic;
using System.Text;

namespace Rokojori
{ 
  [GlobalClass]
  public partial class SineWaveTest: Node
  { 
    [Export]
    public float frequency = 100;

    [Export]
    public AudioStreamPlayer player;

    AudioStreamGeneratorPlayback _playback;

    public override void _Ready()
    {
      CreateGraph();

      if ( player.Stream is AudioStreamGenerator generator ) 
      {
        ag.sampleRate = generator.MixRate;

        player.Play();
        _playback = (AudioStreamGeneratorPlayback) ( player.GetStreamPlayback() );

       Fill();

      }


    } 

    WaveTableGenerator generator;
    AudioGraph ag;
    Constant constant;

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

    void Fill()
    {
      if ( _playback == null )
      {
        return;
      }

      constant.SetConstant( frequency );
      
      int framesAvailable = _playback.GetFramesAvailable();

      if ( framesAvailable == 0 )
      {
        return;
      }

      int blocks = 0;

      while ( framesAvailable > frameBuffer.Count )
      {
        ProcessAudio();

        blocks ++;
      }

    
      var frameBufferData = frameBuffer.ToArray();      

      var bufferSlice = new Vector2[ framesAvailable ];
      System.Array.Copy( frameBufferData, 0, bufferSlice, 0, framesAvailable );

      _playback.PushBuffer( bufferSlice );

      var rest = new Vector2[ frameBuffer.Count - framesAvailable ];
      System.Array.Copy( frameBufferData, framesAvailable, rest, 0, rest.Length );

      frameBuffer = new List<Vector2>( rest );    
    }
    
    List<Vector2> frameBuffer = new List<Vector2>();

    void ProcessAudio()
    {
      ag.Process();

      var outputBuffer = generator.output.GetOutputStreamBuffer();
      var data = new Vector2[ ag.bufferSize ];

      for ( int i = 0; i < ag.bufferSize ; i++ )
      {
        data[ i ] = outputBuffer[ i ] * Vector2.One;
      }

      frameBuffer.AddRange( data );
    }

    public void CreateGraph()
    {
      ag = new AudioGraph();

      var sine   = new float[ 2048 ];
      var square = new float[ 2048 ];

      for ( int i = 0; i < sine.Length; i++ )
      {
        var p = i / 2048f;

        var v = Mathf.Sin( p * Mathf.Pi * 2.0f );

        sine[ i ] = v;
        square[ i ] = i < sine.Length / 2 ? 1 : -1;
      }

      var sines = new Vector2[ 10 ];

      sines[ 0 ] = Vector2.Zero;

      for ( int i = 1; i < sines.Length; i++ )
      {
        sines[ i ] = new Vector2( Mathf.Pow( 0.5f, i - 1 ), 0 );
      }

      var waveTable = WaveTable.FromSines( sines );
      waveTable.Normalize();
      generator = new WaveTableGenerator( ag, waveTable );

      constant = new Constant( ag, frequency );

      constant.output.ConnectTo( generator.frequency );

      ag.output = generator;
      
    }
  }
}