#include "anltk/anltk.hpp"
#include <algorithm>
#include <array>
#include <string>
#include <vector>

namespace anltk
{

const std::string ahaad[20]
    = { "",          "واحد",     "اثنان",   "ثلاثة",    "أربعة",      "خمسة",     "ستة",
	    "سبعة",      "ثمانية",   "تسعة",    "عشرة",     "احدى عشر",   "اثنى عشر", "ثلاثة عشر",
	    "اربعة عشر", "خمسة عشر", "ستة عشر", "سبعة عشر", "ثمانية عشر", "تسعة عشر" };

const std::string ahaad_ordinal[][20]
    = { { // masculine
	      "",           "الواحد",     "الثاني",     "الثالث",     "الرابع",
	      "الخامس",     "السادس",     "السابع",     "الثامن",     "التاسع",
	      "العاشر",     "الحادي عشر", "الثاني عشر", "الثالث عشر", "الرابع عشر",
	      "الخامس عشر", "السادس عشر", "السابع عشر", "الثامن عشر", "التاسع عشر" },
	    { // feminine
	      "",
	      "الواحدة",
	      "الثانية",
	      "الثالثة",
	      "الرابعة",
	      "الخامسة",
	      "السادسة",
	      "السابعة",
	      "الثامنة",
	      "التاسعة",
	      "العاشرة",
	      "الحادية عشرة",
	      "الثانية عشرة",
	      "الثالثة عشرة",
	      "الرابعة عشرة",
	      "الخامسة عشرة",
	      "السادسة عشرة",
	      "السابعة عشرة",
	      "الثامنة عشرة",
	      "التاسعة عشرة" } };

const std::string asharat[10]
    = { "", "", "عشرون", "ثلاثون", "اربعون", "خمسون", "ستون", "سبعون", "ثمانون", "تسعون" };

const std::string meaat[10] = { "",       "مئة",   "مئتان",  "ثلاثمئة", "أربعمئة",
	                            "خمسمئة", "ستمئة", "سبعمئة", "ثمانمئة", "تسعمئة" };

const std::array<long long, 6> alaaf_numbers
    = { 1000, 1000000, 1000000000,
	    //  do we really need large numbers
	    1000000000000, 1000000000000000, 1000000000000000000 };

const std::string alaaf[][4] = {
	{ "", "ألف", "ألفان", "آلاف" },
	{ "", "مليون", "مليونان", "ملايين" },
	{ "", "مليار", "ملياران", "مليارات" },
	{ "", "ترليون", "ترليونان", "ترليونات" },
	{ "", "كوادريليون", "كوادريليونان", "كوادريليونات" },
	{ "", "ترليار", "ترلياران", "ترليارات" },
};

bool is_between(long long num, long long lower_bound, long long upper_bound)
{
	return num > lower_bound && num < upper_bound;
}

void tafqeet_impl(long long num, std::vector<std::string>& pieces, bool is_ordinal = false,
                  bool is_feminine = false)
{
	if (num == 0)
	{
		return;
	}
	if (num < 20)
	{
		pieces.push_back(is_ordinal ? ahaad_ordinal[is_feminine ? 1 : 0][num] : ahaad[num]);
		return;
	}
	if (num < 100)
	{
		pieces.push_back(is_ordinal ? ahaad_ordinal[is_feminine ? 1 : 0][num % 10]
		                            : ahaad[num % 10]);
		pieces.push_back(is_ordinal ? "ال" + asharat[num / 10] : asharat[num / 10]);
		return;
	}
	if (num < 1000)
	{
		pieces.push_back(is_ordinal ? "ال" + meaat[num / 100] : meaat[num / 100]);
		tafqeet_impl(num % 100, pieces, is_ordinal, is_feminine);
		return;
	}

	auto handle_triplet = [&](long long thousands_digits, long long current_multiplier, size_t i)
	{
		if (thousands_digits < 3)
		{
			std::string next_piece = alaaf[i - 1][thousands_digits];
			pieces.push_back(is_ordinal ? "ال" + next_piece : next_piece);
		}
		else
		{
			long long tmp = current_multiplier / 10;
			tafqeet_impl(thousands_digits, pieces, is_ordinal, is_feminine);
			pieces.back() += is_between(thousands_digits % tmp, 2, 11) ? " " + alaaf[i - 1][3]
			                                                           : " " + alaaf[i - 1][1];
		}
		tafqeet_impl(num % current_multiplier, pieces, is_ordinal, is_feminine);
	};

	for (size_t i = 1; i < alaaf_numbers.size(); ++i)
	{
		if (num < alaaf_numbers[i])
		{
			long long current_multiplier = alaaf_numbers[i - 1];
			long long thousands_digits   = num / current_multiplier;
			handle_triplet(thousands_digits, current_multiplier, i);
			return;
		}
	}

	size_t i                     = alaaf_numbers.size();
	long long current_multiplier = alaaf_numbers[i - 1];
	long long thousands_digits   = num / current_multiplier;
	handle_triplet(thousands_digits, current_multiplier, i);

	// tafqeet_impl(num % current_multiplier, pieces, is_ordinal, is_feminine);
}

std::string tafqeet_internal(long long number, bool is_ordinal, bool is_feminine)
{
	bool is_negative = false;
	if (number < 0)
	{
		is_negative = true;
		number      = -number;
	}
	if (number == 0)
	{
		return std::string() + (is_negative ? "سالب " : "") + (is_ordinal ? "الصفر" : "صفر");
	}
	if (number == 1 && is_ordinal)
	{
		return std::string() + (is_negative ? "سالب " : "") + (is_feminine ? "الأولى" : "الأول");
	}

	std::vector<std::string> pieces;

	tafqeet_impl(number, pieces, is_ordinal, is_feminine);

	pieces.erase(std::remove_if(pieces.begin(), pieces.end(),
	                            [](const std::string& elm) { return elm.empty(); }),
	             pieces.end());

	std::string res = pieces.front();

	for (size_t i = 1; i < pieces.size(); ++i)
	{
		if (!pieces[i].empty())
			res += " و " + pieces[i];
	}

	return (is_negative ? "سالب " : "") + res;
}

string_t tafqeet(long long number, bool is_ordinal, bool is_feminine)
{
	// TODO: Needs importvent, قواعد العدد والمعدود, وزيادة الخيارات,
	// TODO: Use this as refrence https://github.com/MohsenAlyafei/tafqit
	return tafqeet_internal(number, is_ordinal, is_feminine);
}

} // namespace anltk