/// A few helper functions for manipulating English words. Extremely basic.
module arsd.english;

/++
	Given a non-one `count` argument, will attempt to return the plural version of `word`. Only handles basic cases. If count is one, simply returns the word.

	I originally wrote this for cases like `You have {n} {messages|plural(n)}` in web templates.
+/
string plural(int count, string word, string pluralWord = null) {
	if(count == 1 || word.length == 0)
		return word; // it isn't actually plural

	if(pluralWord !is null)
		return pluralWord;

	switch(word[$ - 1]) {
		case 's':
		case 'a', 'e', 'i', 'o', 'u':
			return word ~ "es";
		case 'f':
			return word[0 .. $-1] ~ "ves";
		case 'y':
			return word[0 .. $-1] ~ "ies";
		default:
			return word ~ "s";
	}
}

/// Given an integer, tries to write out the long form number. For example, -5 becomes "negative five".
string numberToEnglish(long number) {
	string word;
	if(number == 0)
		return "zero";

	if(number < 0) {
		word = "negative";
		number = -number;
	}

	while(number) {
		if(number < 100) {
			if(number < singleWords.length) {
				word ~= singleWords[cast(int) number];
				break;
			} else {
				auto tens = number / 10;
				word ~= tensPlaceWords[cast(int) tens];
				number = number % 10;
				if(number)
					word ~= "-";
			}
		} else if(number < 1_000) {
			auto hundreds = number / 100;
			word ~= onesPlaceWords[cast(int) hundreds] ~ " hundred";
			number = number % 100;
			if(number)
				word ~= " and ";
		} else if(number < 1_000_000) {
			auto thousands = number / 1_000;
			word ~= numberToEnglish(thousands) ~ " thousand";
			number = number % 1_000;
			if(number)
				word ~= ", ";
		} else if(number < 1_000_000_000) {
			auto millions = number / 1_000_000;
			word ~= numberToEnglish(millions) ~ " million";
			number = number % 1_000_000;
			if(number)
				word ~= ", ";
		} else if(number < 1_000_000_000_000) {
			auto n = number / 1_000_000_000;
			word ~= numberToEnglish(n) ~ " billion";
			number = number % 1_000_000_000;
			if(number)
				word ~= ", ";
		} else if(number < 1_000_000_000_000_000) {
			auto n = number / 1_000_000_000_000;
			word ~= numberToEnglish(n) ~ " trillion";
			number = number % 1_000_000_000_000;
			if(number)
				word ~= ", ";
		} else {
			import std.conv;
			return to!string(number);
		}
	}

	return word;
}

unittest {
	assert(numberToEnglish(1) == "one");
	assert(numberToEnglish(5) == "five");
	assert(numberToEnglish(13) == "thirteen");
	assert(numberToEnglish(54) == "fifty-four");
	assert(numberToEnglish(178) == "one hundred and seventy-eight");
	assert(numberToEnglish(592) == "five hundred and ninety-two");
	assert(numberToEnglish(1234) == "one thousand, two hundred and thirty-four");
	assert(numberToEnglish(10234) == "ten thousand, two hundred and thirty-four");
	assert(numberToEnglish(105234) == "one hundred and five thousand, two hundred and thirty-four");
}

enum onesPlaceWords = [
	"zero",
	"one",
	"two",
	"three",
	"four",
	"five",
	"six",
	"seven",
	"eight",
	"nine",
];

enum singleWords = onesPlaceWords ~ [
	"ten",
	"eleven",
	"twelve",
	"thirteen",
	"fourteen",
	"fifteen",
	"sixteen",
	"seventeen",
	"eighteen",
	"nineteen",
];

enum tensPlaceWords = [
	null,
	"ten",
	"twenty",
	"thirty",
	"forty",
	"fifty",
	"sixty",
	"seventy",
	"eighty",
	"ninety",
];

/*
void main() {
	import std.stdio;
	foreach(i; 3433000 ..3433325)
	writeln(numberToEnglish(i));
}
*/