/*
Potential improvements:
* Hard code lengths for verses 0, 1, 2, and the common strings from verses 3 onwards.
* Calculate extra lengths needed for the common strings from verses 3 onwards using `1 + floor(log10(n))`.
Notes:
* http://www.mattmahoney.net/dc/text.html
* How to create a dictionary: https://groups.google.com/g/comp.compression/c/ZcOTiqck9Tc/m/1KHNFB5ocpoJ.
* cmix pre-processor converts uppercase characters to lowercase characters prepended with escapes codes which decreases symbol vocabulary but increases input length.
*/

/* const ALPHABET: [char; 27] = [
	"\n", // 0
	" ", // 1
	",", // 2
	".", // 3
	"9", // 4
	"G" // 5
	"N", // 6
	"T", // 7
	"a", // 8
	"b", // 9
	"d" // 10
	"e" // 11
	"f", // 12
	"h", // 13
	"i", // 14
	"k", // 15
	"l", // 16
	"m", // 17
	"n", // 18
	"o", // 19
	"p", // 20
	"r", // 21
	"s", // 22
	"t", // 23
	"u", // 24
	"w", // 25
	"y" // 26
]; */

/* "<n length> bottles of beer on the wall, <n length> bottles of beer.
Take one down and pass it around, <(n - 1) length> bottles of beer on the wall.

2 bottles of beer on the wall, 2 bottles of beer.
Take one down and pass it around, 1 bottle of beer on the wall.

1 bottle of beer on the wall, 1 bottle of beer.
Take it down and pass it around, no more bottles of beer on the wall.

No more bottles of beer on the wall, no more bottles of beer.
Go to the store and buy some more, 99 bottles of beer on the wall.
" */

const DICT: [&str; 24] = [
	"N", // 0
	"o m", // 1
	"ore", // 2
	" bottle", // 3
	"s", // 4
	" of beer", // 5
	" on", // 6
	" th", // 7
	"e ", // 8
	"wall", // 9
	", ", // 10
	"n", // 11
	".\n", // 12
	"Go to", // 13
	"st", // 14
	" and ", // 15
	"buy som", // 16
	"m", // 17
	"99", // 18
	"Take", // 19
	" it ", // 20
	"down", // 21
	"pass", // 22
	"around", // 23
];

// const LENGTHS: [usize; 4] = [];

trait CalculateLength {
	fn calculate_length(&self) -> usize;
}

struct Indices<'a>(&'a [usize]);

impl CalculateLength for Indices<'_> {
	fn calculate_length(&self) -> usize {
		self.0.iter().fold(0, |length, index| length + DICT[*index].len())
	}
}

struct IndicesWithPrefixes<'a>(&'a [(&'a str, Indices<'a>)]);

impl CalculateLength for IndicesWithPrefixes<'_> {
	fn calculate_length(&self) -> usize {
		self.0.iter().fold(0, |a, (prefix, indices)| a + prefix.len() + indices.calculate_length())
	}
}

impl IndicesWithPrefixes<'_> {
	fn generate_verse(&self) -> String {
		let mut out = String::with_capacity(self.calculate_length());
		self.0.iter().for_each(|(prefix, indices)| {
			out.push_str(prefix);
			indices.0.iter().for_each(|index| out.push_str(DICT[*index]));
		});
		out
	}
}

pub fn verse(n: u32) -> String {
	match n {
		0 => {
			let indices = Indices(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 1, 2, 3, 4, 5, 12, 13, 7, 8, 14, 2, 15, 16, 8, 17, 2, 10, 18, 3, 4, 5, 6, 7, 8, 9, 12]);
			let mut out = String::with_capacity(indices.calculate_length());
			indices.0.iter().for_each(|index| out.push_str(DICT[*index]));
			out
		}
		1 => {
			let n_string = n.to_string();
			IndicesWithPrefixes(&[(&n_string, Indices(&[3, 5, 6, 7, 8, 9, 10])), (&n_string, Indices(&[3, 5, 12, 19, 20, 21, 15, 22, 20, 23, 10, 11, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12]))]).generate_verse()
		}
		2 => {
			let (n_string, m_string) = (n.to_string(), (n - 1).to_string());
			IndicesWithPrefixes(&[(&n_string, Indices(&[3, 4, 5, 6, 7, 8, 9, 10])), (&n_string, Indices(&[3, 4, 5, 12, 19, 6, 8, 21, 15, 22, 20, 23, 10])), (&m_string, Indices(&[3, 5, 6, 7, 8, 9, 12]))]).generate_verse()
		}
		_ => {
			let (n_string, m_string) = (n.to_string(), (n - 1).to_string());
			IndicesWithPrefixes(&[(&n_string, Indices(&[3, 4, 5, 6, 7, 8, 9, 10])), (&n_string, Indices(&[3, 4, 5, 12, 19, 6, 8, 21, 15, 22, 20, 23, 10])), (&m_string, Indices(&[3, 4, 5, 6, 7, 8, 9, 12]))]).generate_verse()
		}
	}
}

pub fn sing(start: u32, end: u32) -> String {
	"".to_string()
}