
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
using System;
using Godot;
using System.Threading.Tasks;


namespace Rokojori
{  
  public partial class TimeLineManager:NetworkNode
  {        
    public static void RemoveEvent( TimeLine timeline, int id )
    {
      RJLog.Log( "Removing tick:", timeline, id );

      timeline = TimeLineManager.Ensure( timeline );
      var runner = timeline.runner;

      runner.RemoveEvent( id );

    }

    public static TimeLineCallback ScheduleCallback( TimeLine timeline, System.Action<TimeLineCallback> callback )
    {
      timeline = TimeLineManager.Ensure( timeline );
      var runner = timeline.runner;
      var eventID = TimeLineManager.CreateID();

      return runner._ScheduleCallback( callback, eventID );

    }

    static async Task RunLater( float delay, TimeLineEvent te, Action<TimeLineEvent> callback, Node context = null )
    {

      await context.RequestNextFrame();
      await context.WaitForAsyncTimer( delay );

      callback( te );

    }

    public static TimeLineEvent ScheduleEvent( TimeLine timeline, float position, Action<TimeLineEvent> callback, Node context = null )
    {
      timeline = TimeLineManager.Ensure( timeline );

      
      if ( Engine.IsEditorHint() )
      {
        var te = new TimeLineEvent();
        var time = timeline.position;
        RunLater( position - time, te, callback, context );
        return te;
      }

      
      var runner = timeline.runner;
      var eventID = TimeLineManager.CreateID();

      return runner._ScheduleEvent( position, eventID, false, callback );
    }

    public static TimeLineEvent ScheduleEventIn( TimeLine timeline, float offset, Action<TimeLineEvent> callback, Node context = null)
    {
      timeline = TimeLineManager.Ensure( timeline );
      var position = timeline.position + offset;
      return ScheduleEvent( timeline, position, callback, context );
    }

    public static TimeLineEvent ScheduleEventWith( Duration duration, Action<TimeLineEvent> callback, Node context = null )
    {
      return ScheduleEventIn( duration.timeLine, duration.GetDurationInSeconds(), callback, context );
    }

    public static TimeLineEvent ScheduleLoopEvent( TimeLine timeline, float duration, float offset, Action<TimeLineEvent> callback )
    {
      timeline = TimeLineManager.Ensure( timeline );
      var runner = timeline.runner;
      var eventID = TimeLineManager.CreateID();

      return runner._ScheduleLoopEvent( duration, offset, eventID, false, callback );
    }


    public static TimeLineSpan ScheduleSpan( TimeLine timeline, float start, float end, Action<TimeLineSpan,TimeLineSpanUpdateType> callback, Node context = null )
    {
      timeline = TimeLineManager.Ensure( timeline );

      var delay = start - timeline.position;
      var duration = end - start;
      return ScheduleSpanIn( timeline, delay, duration, callback, context );
      
      /*if ( Engine.IsEditorHint() )
      {
        var span = new TimeLineSpan();
        span.id = GodotRandom.Get().IntegerInclusive( 0, 1000000 );
        // var root = Root.Window();
        // RJLog.Log( root, RokojoriPlugin.Get() );

        RunInEditor( span, end - start, callback, context );

        return span;
      }

      timeline = TimeLineManager.Ensure( timeline );
      var runner = timeline.runner;
      var spanID = TimeLineManager.CreateID();
      var duration = end - start;

      return runner._ScheduleSpan( start, end, spanID, false, callback );*/
    }

    public static TimeLineSpan ScheduleSpanWith( Duration duration, Action<TimeLineSpan,TimeLineSpanUpdateType> callback, Node context = null )
    {
      return ScheduleSpanIn( duration.timeLine, 0, duration.GetDurationInSeconds(), callback, context );
    }

    static async Task RunSpanInEditor( TimeLineSpan span, float duration, Action<TimeLineSpan,TimeLineSpanUpdateType> callback, Node context = null )
    {
      if ( context == null )
      {
        return;
      }

      for ( int i = 0; i < 3; i++ )
      {
        await context.RequestNextFrame();
      }
      
      var elapsed = 0f;
      var isFirst = true;

      var startTime = Time.GetTicksMsec() / 1000.0f;
      span.start = startTime;
      span.end = startTime + duration;
      span.wasInside = true;
      span.timeLine = new TimeLine();

      while ( elapsed < duration )
      {
        var type = isFirst ? TimeLineSpanUpdateType.Start : TimeLineSpanUpdateType.InSpan;
        isFirst = false;
        callback( span, type );
        await context.RequestNextFrame();

        elapsed = ( Time.GetTicksMsec() / 1000.0f - startTime );
      }

      callback( span, TimeLineSpanUpdateType.End  );
    }

    public static TimeLineSpan ScheduleSpanIn( TimeLine timeline, float delay, float duration, Action<TimeLineSpan,TimeLineSpanUpdateType> callback, Node context = null )
    {
      timeline = TimeLineManager.Ensure( timeline );

      if ( Engine.IsEditorHint() )
      {
        var span = new TimeLineSpan();
        span.id = GodotRandom.Get().IntegerInclusive( 0, 1000000 );
        // var root = Root.Window();
        // RJLog.Log( root, RokojoriPlugin.Get() );

        RunSpanInEditor( span, duration, callback, context );

        return span;
      }

      
      var runner = timeline.runner;

      if ( runner == null )
      {
        return null;
      }

      var start = timeline.position + delay;
      var end = start + duration;

      var spanID = TimeLineManager.CreateID();

      return runner._ScheduleSpan( start, end, spanID, false, callback );
    }

  
  }
}