﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace NumberWords
{
	public class Converter
	{
		private static readonly String[] _tens = {
			"",
			"ten",
			"twenty",
			"thirty",
			"forty",
			"fifty",
			"sixty",
			"seventy",
			"eighty",
			"ninety"
		};

		private static readonly String[] _uniques = {
			"zero",
			"one",
			"two",
			"three",
			"four",
			"five",
			"six",
			"seven",
			"eight",
			"nine",
			"ten",
			"eleven",
			"twelve",
			"thirteen",
			"fourteen",
			"fifteen",
			"sixteen",
			"seventeen",
			"eighteen",
			"nineteen"
		};

		public string DigitToWord(int digit)
		{
			if (0 <= digit && digit <= 9)
				return _uniques[digit];
			else
				return "";
		}

		private string convertHundreds(int number)
		{
			var parts = new List<string>();

			var h = (number - number % 100) / 100;
			if (h > 0)
				parts.Add(DigitToWord(h) + " hundred");

			number = number % 100;
			if (number < 20)
			{
				if (number > 0)
					parts.Add(_uniques[number]);
			}
			else
			{
				var t = (number - number % 10) / 10;
				parts.Add(_tens[t]);
				var o = number % (t * 10);
				if (o != 0)
					parts.Add(DigitToWord(o));
			}

			return String.Join(" ", parts);
		}

		public string IntToDigitWords(int number)
		{
			var digits = number.ToString().ToCharArray().Select(c => int.Parse(c.ToString()));
			return String.Join(" ", digits.Select(d => _uniques[d]));
		}

		public String IntToWords(int number)
		{
			if (number == 0)
				return "zero";

			var parts = new List<string>();

			if (number < 0)
			{
				parts.Add("negative");
				number = Math.Abs(number);
			}

			string padded = number.ToString("000000000000");

			int billionsPart = Int32.Parse(padded.Substring(0, 3));
			int millionsPart = Int32.Parse(padded.Substring(3, 3));
			int thousandsPart = Int32.Parse(padded.Substring(6, 3));
			int hundredsPart = Int32.Parse(padded.Substring(9, 3));

			if (billionsPart > 0)
			{
				var billions = convertHundreds(billionsPart) + " billion";
				parts.Add(billions);
			}

			if (millionsPart > 0)
			{
				var millions = convertHundreds(millionsPart) + " million";
				parts.Add(millions);
			}

			if (thousandsPart > 0)
			{
				var thousands = convertHundreds(thousandsPart) + " thousand";
				parts.Add(thousands);
			}

			if (hundredsPart >= 0)
			{
				var hundreds = convertHundreds(hundredsPart);
				parts.Add(hundreds);
			}

			return String.Join(" ", parts);
		}

		public string TextNumberToWords(string text)
		{
			var parts = new List<string>();

			var intPart = Regex.Match(text, @"^(\d+)\.?");
			var decimalPart = Regex.Match(text, @"\.(\d+)$");

			if (intPart.Groups.Count > 1 || decimalPart.Groups.Count > 1)
			{
				if (intPart.Groups.Count > 1)
				{
					int i = Int32.Parse(intPart.Groups[1].Value);
					parts.Add(IntToWords(i));
				}

				if (decimalPart.Groups.Count > 1)
				{
					int d = Int32.Parse(decimalPart.Groups[1].Value);

					if (d != 0)
					{
						parts.Add("point");
						parts.Add(IntToDigitWords(d));
					}
				}
			}
			else
			{
				parts.Add(text);
			}

			return String.Join(" ", parts);
		}

		public string TimeTextToWords(string text)
		{
			text = text.ToUpperInvariant();
			var parts = new List<string>();
			var match = Regex.Match(text, @"^(\d{1,2}):?(\d{2}) ?(AM|PM)$");
			var groups = match.Groups;

			if (groups.Count == 4)
			{
				int hours, minutes;
				string ampm = match.Groups[3].Value.ToLower();

				if (int.TryParse(groups[1].Value, out hours)
					&& int.TryParse(groups[2].Value, out minutes))
				{
					parts.Add(IntToWords(hours));

					if (minutes == 0)
						parts.Add("oclock");
					else
					{
						if (minutes < 10)
							parts.Add("oh");
						parts.Add(IntToWords(minutes));
					}

					if (!String.IsNullOrEmpty(ampm))
						parts.Add(ampm);
				}
			}
			else
			{
				parts.Add(text);
			}

			return String.Join(" ", parts);
		}
	}
}
