// Matrix Construct
//
// Copyright (C) Matrix Construct Developers, Authors & Contributors
// Copyright (C) 2016-2018 Jason Volk <jason@zemos.net>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file.

namespace ircd::lex
{
	struct to_float_policy;
	struct to_double_policy;
	struct to_long_double_policy;

	struct from_float_policy;
	struct from_double_policy;
	struct from_long_double_policy;

	template<class T,
	         class... A>
	using rule_to = spirit::qi::rule<const char *, T, A...>;

	template<class T,
	         class... A>
	using rule_from = spirit::karma::rule<char *, T, A...>;

	template<class T,
	         class rule,
	         class exception>
	[[noreturn]]
	static void throw_error(const rule &, const exception &e);

	template<class T,
	         const rule_to<T> &r>
	static bool test(const string_view &) noexcept;

	template<class T,
	         const rule_to<T> &r>
	static T cast(const string_view &);

	template<class T,
	         const rule_from<T()> &r>
	static string_view cast(const mutable_buffer &, T);
}

#pragma GCC visibility push(internal)
namespace ircd::lex
{
	extern const rule_from<bool()> from_bool;
	extern const rule_from<int8_t()> from_int8_t;
	extern const rule_from<uint8_t()> from_uint8_t;
	extern const rule_from<short()> from_short;
	extern const rule_from<ushort()> from_ushort;
	extern const rule_from<int()> from_int;
	extern const rule_from<uint()> from_uint;
	extern const rule_from<long()> from_long;
	extern const rule_from<ulong()> from_ulong;
	extern const rule_from<float()> from_float;
	extern const rule_from<double()> from_double;
	extern const rule_from<long double()> from_long_double;

	extern const rule_to<bool> to_bool, is_bool;
	extern const rule_to<int8_t> to_int8_t, is_int8_t;
	extern const rule_to<uint8_t> to_uint8_t, is_uint8_t;
	extern const rule_to<short> to_short, is_short;
	extern const rule_to<ushort> to_ushort, is_ushort;
	extern const rule_to<int> to_int, is_int;
	extern const rule_to<uint> to_uint, is_uint;
	extern const rule_to<long> to_long, is_long;
	extern const rule_to<ulong> to_ulong, is_ulong;
	extern const rule_to<float> to_float, is_float;
	extern const rule_to<double> to_double, is_double;
	extern const rule_to<long double> to_long_double, is_long_double;
}
#pragma GCC visibility pop

static_assert
(
	ircd::LEX_CAST_BUFS == 256 && sizeof(ircd::lex_cast_buf_head) == 1,
	"ircd::lex_cast ring buffer integer may not modulate as intended"
);

/// Indicates the next buffer to be used in the ring. See below.
thread_local
decltype(ircd::lex_cast_buf_head)
ircd::lex_cast_buf_head
alignas(16);

/// This is a static "ring buffer" to simplify a majority of lex_cast uses.
/// The returned string_view of data from this buffer is only valid for
/// several more calls to lex_cast before it is overwritten.
thread_local
decltype(ircd::lex_cast_buf)
ircd::lex_cast_buf
alignas(64);

template<class T,
         const ircd::lex::rule_from<T()> &rule>
[[gnu::always_inline]]
inline ircd::string_view
ircd::lex::cast(const mutable_buffer &out,
                T in)
try
{
	constexpr bool truncation
	{
		false
	};

	mutable_buffer buf
	{
		out
	};

	const bool pass
	{
		ircd::generate<truncation>
		(
			buf, rule | spirit::eps, in
		)
	};

	assert(pass);
	return string_view
	{
		data(out), data(buf)
	};
}
catch(const std::exception &e)
{
	throw_error<T>(rule, e);
	__builtin_unreachable();
}

template<class T,
         const ircd::lex::rule_to<T> &rule>
[[gnu::always_inline]]
inline T
ircd::lex::cast(const string_view &s)
try
{
	T ret {};
	const char *start(begin(s)), *const stop(end(s));
	const bool pass
	{
		ircd::parse(start, stop, rule, ret)
	};

	assert(pass);
	return ret;
}
catch(const std::exception &e)
{
	throw_error<T>(rule, e);
	__builtin_unreachable();
}

template<class T,
         const ircd::lex::rule_to<T> &rule>
[[gnu::always_inline]]
inline bool
ircd::lex::test(const string_view &s)
noexcept
{
	const char *start(begin(s)), *const stop(end(s));
	return ircd::parse(std::nothrow, start, stop, rule);
}

