internal void Resume(SPComponent behaviour)
        {
            if (_state != RadicalCoroutineOperatingState.Inactive) throw new System.InvalidOperationException("Failed to start RadicalCoroutine. The Coroutine is already being processed.");
            if (behaviour == null) throw new System.ArgumentNullException("behaviour");

            _state = RadicalCoroutineOperatingState.Active;
            _owner = behaviour;
            _token = behaviour.StartCoroutine(this);

            if (_stack.Count > 0 && _stack.Peek() is IPausibleYieldInstruction) (_stack.Peek() as IPausibleYieldInstruction).OnResume();
        }
        private void OnFinish(bool cancelled)
        {
            _stack.Clear();
            _currentIEnumeratorYieldValue = null;
            try
            {
                _owner.StopCoroutine(this); //NOTE - due to a bug in unity, a runtime warning appears if you pass in the Coroutine token while this routine is 'WaitForSeconds'
            }
            catch (System.Exception ex) { Debug.LogException(ex); }

            var ev = System.EventArgs.Empty;
            try
            {
                if (cancelled)
                {
                    _state = RadicalCoroutineOperatingState.Cancelled;
                    if (this.OnCancelled != null) this.OnCancelled(this, ev);
                }
                else
                {
                    _state = RadicalCoroutineOperatingState.Complete;
                    if (this.OnComplete != null) this.OnComplete(this, ev);
                }
            }
            catch (System.Exception ex) { Debug.LogException(ex); }

            if (_immediatelyResumingSignal != null)
            {
                try { _immediatelyResumingSignal(this, ev); }
                catch (System.Exception ex) { Debug.LogException(ex); }
            }
            if (this.OnFinished != null)
            {
                try { this.OnFinished(this, System.EventArgs.Empty); }
                catch (System.Exception ex) { Debug.LogException(ex); }
            }

            _owner = null;
            _token = null;
        }
        /// <summary>
        /// Starts the coroutine, one should always call this method or the StartRadicalCoroutine extension method. Never pass the RadicalCoroutine into the 'StartCoroutine' method.
        /// </summary>
        /// <param name="behaviour">A reference to the MonoBehaviour that should be handling the coroutine.</param>
        /// <param name="disableMode">A disableMode other than Default is only supported if the behaviour is an SPComponent.</param>
        /// <remarks>
        /// Disable modes allow you to decide how the coroutine is dealt with when the component/gameobject are disabled. Note that 'CancelOnDeactivate' is a specical case flag, 
        /// it only takes effect if NO OTHER flag is set (it's a 0 flag actually). What this results in is that Deactivate and Disable are pausible... but a routine can only play 
        /// through Disable, not Deactivate. This is due to the default behaviour of coroutine in unity. In default mode, coroutines continue playing when a component gets disabled, 
        /// but when deactivated the coroutine gets cancelled. This means we can not play through a deactivation.
        /// </remarks>
        public void Start(MonoBehaviour behaviour, RadicalCoroutineDisableMode disableMode = RadicalCoroutineDisableMode.Default)
        {
            if (_state != RadicalCoroutineOperatingState.Inactive) throw new System.InvalidOperationException("Failed to start RadicalCoroutine. The Coroutine is already being processed.");
            if (behaviour == null) throw new System.ArgumentNullException("behaviour");

            _state = RadicalCoroutineOperatingState.Active;
            _owner = behaviour;
            _token = behaviour.StartCoroutine(this);

            _disableMode = disableMode;
            if (_disableMode > RadicalCoroutineDisableMode.Default && _disableMode != RadicalCoroutineDisableMode.ResumeOnEnable)
            {
                //no point in managing the routine if it acts in default mode... a flag of 'ResumeOnEnable' is a redundant mode to default
                var manager = behaviour.AddOrGetComponent<RadicalCoroutineManager>();
                manager.RegisterCoroutine(behaviour, this);
            }

            if (_stack.Count > 0 && _stack.Peek() is IPausibleYieldInstruction) (_stack.Peek() as IPausibleYieldInstruction).OnResume();
        }
        /// <summary>
        /// Stops the coroutine, but preserves the state of it, so that it could be resumed again later by calling start.
        /// </summary>
        /// <param name="behaviour">A reference to the MonoBehaviour that is handling the coroutine.</param>
        public void Stop()
        {
            if (_state == RadicalCoroutineOperatingState.Inactive) return;

            if (_state == RadicalCoroutineOperatingState.Cancelling || _state == RadicalCoroutineOperatingState.Completing)
            {
                this.OnFinish(_state == RadicalCoroutineOperatingState.Cancelling);
            }
            else if (_state == RadicalCoroutineOperatingState.Active)
            {
                _state = RadicalCoroutineOperatingState.Inactive;
                try
                {
                    _owner.StopCoroutine(this); //NOTE - due to a bug in unity, a runtime warning appears if you pass in the Coroutine token while this routine is 'WaitForSeconds'
                }
                catch (System.Exception ex) { Debug.LogException(ex); }
                _owner = null;
                _token = null;

                if (_stack.Count > 0 && _stack.Peek() is IPausibleYieldInstruction) (_stack.Peek() as IPausibleYieldInstruction).OnPause();
            }
        }
 /// <summary>
 /// Stops the coroutine flagging it as finished.
 /// </summary>
 public void Cancel()
 {
     _state = RadicalCoroutineOperatingState.Cancelling;
 }
        bool IEnumerator.MoveNext()
        {
            if (this.Cancelled)
            {
                if (_state == RadicalCoroutineOperatingState.Cancelling)
                {
                    this.OnFinish(true);
                }
                return false;
            }
            else if (this.Complete)
            {
                if (_state == RadicalCoroutineOperatingState.Completing)
                {
                    this.OnFinish(false);
                }
                return false;
            }

            if (_forcedTick)
            {
                _forcedTick = false;
                return true;
            }

            _currentIEnumeratorYieldValue = null;

            //actually operate
            while (_stack.Count > 0 && !_stack.Peek().ContinueBlocking())
            {
                if (_stack.Peek() is IPooledYieldInstruction)
                    (_stack.Pop() as IPooledYieldInstruction).Dispose();
                else
                    _stack.Pop();
            }

            if (_stack.Count > 0)
            {
                if (this.Cancelled)
                {
                    //routine cancelled itself
                    if (_state == RadicalCoroutineOperatingState.Cancelling)
                    {
                        this.OnFinish(true);
                    }
                    return false;
                }

                var current = _stack.Peek().CurrentYieldObject;
                if (current == null)
                {
                    //do nothing
                }
                else if (current is YieldInstruction)
                {
                    if (current is WaitForSeconds && _disableMode.HasFlag(RadicalCoroutineDisableMode.ResumeOnEnable))
                    {
                        _currentIEnumeratorYieldValue = null;
                        _stack.Push(WaitForDuration.FromWaitForSeconds(current as WaitForSeconds));
                    }
                    else
                    {
                        _currentIEnumeratorYieldValue = current;
                    }
                }
                else if (current is WWW)
                {
                    _currentIEnumeratorYieldValue = current;
                }
                else if (current is RadicalCoroutine)
                {
                    // //v3
                    var rad = current as RadicalCoroutine;
                    if (!rad.Finished)
                    {
                        if (rad._token != null)
                        {
                            _currentIEnumeratorYieldValue = rad._token;
                        }
                        else
                        {
                            _stack.Push(EnumWrapper.Create(WaitUntilDone_Routine(rad)));
                        }
                    }
                    else
                    {
                        _currentIEnumeratorYieldValue = null;
                    }
                }
                else if (current is IRadicalYieldInstruction)
                {
                    var instruction = current as IRadicalYieldInstruction;
                    if (instruction is IImmediatelyResumingYieldInstruction) (instruction as IImmediatelyResumingYieldInstruction).Signal += this.OnImmediatelyResumingYieldInstructionSignaled;

                    if (instruction.ContinueBlocking())
                    {
                        _currentIEnumeratorYieldValue = instruction.CurrentYieldObject;
                        _stack.Push(instruction);
                    }
                }
                else if (current is IEnumerable)
                {
                    //yes we have to test for IEnumerable before IEnumerator. When a yield method is returned as an IEnumerator, it still needs 'GetEnumerator' called on it.
                    var e = (current as IEnumerable).GetEnumerator();
                    if (e.MoveNext())
                    {
                        _currentIEnumeratorYieldValue = e.Current;
                        _stack.Push(EnumWrapper.Create(e));
                    }
                }
                else if (current is IEnumerator)
                {
                    var e = current as IEnumerator;
                    if (e.MoveNext())
                    {
                        _currentIEnumeratorYieldValue = e.Current;
                        _stack.Push(EnumWrapper.Create(e));
                    }
                }
                else if (current is RadicalCoroutineEndCommand)
                {
                    var cmd = (RadicalCoroutineEndCommand)current;

                    if (cmd.HasFlag(RadicalCoroutineEndCommand.Cancel))
                    {
                        this.Cancel();
                        if (cmd.HasFlag(RadicalCoroutineEndCommand.StallImmediateResume))
                        {
                            _currentIEnumeratorYieldValue = null;
                        }
                        else
                        {
                            this.OnFinish(true);
                            return false;
                        }
                    }
                    else
                    {
                        if (cmd.HasFlag(RadicalCoroutineEndCommand.StallImmediateResume))
                        {
                            _state = RadicalCoroutineOperatingState.Completing;
                            _currentIEnumeratorYieldValue = null;
                        }
                        else
                        {
                            this.OnFinish(false);
                            return false;
                        }
                    }
                }
                else
                {
                    _currentIEnumeratorYieldValue = current;
                }

                return true;
            }
            else
            {
                this.OnFinish(false);
                return false;
            }
        }
        /// <summary>
        /// Manually step the coroutine. This is usually done from within a Update event.
        /// </summary>
        /// <param name="handle">A MonoBehaviour to use as a handle if a YieldInstruction is to be operated on.</param>
        /// <returns></returns>
        public bool ManualTick(MonoBehaviour handle)
        {
            if (_owner != null || _state != RadicalCoroutineOperatingState.Inactive) throw new System.InvalidOperationException("Can not manually operate a RadicalCoroutine that is already being operated.");
            if (handle == null) throw new System.ArgumentNullException("handle");

            _state = RadicalCoroutineOperatingState.Active;
            bool result = false;
            try
            {
                result = (this as IEnumerator).MoveNext();
            }
            catch(System.Exception ex)
            {
                Debug.LogException(ex, handle);
            }
            if (_state == RadicalCoroutineOperatingState.Active) _state = RadicalCoroutineOperatingState.Inactive;

            if (result)
            {
                var current = _currentIEnumeratorYieldValue;
                _currentIEnumeratorYieldValue = null;

                if (current is YieldInstruction || current is WWW)
                {
                    var wait = ManualWaitForGeneric.Create(this, handle, current);
                    _stack.Push(wait);
                    wait.Start();
                }
            }

            return result;
        }
        public void Dispose()
        {
            _state = RadicalCoroutineOperatingState.Inactive;
            _disableMode = RadicalCoroutineDisableMode.Default;
            if (_stack != null) _stack.Clear();
            _currentIEnumeratorYieldValue = null;
            _forcedTick = false;
            this.OnComplete = null;
            this.OnCancelled = null;
            this.OnFinished = null;

            //TODO - #100 - allow releasing when we've fully implemented coroutine object caching 
            //_pool.Release(this);
        }
 /// <summary>
 /// Should only be ever called from RadicalCoroutineManager.
 /// </summary>
 /// <param name="cancelledByManager"></param>
 internal void Cancel(bool cancelledByManager)
 {
     _state = RadicalCoroutineOperatingState.Cancelling;
     if (cancelledByManager)
     {
         _manager = null;
     }
 }
        /// <summary>
        /// Should only be ever called from RadicalCoroutineManager.
        /// </summary>
        /// <param name="stalledByManager"></param>
        internal void Stop(bool stalledByManager)
        {
            if (_state == RadicalCoroutineOperatingState.Inactive) return;

            if (_state == RadicalCoroutineOperatingState.Cancelling || _state == RadicalCoroutineOperatingState.Completing)
            {
                this.OnFinish(_state == RadicalCoroutineOperatingState.Cancelling);
            }
            else if (_state == RadicalCoroutineOperatingState.Active)
            {
                if (_owner != null)
                {
                    try
                    {
                        if (_owner != null) _owner.StopCoroutine(this);//NOTE - due to a bug in unity, a runtime warning appears if you pass in the Coroutine token while this routine is 'WaitForSeconds'
                    }
                    catch (System.Exception ex) { Debug.LogException(ex, _owner); }
                }

                if (!stalledByManager && _manager != null)
                {
                    _manager.UnregisterCoroutine(this);

                    _state = RadicalCoroutineOperatingState.Inactive;
                    _owner = null;
                    _token = null;
                }
                else
                {
                    _state = RadicalCoroutineOperatingState.Paused;
                    _token = null;

                }

                if (_stack.CurrentOperation is IPausibleYieldInstruction) (_stack.CurrentOperation as IPausibleYieldInstruction).OnPause();
            }
        }
        internal void Resume(MonoBehaviour behaviour)
        {
            if (_state != RadicalCoroutineOperatingState.Inactive && _state != RadicalCoroutineOperatingState.Paused) throw new System.InvalidOperationException("Failed to start RadicalCoroutine. The Coroutine is already being processed.");
            if (behaviour == null) throw new System.ArgumentNullException("behaviour");

            _state = RadicalCoroutineOperatingState.Active;
            _owner = behaviour;

            if (_stack.CurrentOperation is IPausibleYieldInstruction) (_stack.CurrentOperation as IPausibleYieldInstruction).OnResume();

#if SP_LIB
            var manager = behaviour.AddOrGetComponent<RadicalCoroutineManager>();
#else
            var manager = behaviour.GetComponent<RadicalCoroutineManager>();
            if (manager == null) manager = behaviour.gameObject.AddComponent<RadicalCoroutineManager>();
#endif
            _manager = manager;
            _manager.RegisterCoroutine(this);
            _token = behaviour.StartCoroutine(this);
        }
        public void StartAsync(MonoBehaviour behaviour, RadicalCoroutineDisableMode disableMode = RadicalCoroutineDisableMode.Default)
        {
            if (_state != RadicalCoroutineOperatingState.Inactive) throw new System.InvalidOperationException("Failed to start RadicalCoroutine. The Coroutine is already being processed.");
            if (behaviour == null) throw new System.ArgumentNullException("behaviour");

            _state = RadicalCoroutineOperatingState.Active;
            _owner = behaviour;
            _disableMode = disableMode;

            if (_stack.CurrentOperation is IPausibleYieldInstruction) (_stack.CurrentOperation as IPausibleYieldInstruction).OnResume();
            _stack.Push(com.spacepuppy.Async.RadicalTask.Create(this)); //we start the task as an async operation

#if SP_LIB
            var manager = behaviour.AddOrGetComponent<RadicalCoroutineManager>();
#else
            var manager = behaviour.GetComponent<RadicalCoroutineManager>();
            if (manager == null) manager = behaviour.gameObject.AddComponent<RadicalCoroutineManager>();
#endif
            _manager = manager;
            _manager.RegisterCoroutine(this);
            _token = behaviour.StartCoroutine(this);

        }
        bool IEnumerator.MoveNext()
        {
            if (_state == RadicalCoroutineOperatingState.Inactive) return false;

            if (this.Cancelled)
            {
                if (_state == RadicalCoroutineOperatingState.Cancelling)
                {
                    this.OnFinish(true);
                }
                return false;
            }
            else if (this.Complete)
            {
                if (_state == RadicalCoroutineOperatingState.Completing)
                {
                    this.OnFinish(false);
                }
                return false;
            }

            if (_forcedTick)
            {
                _forcedTick = false;
                return true;
            }

            _currentIEnumeratorYieldValue = null;

            //clear completed entries
            while(_stack.Count > 0)
            {
                try
                {
                    if(_stack.Peek().IsComplete)
                    {
                        this.PopStack();
                    }
                    else
                    {
                        break;
                    }
                }
                catch(System.Exception ex)
                {
                    Debug.LogException(ex);
                    this.PopStack();
                }
            }

            if (_stack.Count > 0)
            {
                //operate
                object current;
                var r = _stack.Peek();
                if (!r.Tick(out current))
                {
                    //the tick may have forced a tick, which could have popped this yieldinstruction already, this usually means it was an IImmediatelyResumingYieldInstruction
                    //deal with accordingly
                    if (_stack.Count > 0 && _stack.Peek() == r) this.PopStack();
                    if(_forcedTick)
                    {
                        _forcedTick = false;
                        if (this.Cancelled)
                        {
                            if (_state == RadicalCoroutineOperatingState.Cancelling)
                            {
                                this.OnFinish(true);
                            }
                            return false;
                        }
                        else if (this.Complete)
                        {
                            if (_state == RadicalCoroutineOperatingState.Completing)
                            {
                                this.OnFinish(false);
                            }
                            return false;
                        }
                        else
                        {
                            return true;
                        }
                    }
                    else
                    {
                        current = null;
                    }
                }
                else if (this.Cancelled)
                {
                    //routine cancelled itself
                    if (_state == RadicalCoroutineOperatingState.Cancelling)
                    {
                        this.OnFinish(true);
                    }
                    return false;
                }


                //deal with the current yieldObject
                if (current == null)
                {
                    //do nothing
                }
                else if (current is YieldInstruction)
                {
                    if (current is WaitForSeconds && _disableMode.HasFlag(RadicalCoroutineDisableMode.ResumeOnEnable))
                    {
                        _currentIEnumeratorYieldValue = null;
                        _stack.Push(WaitForDuration.FromWaitForSeconds(current as WaitForSeconds));
                    }
                    else
                    {
                        _currentIEnumeratorYieldValue = current;
                    }
                }
                else if (current is WWW)
                {
                    _currentIEnumeratorYieldValue = current;
                }
                else if (current is RadicalCoroutine)
                {
                    // //v3
                    var rad = current as RadicalCoroutine;
                    if (!rad.Finished)
                    {
                        if (rad._token != null)
                        {
                            _currentIEnumeratorYieldValue = rad._token;
                        }
                        else
                        {
                            _stack.Push(EnumWrapper.Create(WaitUntilDone_Routine(rad)));
                        }
                    }
                    else
                    {
                        _currentIEnumeratorYieldValue = null;
                    }
                }
                else if (current is IRadicalYieldInstruction)
                {
                    var instruction = current as IRadicalYieldInstruction;
                    if (instruction is IResettingYieldInstruction) (instruction as IResettingYieldInstruction).Reset();
                    if (instruction is IImmediatelyResumingYieldInstruction) (instruction as IImmediatelyResumingYieldInstruction).Signal += this.OnImmediatelyResumingYieldInstructionSignaled;

                    object yieldObject;
                    if (instruction.Tick(out yieldObject))
                    {
                        _currentIEnumeratorYieldValue = yieldObject;
                        _stack.Push(instruction);
                    }
                }
                else if (current is IEnumerable)
                {
                    //yes we have to test for IEnumerable before IEnumerator. When a yield method is returned as an IEnumerator, it still needs 'GetEnumerator' called on it.
                    var e = (current as IEnumerable).GetEnumerator();
                    if (e.MoveNext())
                    {
                        _currentIEnumeratorYieldValue = e.Current;
                        _stack.Push(EnumWrapper.Create(e));
                    }
                }
                else if (current is IEnumerator)
                {
                    var e = current as IEnumerator;
                    if (e.MoveNext())
                    {
                        _currentIEnumeratorYieldValue = e.Current;
                        _stack.Push(EnumWrapper.Create(e));
                    }
                }
                else if (current is RadicalCoroutineEndCommand)
                {
                    var cmd = (RadicalCoroutineEndCommand)current;

                    if (cmd.HasFlag(RadicalCoroutineEndCommand.Cancel))
                    {
                        this.Cancel();
                        if (cmd.HasFlag(RadicalCoroutineEndCommand.StallImmediateResume))
                        {
                            _currentIEnumeratorYieldValue = null;
                        }
                        else
                        {
                            this.OnFinish(true);
                            return false;
                        }
                    }
                    else
                    {
                        if (cmd.HasFlag(RadicalCoroutineEndCommand.StallImmediateResume))
                        {
                            _state = RadicalCoroutineOperatingState.Completing;
                            _currentIEnumeratorYieldValue = null;
                        }
                        else
                        {
                            this.OnFinish(false);
                            return false;
                        }
                    }
                }
                else
                {
                    _currentIEnumeratorYieldValue = current;
                }

                return true;
            }
            else
            {
                this.OnFinish(false);
                return false;
            }
        }