
using Godot;
using System.Collections.Generic;

namespace Rokojori
{  
  [Tool][GlobalClass, Icon("res://addons/rokojori_action_library/Icons/PlaySound.svg")]
  public partial class PlaySound:Action
  {
    [Export]
    public AudioStreamPlayer3D player;
    
    [Export]
    public AudioFlag overdrivePreventionFlag;


    [Export]
    public Duration overdrivePreventionDuration;

    
    [ExportGroup("Randomize Playback Position")]
    [Export]
    public bool randomizePlaybackPosition = false;
    
    [Export]
    public Duration durationPerSound;

    [Export]
    public float cutBufferLengths = 0;


    [Export]  
    public bool generatePools = true;


    List<AudioStreamPlayer3D> players = new List<AudioStreamPlayer3D>();


    AudioStreamPlayer3D GetFreePlayer()
    {
      if ( players.Count == 0 )
      {
        players.Add( player );
      }

      var freePlayer = players.Find( p => ! p.Playing );

      if ( freePlayer != null )
      {
        return freePlayer;
      }

      var newPlayer = player.GetParent().CreateChildFromDuplicate( player ) as AudioStreamPlayer3D;
      

      // newPlayer.Stream = player.Stream;
      // newPlayer.AttenuationModel = player.att
      // newPlayer.VolumeDb = player.VolumeDb;
      // newPlayer.MaxDb = player.MaxDb;
      // newPlayer.UnitSize = player.UnitSize;
      // newPlayer.AttenuationModel = player.AttenuationModel;
      // newPlayer.PitchScale = player.PitchScale;


      players.Add( newPlayer );
      return newPlayer;

    }

    protected override void _OnTrigger()
    {
     
      var audioManager = Unique<AudioManager>.Get();

      if ( overdrivePreventionDuration != null && overdrivePreventionFlag != null && audioManager != null )
      {
        if ( ! audioManager.CanPlay( overdrivePreventionFlag, overdrivePreventionDuration.GetDurationInSeconds() ) )
        {
          this.LogInfo( "Can't play sound, prevention" );
          return;
        }
        
      }

      
      
      var offset = 0f;

      var player = generatePools ? GetFreePlayer() : this.player;

      if ( ! IsInstanceValid( player ) )
      {
         this.LogInfo( "Can't play sound, invalid" );
        return;
      }
      
      if ( randomizePlaybackPosition )
      {
        var random = LCG.WithSeed( networkSeed );

        var length = player.Stream.GetLength();
        var numOffsets = Mathf.FloorToInt( length / durationPerSound.GetDurationInSeconds() );
        var randomIndex = random.IntegerExclusive( numOffsets );
        offset = randomIndex * durationPerSound.GetDurationInSeconds();

        // this.LogInfo( "Offset", numOffsets, randomIndex, offset );
      }

      player.Play( offset );

      // this.LogInfo( "Play sound", offset, HierarchyName.Of( player ) );

      if ( overdrivePreventionFlag != null && audioManager != null )
      {        
        audioManager.RecordSoundPlaying( overdrivePreventionFlag );
      }

      if ( audioManager != null && randomizePlaybackPosition )
      {
        var tl = TimeLineManager.Ensure( durationPerSound.timeLine );

        var start = tl.position;

        var stopDuration = ( durationPerSound.GetDurationInSeconds() - audioManager.data.bufferCutDuration * cutBufferLengths ) / player.PitchScale;
        TimeLineManager.ScheduleSpanIn( durationPerSound.timeLine, 0, stopDuration, 
        ( span, type )=>
        {         
          var timeNow = tl.position;
          var elapsed = timeNow - start;

          var phase = span.phase;
         
          if ( type == TimeLineSpanUpdateType.End )
          {          
            player.Stop();
            player.Playing = false;
          }
        }
      );
      }
    }
  }
}