#pragma once

#include <psemek/bt/node.hpp>
#include <psemek/bt/assert.hpp>

#include <vector>

namespace psemek::bt
{

	template <typename Tree, typename IndexFn>
	struct by_index_node;

	template <typename Time, typename Event, typename ... Args, typename IndexFn>
	struct by_index_node<tree<Time, Event, Args...>, IndexFn>
		: node<tree<Time, Event, Args...>>
	{
		using tree_type = tree<Time, Event, Args...>;
		using node_type = node<tree_type>;
		using typename node_type::finished;
		using typename node_type::status;

		by_index_node(IndexFn && index_fn, std::vector<node_ptr<tree_type>> children)
			: index_fn_(std::move(index_fn))
			, children_(std::move(children))
		{}

		void start(Args ... args) override
		{
			failed_start_ = false;

			try
			{
				current_index_ = index_fn_(args...);
			}
			catch (assertion_failed_exception const &)
			{
				failed_start_ = true;
				return;
			}

			if (current_index_ < children_.size())
				children_[current_index_]->start(args...);
		}

		status update(Time dt, Args ... args) override
		{
			if (failed_start_)
				return finished{false};

			if (current_index_ >= children_.size())
				return finished{false};

			return children_[current_index_]->update(dt, args...);
		}

		bool event(Event const & event, Args ... args) override
		{
			if (current_index_ < children_.size())
				return children_[current_index_]->event(event, args...);

			return false;
		}

	private:
		IndexFn index_fn_;
		bool failed_start_ = false;
		std::vector<node_ptr<tree_type>> children_;
		std::size_t current_index_ = 0;
	};

	template <typename Tree, typename IndexFn, typename ... Nodes>
	node_ptr<Tree> by_index(IndexFn && index_fn, node_ptr<Tree> child, Nodes && ... other_children)
	{
		std::vector<node_ptr<Tree>> children;
		children.push_back(std::move(child));
		(children.push_back(std::move(other_children)), ...);
		return std::make_unique<by_index_node<Tree, IndexFn>>(std::move(index_fn), std::move(children));
	}

}
