Option Explicit On
Option Strict On

'###########################
'Class: CashToWords
'Author: Adam Ward
'Date: 11 July 2005
'Purpose: Converts 123.45 into "ONE HUNDRED AND TWENTY THREE DOLLARS, FORTY FIVE CENTS"
'###########################

Imports System.Text

Public Class CashToWords

	Private Shared _Magnitudes() As String = New String() {"", " THOUSAND", " MILLION", " BILLION", " TRILLION", " QUADRILLION", " QUINTILLION", " HECTILLION", " SEPTILLION", " OCTILLION"}
	Private Shared _Tens() As String = New String() {"", "", "TWENTY", "THIRTY", "FORTY", "FIFTY", "SIXTY", "SEVENTY", "EIGHTY", "NINETY"}
	Private Shared _Units() As String = New String() {"", "ONE", "TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN", "EIGHT", "NINE", "TEN", "ELEVEN", "TWELVE", "THIRTEEN", "FOURTEEN", "FIFTEEN", "SIXTEEN", "SEVENTEEN", "EIGHTTEEN", "NINETEEN"}

	Public Shared Function WriteNumberInWords(ByVal number As Decimal) As String

		If number >= 1.0E+25 Then Throw New ArgumentException("Number must be less than or equal to 1.0E+25")
		If number < 0 Then Throw New ArgumentException("Number must be zero or greater")
		'if number >

		Dim buff As StringBuilder = New StringBuilder
		Dim numStr As String = String.Format("{0:f2}", number)
		Dim buf As StringBuilder = New StringBuilder
		Dim dot As Integer = numStr.IndexOf("."c)

		'pad the string to a group boundary

		numStr = New String("0"c, (3 - (dot Mod 3)) Mod 3) & numStr
		dot = numStr.IndexOf("."c)

		Dim upper As Integer = (dot \ 3) - 1
		Dim groups() As String = New String(upper) {}

		For i As Integer = 0 To upper
			groups(i) = numStr.Substring(i * 3, 3)
		Next i

		For i As Integer = 0 To upper
			Dim g As String = DoGroup(groups(i))
			If g.Length > 0 Then buf.Append(g & _Magnitudes(upper - i) & " ")
		Next i

		If buf.ToString.Trim.Length = 0 Then buf = New StringBuilder("ZERO ")

		Dim cents As String = DoTens(numStr.Substring(numStr.Length - 2, 2))
		If cents.Length = 0 Then cents = "ZERO"

		buf.Append("DOLLARS, " & cents & " CENTS")

		Return buf.ToString

	End Function

	Private Shared Function DoGroup(ByVal group As String, Optional ByVal includeAND As Boolean = True) As String

		group = group.PadLeft(3, "0"c)

		Dim A As String = ""

		If Char.IsDigit(group.Chars(0)) Then
			A = _Units(Integer.Parse(group.Chars(0)))
		End If

		If A.Length > 0 Then A &= " HUNDRED"

		Dim BC As String = DoTens(group.Substring(1, 2))

		If A.Length > 0 AndAlso BC.Length > 0 AndAlso includeAND Then A &= " AND "

		Return (A & BC).Trim

	End Function

	Private Shared Function DoTens(ByVal tens As String) As String

		Dim temp As String = ""
		Dim x As Integer = Integer.Parse(tens.PadLeft(2, "0"c))

		If x < 20 Then
			temp = _Units(x)
		Else
			temp = _Tens(Integer.Parse(tens.Chars(0))) & " " & _Units(Integer.Parse(tens.Chars(1))).Trim
		End If

		Return temp

	End Function

End Class
