#include <Exam_HelperStructs.h>

#include "Blackboard.h"

namespace BT
{
	enum class State
	{
		Failure,
		Success,
		Running
	};

	class IBehavior
	{
	public:
		IBehavior() = default;
		virtual ~IBehavior() = default;
		virtual State Execute(Blackboard* blackboardPtr) = 0;

	protected:
		State m_CurrentState = State::Failure;
	};

#pragma region COMPOSITES
	class Composite : public IBehavior
	{
	public:
		explicit Composite(const std::vector<IBehavior*>& childBehaviors);
		~Composite() override;

		State Execute(Blackboard* blackboardPtr) override = 0;

	protected:
		std::vector<IBehavior*> m_ChildBehaviors = {};
	};

	class Selector final : public Composite
	{
	public:
		explicit Selector(std::vector<IBehavior*> childBehaviors) :
			Composite(std::move(childBehaviors)) {
		}
		~Selector() override = default;

		State Execute(Blackboard* blackboardPtr) override;
	};

	class Sequence : public Composite
	{
	public:
		inline explicit Sequence(std::vector<IBehavior*> childBehaviors) :
			Composite(std::move(childBehaviors)) {
		}
		~Sequence() override = default;

		State Execute(Blackboard* blackboardPtr) override;
	};

	class PartialSequence final : public Sequence
	{
	public:
		inline explicit PartialSequence(std::vector<IBehavior*> childBehaviors)
			: Sequence(std::move(childBehaviors)) {
		}
		~PartialSequence() override = default;

		State Execute(Blackboard* blackboardPtr) override;

	private:
		unsigned int m_CurrentBehaviorIndex = 0;
	};
#pragma endregion

	class Conditional final : public IBehavior
	{
	public:
		explicit Conditional(std::function<bool(Blackboard*)> fp)
			: m_ConditionalPtr(std::move(fp)) {
		}

		State Execute(Blackboard* blackboardPtr) override;

	private:
		std::function<bool(Blackboard*)> m_ConditionalPtr = nullptr;
	};

	class Action final : public IBehavior
	{
	public:
		explicit Action(std::function<State(Blackboard*)> fp)
			: m_ActionPtr(std::move(fp)) {
		}
		State Execute(Blackboard* blackboardPtr) override;

	private:
		std::function<State(Blackboard*)> m_ActionPtr = nullptr;
	};

	class BehaviorTree final
	{
	public:
		inline explicit BehaviorTree(Blackboard* blackboardPtr, IBehavior* pRootBehavior)
			: m_BlackboardPtr(blackboardPtr),
			m_RootBehaviorPtr(pRootBehavior) {
		}
		~BehaviorTree();

		void Update();

		Blackboard* GetBlackboard() const;

	private:
		State m_CurrentState = State::Failure;
		Blackboard* m_BlackboardPtr = nullptr;
		IBehavior* m_RootBehaviorPtr = nullptr;
	};
}