#include "Macho.hpp"
using namespace Macho;
#ifdef MACHO_TRACE
# include <iostream>
void MACHO_TRC1(const char * msg) { std::cout << msg << std::endl; }
void MACHO_TRC2(const char * state, const char * msg)
{ std::cout << "State " << state << ": " << msg << std::endl; }
void MACHO_TRC3(const char * state, const char * msg1, const char * msg2)
{ std::cout << "State " << state << ": " << msg1 << " " << msg2 << std::endl; }
#else
# define MACHO_TRC1(MSG)
# define MACHO_TRC2(STATE, MSG)
# define MACHO_TRC3(STATE, MSG1, MSG2)
#endif
_EmptyBox _EmptyBox::theEmptyBox;
template<>
void * Macho::_createBox<_EmptyBox>(void * & place) {
return &_EmptyBox::theEmptyBox;
}
template<>
void Macho::_deleteBox<_EmptyBox>(void * & box, void * & place) {
}
#ifdef MACHO_SNAPSHOTS
template<>
void * Macho::_cloneBox<_EmptyBox>(void * other) {
return &_EmptyBox::theEmptyBox;
}
#endif
void Alias::setState(_MachineBase & machine) const {
machine.setPendingState(key()->instanceGenerator(machine), myInitializer->clone());
}
_StateInstance & _StateSpecification::_getInstance(_MachineBase & machine) {
_StateInstance * & instance = machine.getInstance(0);
if (!instance)
instance = new _RootInstance(machine, 0);
return *instance;
}
void _StateSpecification::_shutdown() {
_myStateInstance.machine().shutdown();
}
void _StateSpecification::_restore(_StateInstance & current) {
_myStateInstance.machine().myCurrentState = ¤t;
}
void _StateSpecification::setState(const Alias & state) {
state.setState(_myStateInstance.machine());
}
#ifdef MACHO_SNAPSHOTS
void _StateSpecification::setState(_StateInstance & current) {
_myStateInstance.machine().setPendingState(current, &_theDefaultInitializer);
}
#endif
_StateInstance::_StateInstance(_MachineBase & machine, _StateInstance * parent)
: myMachine(machine)
, mySpecification(0)
, myHistory(0)
, myParent(parent)
, myBox(0)
, myBoxPlace(0)
{}
_StateInstance::~_StateInstance() {
if (myBoxPlace)
::operator delete(myBoxPlace);
delete mySpecification;
}
void _StateInstance::entry(_StateInstance & previous, bool first) {
if (!myParent)
return;
if (first || !previous.isChild(*this)) {
myParent->entry(previous, false);
createBox();
MACHO_TRC2(name(), "Entry");
mySpecification->entry();
}
}
void _StateInstance::exit(_StateInstance & next) {
if (!myParent)
return;
if (this == &next || !next.isChild(*this)) {
MACHO_TRC2(name(), "Exit");
mySpecification->exit();
if (myBox != &_EmptyBox::theEmptyBox)
mySpecification->_deleteBox(*this);
myParent->exit(next);
}
}
void _StateInstance::init(bool history) {
if (history && myHistory) {
MACHO_TRC3(name(), "History transition to", myHistory->name());
myMachine.setPendingState(*myHistory, &_theDefaultInitializer);
} else {
MACHO_TRC2(name(), "Init");
mySpecification->init();
}
myHistory = 0;
}
#ifdef MACHO_SNAPSHOTS
void _StateInstance::copy(_StateInstance & original) {
if (original.myHistory) {
_StateInstance * history = myMachine.getInstance(original.myHistory->id());
assert(history);
setHistory(history);
}
if (original.myBox)
cloneBox(original.myBox);
}
_StateInstance * _StateInstance::clone(_MachineBase & newMachine) {
assert(!newMachine.getInstance(id()));
_StateInstance * parent = 0;
if (myParent)
parent = newMachine.createClone(myParent->id(), myParent);
_StateInstance * clone = create(newMachine, parent);
return clone;
}
#endif
_MachineBase::_MachineBase()
: myCurrentState(0)
, myPendingState(0)
, myPendingInit(0)
, myPendingBox(0) , myPendingEvent(0)
{}
_MachineBase::~_MachineBase() {
assert(!myPendingInit);
delete[] myInstances;
delete myPendingEvent;
}
Alias _MachineBase::currentState() const {
return Alias(myCurrentState->key());
}
void _MachineBase::setState(_StateInstance & instance, _Initializer * init) {
setPendingState(instance, init);
rattleOn();
}
void _MachineBase::setState(const Alias & state) {
state.setState(*this);
rattleOn();
}
void _MachineBase::start(_StateInstance & instance) {
MACHO_TRC1("Starting Machine");
myCurrentState = &_StateSpecification::_getInstance(*this);
setState(instance, &_theDefaultInitializer);
}
void _MachineBase::start(const Alias & state) {
MACHO_TRC1("Starting Machine");
myCurrentState = &_StateSpecification::_getInstance(*this);
setState(state);
}
void _MachineBase::shutdown() {
assert(!myPendingState);
MACHO_TRC1("Shutting down Machine");
setState(_StateSpecification::_getInstance(*this), &_theDefaultInitializer);
myCurrentState = 0;
}
void _MachineBase::allocate(unsigned int count) {
myInstances = new _StateInstance *[count];
for (unsigned int i = 0; i < count; ++i)
myInstances[i] = 0;
}
void _MachineBase::free(unsigned int count) {
unsigned int i = count;
while (i > 0) {
--i;
delete myInstances[i];
myInstances[i] = 0;
}
}
void _MachineBase::clearHistoryDeep(unsigned int count, const _StateInstance & instance) {
for (unsigned int i = 0; i < count; ++i) {
_StateInstance * s = myInstances[i];
if (s && s->isChild(instance))
s->setHistory(0);
}
}
#ifdef MACHO_SNAPSHOTS
void _MachineBase::copy(_StateInstance ** others, unsigned int count) {
for (ID i = 0; i < count; ++i)
createClone(i, others[i]);
for (ID i = 0; i < count; ++i) {
_StateInstance * state = myInstances[i];
if (state) {
assert(others[i]);
state->copy(*others[i]);
}
}
}
_StateInstance * _MachineBase::createClone(ID id, _StateInstance * original) {
_StateInstance * & clone = getInstance(id);
if (!clone && original)
clone = original->clone(*this);
return clone;
}
#endif
void _MachineBase::rattleOn() {
assert(myCurrentState);
while (myPendingState || myPendingEvent) {
while (myPendingState) {
MACHO_TRC3(myCurrentState->name(), "Transition to", myPendingState->name());
#ifndef NDEBUG
if (!myPendingEvent)
myPendingEvent = (_IEventBase *) &myPendingEvent;
#endif
myCurrentState->exit(*myPendingState);
myCurrentState->setHistorySuper(*myCurrentState);
_StateInstance * previous = myCurrentState;
myCurrentState = myPendingState;
if (myPendingBox) {
myCurrentState->setBox(myPendingBox);
myPendingBox = 0;
}
myCurrentState->entry(*previous);
myPendingState = 0;
_Initializer * init = myPendingInit;
myPendingInit = 0;
init->execute(*myCurrentState);
init->destroy();
assert("Init may only transition to proper substates" &&
(!myPendingState ||
(myPendingState->isChild(*myCurrentState) && (myCurrentState != myPendingState)))
);
#ifndef NDEBUG
if (myPendingEvent == (_IEventBase *) &myPendingEvent)
myPendingEvent = 0;
#endif
}
if (myPendingEvent) {
_IEventBase * event = myPendingEvent;
myPendingEvent = 0;
event->dispatch(*myCurrentState);
delete event;
}
}
}
Key _AdaptingInitializer::adapt(Key key) {
ID id = static_cast<_KeyData *>(key)->id;
const _StateInstance * instance = myMachine.getInstance(id);
_StateInstance * history = 0;
if (instance)
history = instance->history();
return history ? history->key() : key;
}
_DefaultInitializer _theDefaultInitializer;
_HistoryInitializer _theHistoryInitializer;