template<class T,
         class rule,
         class exception>
[[noreturn, gnu::noinline, gnu::cold]]
void
ircd::lex::throw_error(const rule &r,
                       const exception &e)
{
	throw bad_lex_cast
	{
		"Invalid lexical conversion of %s (%s).",
		r.name(),
		demangle(typeid(T).name()),
	};
}

//
// bool
//

decltype(ircd::lex::from_bool)
ircd::lex::from_bool
{
	spirit::karma::bool_
	,"boolean"
};

decltype(ircd::lex::to_bool)
ircd::lex::to_bool
{
	spirit::expect[spirit::qi::bool_ >> spirit::eoi]
	,"boolean"
};

decltype(ircd::lex::is_bool)
ircd::lex::is_bool
{
	spirit::qi::bool_ >> spirit::eoi
	,"boolean"
};

template<> ircd::string_view
ircd::lex_cast(bool i,
               const mutable_buffer &buf)
{
	return lex::cast<bool, lex::from_bool>(buf, i);
}

template<> bool
ircd::lex_cast(const string_view &s)
{
	const bool literal[]
	{
		s == "true",
		s == "false",
		s == "null",
	};

	// Handle the common cases early in this frame before call.
	if(likely(literal[0] | literal[1] | literal[2]))
		return literal[0];

	return lex::cast<bool, lex::to_bool>(s);
}

template<> bool
ircd::lex_castable<bool>(const string_view &s)
noexcept
{
	const bool literal[]
	{
		s == "true",
		s == "false",
		s == "null",
	};

	// Handle the common cases early in this frame before call.
	if(likely(literal[0] | literal[1] | literal[2]))
		return true;

	return lex::test<bool, lex::is_bool>(s);
}

//
// int8_t
//

decltype(ircd::lex::from_int8_t)
ircd::lex::from_int8_t
{
	spirit::karma::ushort_
	,"signed byte"
};

decltype(ircd::lex::to_int8_t)
ircd::lex::to_int8_t
{
	spirit::expect[spirit::qi::ushort_ >> spirit::eoi]
	,"signed byte"
};

decltype(ircd::lex::is_int8_t)
ircd::lex::is_int8_t
{
	spirit::qi::ushort_ >> spirit::eoi
	,"signed byte"
};

template<> ircd::string_view
ircd::lex_cast(int8_t i,
               const mutable_buffer &buf)
{
	return lex::cast<int8_t, lex::from_int8_t>(buf, i);
}

template<> int8_t
ircd::lex_cast(const string_view &s)
{
	return lex::cast<int8_t, lex::to_int8_t>(s);
}

template<> bool
ircd::lex_castable<int8_t>(const string_view &s)
noexcept
{
	return lex::test<int8_t, lex::is_int8_t>(s);
}

//
// uint8_t
//

decltype(ircd::lex::from_uint8_t)
ircd::lex::from_uint8_t
{
	spirit::karma::ushort_
	,"unsigned byte"
};

decltype(ircd::lex::to_uint8_t)
ircd::lex::to_uint8_t
{
	spirit::expect[spirit::qi::ushort_ >> spirit::eoi]
	,"unsigned byte"
};

decltype(ircd::lex::is_uint8_t)
ircd::lex::is_uint8_t
{
	spirit::qi::ushort_ >> spirit::eoi
	,"unsigned byte"
};

template<> ircd::string_view
ircd::lex_cast(uint8_t i,
               const mutable_buffer &buf)
{
	return lex::cast<uint8_t, lex::from_uint8_t>(buf, i);
}

template<> uint8_t
ircd::lex_cast(const string_view &s)
{
	return lex::cast<uint8_t, lex::to_uint8_t>(s);
}

template<> bool
ircd::lex_castable<uint8_t>(const string_view &s)
noexcept
{
	return lex::test<uint8_t, lex::is_uint8_t>(s);
}

//
// short
//

decltype(ircd::lex::from_short)
ircd::lex::from_short
{
	spirit::karma::short_
	,"signed short integer"
};

decltype(ircd::lex::to_short)
ircd::lex::to_short
{
	spirit::expect[spirit::qi::short_ >> spirit::eoi]
	,"signed short integer"
};

decltype(ircd::lex::is_short)
ircd::lex::is_short
{
	spirit::qi::short_ >> spirit::eoi
	,"signed short integer"
};

