/*
 * XFState.cpp
 *
 *  Created on: 23 nov. 2023
 *      Author: remi.heredero
 */

#include "xf/XFState.h"
#include "xf/include/xf/behavior.h"
#include <cassert>
#include "xf/event.h"
#include "trace/trace.h"

XFState::XFState(XFBehavior* behavior)
	: pBehavior(behavior) {
	/*
	 * Initialise behavior of this state on the constructor.
	 * Can't be null !
	 */
	assert(behavior != nullptr);
}

void XFState::addTransition(XFEvent::XFEventType type, const int evid, const int time, XFState* state) {
	assert(state != nullptr);
	/*
	 * Create a transition with his Type, ID, time if Timeout and the state to be set when this transition happen
	 * Push the transition on the list of transition of the event
	 * Pointer State can't be null
	 */
	transition t;
	t.evType = type;
	t.evid = evid;
	t.time = time;
	t.nextState = state;
	transitions_.push_back(t);
}

XFEventStatus XFState::changeState(XFState* state) {
	// Change current state of the behavior
	pBehavior->currentXFState_ = state;

	// Set state is changed
	pBehavior->changeXFState = true;

	// Return the event status as consumed
	return XFEventStatus::Consumed;
}

XFEventStatus XFState::onState(const XFEvent* ev) {

	// Execute onState callBack without parameter if it's defined
	if(cbState_ != nullptr) {
		(pBehavior->*cbState_)();
	}

	// Execute onState callBack with event as parameter if it's defined
	if(cbEvState_ != nullptr) {
		(pBehavior->*cbEvState_)(ev);
	}

	// Test each transition and change state if one is actual transition
	for(transition t : transitions_) {

		switch(ev->getEventType()) {

			case XFEvent::XFEventType::Initial:
				if(t.evType == XFEvent::XFEventType::Initial) {
					return changeState(t.nextState);
				}
				break;

			case XFEvent::XFEventType::Timeout:
				if(t.evType == XFEvent::XFEventType::Timeout) {
					return changeState(t.nextState);
				}
				break;

			case XFEvent::XFEventType::Event:
				if(t.evid == ev->getId()) {
					return changeState(t.nextState);
				}
				break;

		}

	}

	// Return event status as notConsumed if actual event isn't on the list of transitions
	return XFEventStatus::NotConsumed;
}

void XFState::onEntry(const XFEvent* ev) {

	// Execute onEntry callBack without parameter if it's defined
	if(cbEntry_ != nullptr) {
		(pBehavior->*cbEntry_)();
	}

	// Execute onExit callBack with event as parameter if it's defined
	if(cbEvEntry_ != nullptr) {
		(pBehavior->*cbEvEntry_)(ev);
	}

	// If a timeout is on the list of transition start it with is own delay
	for(transition t : transitions_) {

		if(t.evType == XFEvent::XFEventType::Timeout) {
			pBehavior->scheduleTimeout(XFEvent::Timeout, t.time);
		}

	}
}
void XFState::onExit(const XFEvent* ev) {

	// Execute onExit callBack without parameter if it's defined
	if(cbExit_ != nullptr) {
		(pBehavior->*cbExit_)();
	}

	// Execute onExit callBack with event as parameter if it's defined
	if(cbEvExit_ != nullptr) {
		(pBehavior->*cbEvExit_)(ev);
	}

	// If a timeout is on the list of transition and isn't the actual event, stop it
	for(transition t: transitions_) {

		if(t.evType == XFEvent::XFEventType::Timeout) {
			if(ev->getEventType() != XFEvent::XFEventType::Timeout) {
				pBehavior->unscheduleTimeout(XFEvent::Timeout);
			}
		}
	}
}
