//! \~\file pistatemachine_state.h
//! \~\ingroup StateMachine
//! \~\brief
//! \~english Declares states used by PIStateMachine
//! \~russian Объявляет состояния, используемые в PIStateMachine
/*
    PIP - Platform Independent Primitives
    State machine node
    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_state_H
#define pistatemachine_state_H

#include "pistatemachine_base.h"
#include "pistring.h"
#include "pisystemtime.h"


//! \~\ingroup StateMachine
//! \~\brief
//! \~english Base class for states in a hierarchical state machine.
//! \~russian Базовый класс состояния в иерархической машине состояний.
//! \~\details
//! \~english
//! A state can own child states and outgoing transitions. Compound states activate
//! either their initial child state or all children when parallel mode is enabled.
//! \~russian
//! Состояние может владеть дочерними состояниями и исходящими переходами.
//! Составные состояния активируют либо начальное дочернее состояние, либо все
//! дочерние состояния при включенном параллельном режиме.
class PIP_EXPORT PIStateBase {
	friend class PIStateMachine;
	friend class PITransitionBase;
	friend class PIStateFinal;


public:
	//! \~english Creates a state with an optional diagnostic name.
	//! \~russian Создает состояние с необязательным именем для диагностики.
	PIStateBase(const PIString & n = {}): name_(n) { ; }

	//! \~english Destroys the state together with owned child states and transitions.
	//! \~russian Уничтожает состояние вместе с принадлежащими дочерними состояниями и переходами.
	virtual ~PIStateBase();


	//! \~english Called when the state becomes active.
	//! \~russian Вызывается при активации состояния.
	virtual void onEnter() {}

	//! \~english Called when the state becomes inactive.
	//! \~russian Вызывается при деактивации состояния.
	virtual void onExit() {}

	//! \~english Called when a final child finishes this state.
	//! \~russian Вызывается, когда финальное дочернее состояние завершает это состояние.
	virtual void onFinish() {}


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

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

	//! \~english Returns the active child of a non-parallel compound state.
	//! \~russian Возвращает активное дочернее состояние непараллельного составного состояния.
	PIStateBase * activeChild() const { return active_state; }

	//! \~english Returns active child states.
	//! \~russian Возвращает активные дочерние состояния.
	PIVector<PIStateBase *> activeChildren() const;

	//! \~english Returns active atomic leaf states below this state.
	//! \~russian Возвращает активные атомарные листовые состояния под этим состоянием.
	PIVector<PIStateBase *> activeAtomics() const;


	//! \~english Adds a child state owned by this state.
	//! \~russian Добавляет дочернее состояние, которым владеет это состояние.
	void addState(PIStateBase * s);

	//! \~english Adds multiple child states owned by this state.
	//! \~russian Добавляет несколько дочерних состояний, которыми владеет это состояние.
	void addStates(PIVector<PIStateBase *> s);

	//! \~english Sets the initial child state for a non-parallel compound state.
	//! \~russian Задает начальное дочернее состояние для непараллельного составного состояния.
	void setInitialState(PIStateBase * s);


	//! \~english Adds an event-driven transition from this state to \a target.
	//! \~russian Добавляет переход по событию из этого состояния в \a target.
	PITransitionBase * addTransition(PIStateBase * target, int event_id);

	//! \~english Adds a timeout transition that fires while this state stays active.
	//! \~russian Добавляет переход по таймауту, который срабатывает пока это состояние активно.
	PITransitionTimeout * addTimeoutTransition(PIStateBase * target, PISystemTime timeout);


	//! \~english Enables or disables parallel activation of child states.
	//! \~russian Включает или выключает параллельную активацию дочерних состояний.
	void setParallel(bool yes) { is_parallel = yes; }


	//! \~english Returns the state name.
	//! \~russian Возвращает имя состояния.
	const PIString & getName() const { return name_; }

	//! \~english Returns true for the root machine object.
	//! \~russian Возвращает true для корневого объекта машины состояний.
	bool isStateMachine() const { return is_root; }

	//! \~english Returns true if the state is currently active.
	//! \~russian Возвращает true, если состояние сейчас активно.
	bool isActive() const { return is_active; }

	//! \~english Returns true if child states are activated in parallel.
	//! \~russian Возвращает true, если дочерние состояния активируются параллельно.
	bool isParallel() const { return is_parallel; }

	//! \~english Returns true if the state is marked as final.
	//! \~russian Возвращает true, если состояние помечено как финальное.
	bool isFinal() const { return is_final; }

	//! \~english Returns true if the state has no children.
	//! \~russian Возвращает true, если у состояния нет дочерних состояний.
	bool isAtomic() const { return children.isEmpty(); }

	//! \~english Returns true if the state owns child states.
	//! \~russian Возвращает true, если состояние содержит дочерние состояния.
	bool isCompound() const { return children.isNotEmpty(); }

	//! \~english Returns direct child states.
	//! \~russian Возвращает прямые дочерние состояния.
	const PIVector<PIStateBase *> & getChildren() const { return children; }

	//! \~english Returns outgoing transitions owned by this state.
	//! \~russian Возвращает исходящие переходы, принадлежащие этому состоянию.
	const PIVector<PITransitionBase *> & getTransitions() const { return transitions; }


	//! \~english Prints the state tree and active branches to piCout.
	//! \~russian Печатает дерево состояний и активные ветви в piCout.
	void print(PIString prefix = {});

	//! \~english Returns this state and all descendant states.
	//! \~russian Возвращает это состояние и все дочерние состояния.
	PIVector<PIStateBase *> gatherStates();


private:
	bool start(bool force = false);
	void setActive(bool yes);
	void setActiveRecursive(bool yes);
	void setChildrenActive(bool yes);
	void setChildrenActiveRecursive(bool yes);
	void activeChild(PIStateBase * c);
	void childActived(PIStateBase * s);
	void propagateRoot(PIStateMachine * r);
	void gatherActiveStates(PIVector<PIStateBase *> & output);
	void gatherActiveAtomicStates(PIVector<PIStateBase *> & output) const;
	void gatherPathToMachine(PIVector<PIStateBase *> & output);
	PIVector<PIStateBase *> pathToMachine();

	bool is_active = false, is_root = false, is_parallel = false, is_final = false;
	PIVector<PIStateBase *> children;
	PIVector<PITransitionBase *> transitions;
	PIStateBase *active_state = nullptr, *initial_state = nullptr, *parent_state = nullptr;
	PIStateMachine * root = nullptr;
	PIString name_;
};


//! \~\ingroup StateMachine
//! \~\brief
//! \~english State implementation that forwards enter and exit virtual methods to callbacks.
//! \~russian Реализация состояния, которая перенаправляет виртуальные методы входа и выхода в callback-функции.
class PIP_EXPORT PIStateLambda: public PIStateBase {
public:
	//! \~english Creates a state backed by enter and exit callbacks.
	//! \~russian Создает состояние с callback-функциями входа и выхода.
	PIStateLambda(std::function<void()> on_enter, std::function<void()> on_exit = nullptr, const PIString & n = {})
		: PIStateBase(n)
		, enter(std::move(on_enter))
		, exit(std::move(on_exit)) {}


	//! \~english Executes the enter callback.
	//! \~russian Выполняет callback-функцию входа.
	void onEnter() override {
		if (enter) enter();
	}

	//! \~english Executes the exit callback.
	//! \~russian Выполняет callback-функцию выхода.
	void onExit() override {
		if (exit) exit();
	}


private:
	std::function<void()> enter, exit;
};


//! \~\ingroup StateMachine
//! \~\brief
//! \~english Final state that finishes its parent state when entered.
//! \~russian Финальное состояние, которое завершает родительское состояние при входе.
class PIP_EXPORT PIStateFinal: public PIStateBase {
public:
	//! \~english Creates a final state with an optional callback executed on entry.
	//! \~russian Создает финальное состояние с необязательной callback-функцией, выполняемой при входе.
	PIStateFinal(std::function<void()> on_finish = nullptr, const PIString & n = {}): PIStateBase(n), enter(std::move(on_finish)) {
		is_final = true;
	}


	//! \~english Executes the finish callback when the final state is entered.
	//! \~russian Выполняет callback завершения при входе в финальное состояние.
	void onEnter() override {
		if (enter) enter();
	}


private:
	std::function<void()> enter;
};


//! \relatesalso PIStateBase
//! \~english Writes a textual representation of a state to PICout.
//! \~russian Записывает текстовое представление состояния в PICout.
inline PICout operator<<(PICout c, PIStateBase * s) {
	if (!s)
		c << "state(nullptr)";
	else
		c << ("state(\"" + s->getName() + "\")");
	return c;
}


#endif