template<> ircd::string_view
ircd::lex_cast(short i,
               const mutable_buffer &buf)
{
	return lex::cast<short, lex::from_short>(buf, i);
}

template<> short
ircd::lex_cast(const string_view &s)
{
	return lex::cast<short, lex::to_short>(s);
}

template<> bool
ircd::lex_castable<short>(const string_view &s)
noexcept
{
	return lex::test<short, lex::is_short>(s);
}

//
// ushort
//

decltype(ircd::lex::from_ushort)
ircd::lex::from_ushort
{
	spirit::karma::ushort_
	,"unsigned short integer"
};

decltype(ircd::lex::to_ushort)
ircd::lex::to_ushort
{
	spirit::expect[spirit::qi::ushort_ >> spirit::eoi]
	,"unsigned short integer"
};

decltype(ircd::lex::is_ushort)
ircd::lex::is_ushort
{
	spirit::qi::ushort_ >> spirit::eoi
	,"unsigned short integer"
};

template<> ircd::string_view
ircd::lex_cast(ushort i,
               const mutable_buffer &buf)
{
	return lex::cast<ushort, lex::from_ushort>(buf, i);
}

template<> ushort
ircd::lex_cast(const string_view &s)
{
	return lex::cast<ushort, lex::to_ushort>(s);
}

template<> bool
ircd::lex_castable<ushort>(const string_view &s)
noexcept
{
	return lex::test<ushort, lex::is_ushort>(s);
}

//
// signed
//

decltype(ircd::lex::from_int)
ircd::lex::from_int
{
	spirit::karma::int_
	,"signed integer"
};

decltype(ircd::lex::to_int)
ircd::lex::to_int
{
	spirit::expect[spirit::qi::int_ >> spirit::eoi]
	,"signed integer"
};

decltype(ircd::lex::is_int)
ircd::lex::is_int
{
	spirit::qi::int_ >> spirit::eoi
	,"signed integer"
};

template<> ircd::string_view
ircd::lex_cast(int i,
               const mutable_buffer &buf)
{
	return lex::cast<int, lex::from_int>(buf, i);
}

template<> int
ircd::lex_cast(const string_view &s)
{
	return lex::cast<int, lex::to_int>(s);
}

template<> bool
ircd::lex_castable<int>(const string_view &s)
noexcept
{
	return lex::test<int, lex::is_int>(s);
}

//
// unsigned
//

decltype(ircd::lex::from_uint)
ircd::lex::from_uint
{
	spirit::karma::uint_
	,"unsigned integer"
};

decltype(ircd::lex::to_uint)
ircd::lex::to_uint
{
	spirit::expect[spirit::qi::uint_ >> spirit::eoi]
	,"unsigned integer"
};

decltype(ircd::lex::is_uint)
ircd::lex::is_uint
{
	spirit::qi::uint_ >> spirit::eoi
	,"unsigned integer"
};

template<> ircd::string_view
ircd::lex_cast(uint i,
               const mutable_buffer &buf)
{
	return lex::cast<uint, lex::from_uint>(buf, i);
}

template<> unsigned int
ircd::lex_cast(const string_view &s)
{
	return lex::cast<uint, lex::to_uint>(s);
}

template<> bool
ircd::lex_castable<uint>(const string_view &s)
noexcept
{
	return lex::test<uint, lex::is_uint>(s);
}

//
// long
//

decltype(ircd::lex::from_long)
ircd::lex::from_long
{
	spirit::karma::long_
	,"long integer"
};

decltype(ircd::lex::to_long)
ircd::lex::to_long
{
	spirit::expect[spirit::qi::long_ >> spirit::eoi]
	,"long integer"
};

decltype(ircd::lex::is_long)
ircd::lex::is_long
{
	spirit::qi::long_ >> spirit::eoi
	,"long integer"
};

template<> ircd::string_view
ircd::lex_cast(long i,
               const mutable_buffer &buf)
{
	return lex::cast<long, lex::from_long>(buf, i);
}

template<> long
ircd::lex_cast(const string_view &s)
{
	return lex::cast<long, lex::to_long>(s);
}

template<> bool
ircd::lex_castable<long>(const string_view &s)
noexcept
{
	return lex::test<long, lex::is_long>(s);
}

//
// ulong
//

decltype(ircd::lex::from_ulong)
ircd::lex::from_ulong
{
	spirit::karma::ulong_
	,"long unsigned integer"
};

