#pragma once

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

namespace psemek::bt
{

	template <typename Tree, typename DurationFn>
	struct wait_node;

	template <typename Time, typename Event, typename ... Args, typename DurationFn>
	struct wait_node<tree<Time, Event, Args...>, DurationFn>
		: 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::suspended;
		using typename node_type::status;

		wait_node(DurationFn && duration_fn)
			: duration_fn_(std::move(duration_fn))
		{}

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

			try
			{
				remaining_time_ = duration_fn_(args...);
			}
			catch (assertion_failed_exception const &)
			{
				failed_start_ = true;
			}
		}

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

			remaining_time_ -= dt;
			if (remaining_time_ <= 0)
				return finished{true};
			return suspended{remaining_time_};
		}

	private:
		DurationFn duration_fn_;
		Time remaining_time_ = Time{};
		bool failed_start_ = false;
	};

	namespace detail
	{

		template <typename Tree, typename DurationFn>
		struct wait_helper
		{
			static node_ptr<Tree> make(DurationFn && duration_fn)
			{
				return std::make_unique<wait_node<Tree, DurationFn>>(std::move(duration_fn));
			}
		};

		template <typename Time, typename Event, typename ... Args>
		struct wait_helper<tree<Time, Event, Args...>, Time>
		{
			static node_ptr<tree<Time, Event, Args...>> make(Time duration)
			{
				auto duration_fn = [duration](Args ...){ return duration; };
				return std::make_unique<wait_node<tree<Time, Event, Args...>, decltype(duration_fn)>>(std::move(duration_fn));
			}
		};

	}

	template <typename Tree, typename DurationFn>
	node_ptr<Tree> wait(DurationFn && duration_fn)
	{
		return detail::wait_helper<Tree, DurationFn>::make(std::move(duration_fn));
	}

}
