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


namespace Rokojori
{  
  [GlobalClass]
  public partial class TimeLineScheduler:Node
  {  
    public static int ScheduleEventIn( TimeLine timeLine, float offset, Action<int> action, bool persistent = false )
    {
      var scheduler = Unique<TimeLineScheduler>.Get();
      return scheduler._ScheduleEventIn( timeLine, offset, action, persistent );
    }

    int _ScheduleEventIn( TimeLine timeLine, float offset, Action<int> action, bool persistent = false )
    {
      var tm = Unique<TimeLineManager>.Get();
      var tIndex = tm.GetTimeLineIndex( timeLine );
      var position = tm.GetPosition( tIndex ) + offset;
      return _ScheduleEventAt( timeLine, position, action, persistent );
    }


    public static int ScheduleEventAt( TimeLine timeLine, float position, Action<int> action, bool persistent = false )
    {
      var scheduler = Unique<TimeLineScheduler>.Get();
      return scheduler._ScheduleEventAt( timeLine, position, action, persistent );
    }

    int _ScheduleEventAt( TimeLine timeLine, float position, Action<int> action, bool persistent = false )
    {
      AttachListeners();
      var tm = Unique<TimeLineManager>.Get();
      var tIndex = tm.GetTimeLineIndex( timeLine );
      var id = tm.CreateID();
      _eventActions[ id ] = action;
      tm.ScheduleEvent( tIndex, position, id, persistent );
      return id;
    }

    public static int ScheduleLoopEvent( TimeLine timeLine, float loopDuration, float loopOffset, Action<int> action, bool persistent = false )
    {
      var scheduler = Unique<TimeLineScheduler>.Get();
      return scheduler._ScheduleLoopEvent( timeLine, loopDuration, loopOffset, action, persistent );
    }  

    int _ScheduleLoopEvent( TimeLine timeLine, float loopDuration, float loopOffset, Action<int> action, bool persistent = false )
    {
      AttachListeners();
      var tm = Unique<TimeLineManager>.Get();
      var tIndex = tm.GetTimeLineIndex( timeLine );
      var id = tm.CreateID();
      _eventActions[ id ] = action;
      tm.ScheduleLoopEvent( tIndex, loopDuration, loopOffset, id, persistent );
      return id;
    }

    public static void RemoveEvent( TimeLine timeLine, int id )
    {
      var tm = Unique<TimeLineManager>.Get();
      var tIndex = tm.GetTimeLineIndex( timeLine );
      tm.RemoveEvent( tIndex, id );
    }


    public static int ScheduleSpanAt( TimeLine timeLine, float start, float end, Action<int,TimeLineSpanUpdateType> action, bool persistent = false )
    {
      var scheduler = Unique<TimeLineScheduler>.Get();
      return scheduler._ScheduleSpan( timeLine, start, end, action, persistent );
    }

    public static int ScheduleSpanIn( TimeLine timeLine, float offset, float duration, Action<int,TimeLineSpanUpdateType> action, bool persistent = false )
    {
      var scheduler = Unique<TimeLineScheduler>.Get();
      var tm = Unique<TimeLineManager>.Get();
      var tIndex = tm.GetTimeLineIndex( timeLine );
      var position = tm.GetPosition( tIndex );
      var start = position + offset;
      var end   = start + duration;
      return scheduler._ScheduleSpan( timeLine, start, end, action, persistent );
    }

    int _ScheduleSpan( TimeLine timeLine, float start, float end, Action<int,TimeLineSpanUpdateType> action, bool persistent = false )
    {
      AttachListeners();
      var tm = Unique<TimeLineManager>.Get();
      var tIndex = tm.GetTimeLineIndex( timeLine );
      var id = tm.CreateID();
      _spanActions[ id ] = action;
      tm.ScheduleSpan( tIndex, start, end, id, persistent );
      return id;
    }

    bool _listenersAttached = false;
    

    HashSet<int> _persistentEventsAndSpans = new HashSet<int>();
    Dictionary<int,Action<int>> _eventActions = new Dictionary<int, Action<int>>();
    Dictionary<int,Action<int,TimeLineSpanUpdateType>> _spanActions = new Dictionary<int, Action<int,TimeLineSpanUpdateType>>();

    
    void AttachListeners()
    {      
      if ( _listenersAttached )
      {
        return;
      }

      _listenersAttached = true;

      var tm = Unique<TimeLineManager>.Get();

      tm.onEvent.AddAction(
        ( id )=>
        {
          OnEvent( id );
        }
      );

      tm.onSpan.AddAction(
        ( t )=>
        {
          OnSpan( t.Item1 , t.Item2 );
        }
      );
    }

    public void _RemoveEventEntry( int id )
    {
      _eventActions.Remove( id );
    }

    public void _RemoveSpanEntry( int id )
    {
      _spanActions.Remove( id );
    }



    void OnEvent( int id )
    {
      if ( ! _eventActions.ContainsKey( id ) )
      {
        return;
      }

      _eventActions[ id ]( id );

    }

    void OnSpan( int id, TimeLineSpanUpdateType type )
    {
      if ( ! _spanActions.ContainsKey( id ) )
      {
        return;
      }

      _spanActions[ id ]( id, type  );

    }
  }
}