decltype(ircd::lex::to_ulong)
ircd::lex::to_ulong
{
	spirit::expect[spirit::qi::ulong_ >> spirit::eoi]
	,"long unsigned integer"
};

decltype(ircd::lex::is_ulong)
ircd::lex::is_ulong
{
	spirit::qi::ulong_ >> spirit::eoi
	,"long unsigned integer"
};

template<> ircd::string_view
ircd::lex_cast(ulong i,
               const mutable_buffer &buf)
{
	return lex::cast<ulong, lex::from_ulong>(buf, i);
}

template<> unsigned long
ircd::lex_cast(const string_view &s)
{
	return lex::cast<ulong, lex::to_ulong>(s);
}

template<> bool
ircd::lex_castable<ulong>(const string_view &s)
noexcept
{
	return lex::test<ulong, lex::is_ulong>(s);
}

//
// float
//

struct [[gnu::visibility("internal")]]
ircd::lex::to_float_policy
:spirit::qi::real_policies<float>
{
	static const bool
	allow_leading_dot   { false },
	allow_trailing_dot  { false },
	expect_dot          { false };
};

struct [[gnu::visibility("internal")]]
ircd::lex::from_float_policy
:spirit::karma::real_policies<float>
{

};

decltype(ircd::lex::from_float)
ircd::lex::from_float
{
	spirit::karma::real_generator<float, from_float_policy>{}
	,"single floating point precision"
};

decltype(ircd::lex::to_float)
ircd::lex::to_float
{
	spirit::expect[spirit::qi::real_parser<float, to_float_policy>{} >> spirit::eoi]
	,"single floating point precision"
};

decltype(ircd::lex::is_float)
ircd::lex::is_float
{
	spirit::qi::real_parser<float, to_float_policy>{} >> spirit::eoi
	,"single floating point precision"
};

template<> ircd::string_view
ircd::lex_cast(float i,
               const mutable_buffer &buf)
{
	return lex::cast<float, lex::from_float>(buf, i);
}

template<> float
ircd::lex_cast(const string_view &s)
{
	return lex::cast<float, lex::to_float>(s);
}

template<> bool
ircd::lex_castable<float>(const string_view &s)
noexcept
{
	return lex::test<float, lex::is_float>(s);
}

//
// double
//

struct [[gnu::visibility("internal")]]
ircd::lex::to_double_policy
:spirit::qi::real_policies<double>
{
	static const bool
	allow_leading_dot   { false },
	allow_trailing_dot  { false },
	expect_dot          { false };
};

struct [[gnu::visibility("internal")]]
ircd::lex::from_double_policy
:spirit::karma::real_policies<double>
{

};

decltype(ircd::lex::from_double)
ircd::lex::from_double
{
	spirit::karma::real_generator<double, from_double_policy>{}
	,"double floating point precision"
};

decltype(ircd::lex::to_double)
ircd::lex::to_double
{
	spirit::expect[spirit::qi::real_parser<double, to_double_policy>{} >> spirit::eoi]
	,"double floating point precision"
};

decltype(ircd::lex::is_double)
ircd::lex::is_double
{
	spirit::qi::real_parser<double, to_double_policy>{} >> spirit::eoi
	,"double floating point precision"
};

template<> ircd::string_view
ircd::lex_cast(double i,
               const mutable_buffer &buf)
{
	return lex::cast<double, lex::from_double>(buf, i);
}

template<> double
ircd::lex_cast(const string_view &s)
{
	return lex::cast<double, lex::to_double>(s);
}

template<> bool
ircd::lex_castable<double>(const string_view &s)
noexcept
{
	return lex::test<double, lex::is_double>(s);
}

//
// long double
//

struct [[gnu::visibility("internal")]]
ircd::lex::to_long_double_policy
:spirit::qi::real_policies<long double>
{
	static const bool
	allow_leading_dot   { false },
	allow_trailing_dot  { false },
	expect_dot          { false };
};

struct [[gnu::visibility("internal")]]
ircd::lex::from_long_double_policy
:spirit::karma::real_policies<long double>
{

};

decltype(ircd::lex::from_long_double)
ircd::lex::from_long_double
{
	spirit::karma::real_generator<long double, from_long_double_policy>{}
	,"long double floating point precision"
};

decltype(ircd::lex::to_long_double)
ircd::lex::to_long_double
{
	spirit::expect[spirit::qi::real_parser<long double, to_long_double_policy>{} >> spirit::eoi]
	,"long double floating point precision"
};

