#include <psemek/parser/primitives.hpp>

#include <psemek/app/default_application_factory.hpp>

#include <iostream>
#include <fstream>
#include <iterator>

#include <psemek/util/clock.hpp>

#include <psemek/gfx/obj_parser.hpp>

template <typename Stream>
Stream & operator << (Stream & s, std::monostate)
{
	return s << "()";
}

template <typename Stream>
Stream & operator << (Stream & s, psemek::parser::end_token)
{
	return s << "(end)";
}

template <typename Stream>
Stream & operator << (Stream & s, psemek::parser::ws_token)
{
	return s << "(ws)";
}

template <typename Stream>
Stream & operator << (Stream & s, psemek::parser::newline_token)
{
	return s << "(newline)";
}

template <typename Stream, typename T>
Stream & operator << (Stream & s, std::optional<T> const & x)
{
	if (x)
		return s << *x;
	return s << "(none)";
}

template <typename Stream, typename T>
Stream & operator << (Stream & s, std::vector<T> const & v)
{
	s << '[';
	for (std::size_t i = 0; i < v.size(); ++i)
	{
		if (i > 0) s << ", ";
		s << v[i];
	}
	return s << ']';
}

template <typename Stream, typename T, std::size_t ... I>
void print_tuple (Stream & s, T const & t, std::index_sequence<I...>)
{
	((s << (I == 0 ? "" : ", ") << std::get<I>(t)), ...);
}

template <typename Stream, typename ... Ts>
Stream & operator << (Stream & s, std::tuple<Ts...> const & t)
{
	s << '(';
	print_tuple(s, t, std::make_index_sequence<sizeof...(Ts)>{});
	return s << ')';
}

template <typename Stream, typename T, typename ... Ts>
Stream & operator << (Stream & s, std::variant<T, Ts...> const & v)
{
	auto visitor = [&s](auto const & x){ s << x; };
	std::visit(visitor, v);
	return s;
}

namespace psemek::app
{

	std::unique_ptr<application::factory> make_application_factory()
	{
		return default_application_factory({.name = "Parser example"}, [](auto const & ...){
			using namespace psemek::parser;

			auto const p = map(concat(integer<int>, ws, one_of(ch('+'), ch('-')), ws, integer<int>), [](auto const & t){
				auto id = [](auto x){ return x; };
				return std::make_tuple(std::get<0>(t), std::visit(id, std::get<2>(t)), std::get<4>(t));
			});

			std::cout << p.parse("45 + 67") << std::endl;

			return nullptr;
		});
	}

}
