using System;
using System.Collections.Generic;
using UnityEngine;
namespace Platformer.Core
{
///
/// The Simulation class implements the discrete event simulator pattern.
/// Events are pooled, with a default capacity of 4 instances.
///
public static partial class Simulation
{
static HeapQueue eventQueue = new HeapQueue();
static Dictionary> eventPools = new Dictionary>();
///
/// Create a new event of type T and return it, but do not schedule it.
///
///
///
static public T New() where T : Event, new()
{
Stack pool;
if (!eventPools.TryGetValue(typeof(T), out pool))
{
pool = new Stack(4);
pool.Push(new T());
eventPools[typeof(T)] = pool;
}
if (pool.Count > 0)
return (T)pool.Pop();
else
return new T();
}
///
/// Clear all pending events and reset the tick to 0.
///
public static void Clear()
{
eventQueue.Clear();
}
///
/// Schedule an event for a future tick, and return it.
///
/// The event.
/// Tick.
/// The event type parameter.
static public T Schedule(float tick = 0) where T : Event, new()
{
var ev = New();
ev.tick = Time.time + tick;
eventQueue.Push(ev);
return ev;
}
///
/// Reschedule an existing event for a future tick, and return it.
///
/// The event.
/// Tick.
/// The event type parameter.
static public T Reschedule(T ev, float tick) where T : Event, new()
{
ev.tick = Time.time + tick;
eventQueue.Push(ev);
return ev;
}
///
/// Return the simulation model instance for a class.
///
///
static public T GetModel() where T : class, new()
{
return InstanceRegister.instance;
}
///
/// Set a simulation model instance for a class.
///
///
static public void SetModel(T instance) where T : class, new()
{
InstanceRegister.instance = instance;
}
///
/// Destroy the simulation model instance for a class.
///
///
static public void DestroyModel() where T : class, new()
{
InstanceRegister.instance = null;
}
///
/// Tick the simulation. Returns the count of remaining events.
/// If remaining events is zero, the simulation is finished unless events are
/// injected from an external system via a Schedule() call.
///
///
static public int Tick()
{
var time = Time.time;
var executedEventCount = 0;
while (eventQueue.Count > 0 && eventQueue.Peek().tick <= time)
{
var ev = eventQueue.Pop();
var tick = ev.tick;
ev.ExecuteEvent();
if (ev.tick > tick)
{
//event was rescheduled, so do not return it to the pool.
}
else
{
// Debug.Log($"{ev.tick} {ev.GetType().Name}");
ev.Cleanup();
try
{
eventPools[ev.GetType()].Push(ev);
}
catch (KeyNotFoundException)
{
//This really should never happen inside a production build.
Debug.LogError($"No Pool for: {ev.GetType()}");
}
}
executedEventCount++;
}
return eventQueue.Count;
}
}
}