//! \~\file pistatemachine_transition.h
//! \~\ingroup StateMachine
//! \~\brief
//! \~english Declares transitions used by PIStateMachine
//! \~russian Объявляет переходы, используемые в PIStateMachine
/*
    PIP - Platform Independent Primitives
	State machine transition
	Ivan Pelipenko peri4ko@yandex.ru

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef pistatemachine_transition_H
#define pistatemachine_transition_H

#include "pistatemachine_base.h"
#include "pitimer.h"


//! \~\ingroup StateMachine
//! \~\brief
//! \~english Transition between states in PIStateMachine.
//! \~russian Переход между состояниями в PIStateMachine.
//! \~\details
//! \~english
//! A transition can be triggered by an event or by a timeout. Optional guards are
//! evaluated against arguments passed to \a PIStateMachine::postEvent().
//! \~russian
//! Переход может запускаться событием или таймаутом. Необязательные guard-функции
//! проверяются на аргументах, переданных в \a PIStateMachine::postEvent().
class PIP_EXPORT PITransitionBase {
	friend class PIStateMachine;
	friend class PIStateBase;


public:
	//! \~english Creates a transition from \a source to \a target for the specified event identifier.
	//! \~russian Создает переход из \a source в \a target для указанного идентификатора события.
	PITransitionBase(PIStateBase * source, PIStateBase * target, int event_id);

	//! \~english Destroys the transition and its stored guard.
	//! \~russian Уничтожает переход и сохраненную guard-функцию.
	virtual ~PITransitionBase();


	//! \~english Returns the root state machine.
	//! \~russian Возвращает корневую машину состояний.
	PIStateMachine * machine() const { return root; }

	//! \~english Returns the source state.
	//! \~russian Возвращает исходное состояние.
	PIStateBase * source() const { return source_state; }

	//! \~english Returns the target state.
	//! \~russian Возвращает целевое состояние.
	PIStateBase * target() const { return target_state; }


	//! \~english Sets a guard function that must return bool.
	//! \~russian Задает guard-функцию, которая должна возвращать bool.
	template<typename R, typename... Args>
	PITransitionBase * addGuard(std::function<R(Args...)> f) {
		static_assert(std::is_same<R, bool>::value, "guard function should return bool!");
		piDeleteSafety(guard);
		guard = PIStateMachineHelpers::makeFunction(f);
		return this;
	}

	//! \~english Sets a guard from a lambda or other callable object.
	//! \~russian Задает guard-функцию из lambda или другого вызываемого объекта.
	template<typename L>
	PITransitionBase * addGuard(L f) {
		return addGuard(toStdFunction(f));
	}

	//! \~english Tests the guard against event arguments passed to the transition.
	//! \~russian Проверяет guard-функцию на аргументах события, переданных переходу.
	template<typename... Args>
	bool testGuard(Args... args) {
		if (!guard) return true;
		if (guard->formatHash() != PIStateMachineHelpers::Function<Args...>().formatHash()) {
			piCout << "invalid arguments format!";
			return false;
		}
		return reinterpret_cast<PIStateMachineHelpers::Function<Args...> *>(guard)->func(args...);
	}


	//! \~english Sets an action executed when the transition fires.
	//! \~russian Задает действие, выполняемое при срабатывании перехода.
	PITransitionBase * addAction(std::function<void()> a);

	//! \~english Executes the transition action if it is set.
	//! \~russian Выполняет действие перехода, если оно задано.
	void makeAction();

	//! \~english Triggers the transition and updates active states.
	//! \~russian Запускает переход и обновляет активные состояния.
	void trigger();


protected:
	virtual void enabled() {}
	virtual void disabled() {}

	int eventID               = 0;
	PIStateBase *source_state = nullptr, *target_state = nullptr;
	PIStateMachine * root                       = nullptr;
	PIStateMachineHelpers::FunctionBase * guard = nullptr;
	std::function<void()> action;
};


//! \~\ingroup StateMachine
//! \~\brief
//! \~english Transition that fires automatically after a timeout.
//! \~russian Переход, который автоматически срабатывает по истечении таймаута.
class PIP_EXPORT PITransitionTimeout: public PITransitionBase {
public:
	//! \~english Creates a transition that fires after \a timeout while the source state is active.
	//! \~russian Создает переход, который срабатывает через \a timeout, пока исходное состояние активно.
	PITransitionTimeout(PIStateBase * source, PIStateBase * target, PISystemTime timeout);

	//! \~english Stops the internal timer and destroys the transition.
	//! \~russian Останавливает внутренний таймер и уничтожает переход.
	~PITransitionTimeout();


private:
	void enabled() override;
	void disabled() override;

	PITimer timer;
};

#endif
