/**
* Copyright 2015 IBM Corp. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Uncomment to enable debugging of the Runnable class.
//#define ENABLE_RUNNABLE_DEBUGGING
namespace Mapbox.Unity.Utilities
{
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
///
/// Helper class for running co-routines without having to inherit from MonoBehavior.
///
public class Runnable : MonoBehaviour
{
#region Public Properties
///
/// Returns the Runnable instance.
///
public static Runnable Instance { get { return Singleton.Instance; } }
#endregion
#region Public Interface
///
/// Start a co-routine function.
///
/// The IEnumerator returns by the co-routine function the user is invoking.
/// Returns a ID that can be passed into Stop() to halt the co-routine.
public static int Run(IEnumerator routine)
{
Routine r = new Routine(routine);
return r.ID;
}
///
/// Stops a active co-routine.
///
/// THe ID of the co-routine to stop.
public static void Stop(int ID)
{
Routine r = null;
if (Instance.m_Routines.TryGetValue(ID, out r))
r.Stop = true;
}
///
/// Check if a routine is still running.
///
/// The ID returned by Run().
/// Returns true if the routine is still active.
static public bool IsRunning(int id)
{
return Instance.m_Routines.ContainsKey(id);
}
#if UNITY_EDITOR
private static bool sm_EditorRunnable = false;
///
/// This function enables the Runnable in edit mode.
///
public static void EnableRunnableInEditor()
{
if (!sm_EditorRunnable)
{
sm_EditorRunnable = true;
UnityEditor.EditorApplication.update += UpdateRunnable;
}
}
static void UpdateRunnable()
{
if (!Application.isPlaying)
{
Instance.UpdateRoutines();
}
}
#endif
#endregion
#region Private Types
///
/// This class handles a running co-routine.
///
private class Routine : IEnumerator
{
#region Public Properties
public int ID { get; private set; }
public bool Stop { get; set; }
#endregion
#region Private Data
private bool m_bMoveNext = false;
private IEnumerator m_Enumerator = null;
#endregion
public Routine(IEnumerator a_enumerator)
{
m_Enumerator = a_enumerator;
Runnable.Instance.StartCoroutine(this);
Stop = false;
ID = Runnable.Instance.m_NextRoutineId++;
Runnable.Instance.m_Routines[ID] = this;
#if ENABLE_RUNNABLE_DEBUGGING
Debug.Log( string.Format("Coroutine {0} started.", ID ) );
#endif
}
#region IEnumerator Interface
public object Current { get { return m_Enumerator.Current; } }
public bool MoveNext()
{
m_bMoveNext = m_Enumerator.MoveNext();
if (m_bMoveNext && Stop)
m_bMoveNext = false;
if (!m_bMoveNext)
{
Runnable.Instance.m_Routines.Remove(ID); // remove from the mapping
#if ENABLE_RUNNABLE_DEBUGGING
Debug.Log( string.Format("Coroutine {0} stopped.", ID ) );
#endif
}
return m_bMoveNext;
}
public void Reset() { m_Enumerator.Reset(); }
#endregion
}
#endregion
#region Private Data
private Dictionary m_Routines = new Dictionary();
private int m_NextRoutineId = 1;
#endregion
///
/// THis can be called by the user to force all co-routines to get a time slice, this is usually
/// invoked from an EditorApplication.Update callback so we can use runnable in Editor mode.
///
public void UpdateRoutines()
{
if (m_Routines.Count > 0)
{
// we are not in play mode, so we must manually update our co-routines ourselves
List routines = new List();
foreach (var kp in m_Routines)
routines.Add(kp.Value);
foreach (var r in routines)
r.MoveNext();
}
}
}
}