decltype(ircd::lex::is_long_double)
ircd::lex::is_long_double
{
	spirit::qi::real_parser<long double, to_long_double_policy>{} >> spirit::eoi
	,"long double floating point precision"
};

template<> ircd::string_view
ircd::lex_cast(long double i,
               const mutable_buffer &buf)
{
	return lex::cast<long double, lex::from_long_double>(buf, i);
}

template<> long double
ircd::lex_cast(const string_view &s)
{
	return lex::cast<long double, lex::to_long_double>(s);
}

template<> bool
ircd::lex_castable<long double>(const string_view &s)
noexcept
{
	return lex::test<long double, lex::is_long_double>(s);
}

//
// hours
//

template<> ircd::string_view
ircd::lex_cast(hours i,
               const mutable_buffer &buf)
{
	return lex::cast<time_t, lex::from_long>(buf, i.count());
}

template<> ircd::hours
ircd::lex_cast(const string_view &s)
{
	return std::chrono::duration<time_t, std::ratio<3600L, 1L>>
	(
		lex::cast<time_t, lex::to_long>(s)
	);
}

template<> bool
ircd::lex_castable<ircd::hours>(const string_view &s)
noexcept
{
	return lex::test<time_t, lex::is_long>(s);
}

//
// minutes
//

template<> ircd::string_view
ircd::lex_cast(minutes i,
               const mutable_buffer &buf)
{
	return lex::cast<time_t, lex::from_long>(buf, i.count());
}

template<> ircd::minutes
ircd::lex_cast(const string_view &s)
{
	return std::chrono::duration<time_t, std::ratio<60L, 1L>>
	(
		lex::cast<time_t, lex::to_long>(s)
	);
}

template<> bool
ircd::lex_castable<ircd::minutes>(const string_view &s)
noexcept
{
	return lex::test<time_t, lex::is_long>(s);
}

//
// seconds
//

template<> ircd::string_view
ircd::lex_cast(seconds i,
               const mutable_buffer &buf)
{
	return lex::cast<time_t, lex::from_long>(buf, i.count());
}

template<> ircd::seconds
ircd::lex_cast(const string_view &s)
{
	return std::chrono::duration<time_t, std::ratio<1L, 1L>>
	(
		lex::cast<time_t, lex::to_long>(s)
	);
}

template<> bool
ircd::lex_castable<ircd::seconds>(const string_view &s)
noexcept
{
	return lex::test<time_t, lex::is_long>(s);
}

//
// milliseconds
//

template<> ircd::string_view
ircd::lex_cast(milliseconds i,
               const mutable_buffer &buf)
{
	return lex::cast<time_t, lex::from_long>(buf, i.count());
}

template<> ircd::milliseconds
ircd::lex_cast(const string_view &s)
{
	return std::chrono::duration<time_t, std::ratio<1L, 1000L>>
	(
		lex::cast<time_t, lex::to_long>(s)
	);
}

template<> bool
ircd::lex_castable<ircd::milliseconds>(const string_view &s)
noexcept
{
	return lex::test<time_t, lex::is_long>(s);
}

//
// microseconds
//

template<> ircd::string_view
ircd::lex_cast(microseconds i,
               const mutable_buffer &buf)
{
	return lex::cast<time_t, lex::from_long>(buf, i.count());
}

template<> ircd::microseconds
ircd::lex_cast(const string_view &s)
{
	return std::chrono::duration<time_t, std::ratio<1L, 1000000L>>
	(
		lex::cast<time_t, lex::to_long>(s)
	);
}

template<> bool
ircd::lex_castable<ircd::microseconds>(const string_view &s)
noexcept
{
	return lex::test<time_t, lex::is_long>(s);
}

//
// nanoseconds
//

template<> ircd::string_view
ircd::lex_cast(nanoseconds i,
               const mutable_buffer &buf)
{
	return lex::cast<time_t, lex::from_long>(buf, i.count());
}

template<> ircd::nanoseconds
ircd::lex_cast(const string_view &s)
{
	return std::chrono::duration<time_t, std::ratio<1L, 1000000000L>>
	(
		lex::cast<time_t, lex::to_long>(s)
	);
}

template<> bool
ircd::lex_castable<ircd::nanoseconds>(const string_view &s)
noexcept
{
	return lex::test<time_t, lex::is_long>(s);
}
