<?php

	class CLASS_Numbertoword {
		
		const bAppendS = true; //Use this when the unit takes a "s" when multiplied
		const bDontAppendS = false; //Use this when the unit does'n't takes a "s" when multiplied
		
		const bSpecifyOne = true; //use this when we need to use the "one" word before the unit ie: un million
		const bDontSpecifyOne = false; //use this when we don't need to use the "one" word before the unit ie: mille (not "un mille")
		
		const bUseAndOne = true; //some tens might require a "and" modification ie : vingt-et-un
		const bDontUseAndOne = false; //some tens don't need a "and" modification ie: quatre-vingt-un
		
		private static $sCurrentLanguage; //we'll use this to store the language we are working with
		
		private static $a_Config = array (
			 
			'EN' => array (
				'AndOneWord' => '',
				'HundredsAndTensSeparator' => ' and ',
				'Minus' => 'minus',
				'Exceptions' => array (
					'11'=> 'eleven',
					'12'=> 'twelve',
					'13'=> 'thirteen',
					'14'=> 'fourteen',
					'15'=> 'fifteen',
					'16'=> 'sixteen',	
					'17'=> 'seventeen',	
					'18'=> 'eighteen',	
					'19'=> 'nineteen'
				),
				'Units' => array (
					'zero',
					'one',
					'two',
					'three',
					'four',
					'five',
					'six',
					'seven',
					'eight',
					'nine',
				),
				'Tens' => array(
					array ('',			self::bDontUseAndOne), //left empty so that the key is equal to the number
					array ('ten',		self::bDontUseAndOne),
					array ('twenty',	self::bDontUseAndOne),
					array ('thirty',	self::bDontUseAndOne),
					array ('fourty',	self::bDontUseAndOne),
					array ('fifty',		self::bDontUseAndOne),
					array ('sixty',		self::bDontUseAndOne),
					array ('seventy',	self::bDontUseAndOne),
					array ('eighty',	self::bDontUseAndOne),
					array ('ninety',	self::bDontUseAndOne)
				),
				'Multipliers' => array (
					array ('hundred',	self::bSpecifyOne,	self::bDontAppendS),
					array ('thousand',	self::bSpecifyOne,	self::bDontAppendS),
					array ('million',	self::bSpecifyOne,	self::bDontAppendS),
					array ('billion',	self::bSpecifyOne,	self::bDontAppendS),
					array ('trillion',	self::bSpecifyOne,	self::bDontAppendS)
				)
			)
		);		
		
		private static function chunkToWord ($sChunk, $iMultiplier){
			$sReturn = '';
			
			if ($sChunk[0] == '0'){
				
			}
			else {
				if ($sChunk[0] == '1'){
					if (self::$a_Config[self::$sCurrentLanguage]['Multipliers'][0][1] == self::bDontSpecifyOne) {
						$sReturn .= self::$a_Config[self::$sCurrentLanguage]['Multipliers'][0][0];
					}
					else {
						$sReturn .= self::$a_Config[self::$sCurrentLanguage]['Units'][1].' '.self::$a_Config[self::$sCurrentLanguage]['Multipliers'][0][0];
					}
				}
				else {
					$sReturn .= self::$a_Config[self::$sCurrentLanguage]['Units'][$sChunk[0]].' '.self::$a_Config[self::$sCurrentLanguage]['Multipliers'][0][0];
						
					if (self::$a_Config[self::$sCurrentLanguage]['Multipliers'][0][2]){
						if (self::$sCurrentLanguage == 'FR'){
							if ($sChunk[1].$sChunk[2] == '00'){
								$sReturn .= 's';
							}
						}
						else {
							$sReturn .= 's';
						}
					}
				}
				if ($sChunk[1].$sChunk[2] != '00'){
					$sReturn .= self::$a_Config[self::$sCurrentLanguage]['HundredsAndTensSeparator'];
				}	
				else {
					$sReturn .= ' ';
				}
			}
			if (isset(self::$a_Config[self::$sCurrentLanguage]['Exceptions'][$sChunk[1].$sChunk[2]])){
				$sReturn .= self::$a_Config[self::$sCurrentLanguage]['Exceptions'][$sChunk[1].$sChunk[2]];
			}
			else {
				if ($sChunk[1] != '0'){
					$sReturn .= self::$a_Config[self::$sCurrentLanguage]['Tens'][$sChunk[1]][0];
				}
				if ($sChunk[2] != '0'){
					if ($sChunk[2] == '1' && self::$a_Config[self::$sCurrentLanguage]['Tens'][$sChunk[1]][1] == self::bUseAndOne){
						$sReturn .= self::$a_Config[self::$sCurrentLanguage]['AndOneWord'];
					}
					else {
						$sReturn .= ($sChunk[1] != '0' ?'-':'').self::$a_Config[self::$sCurrentLanguage]['Units'][$sChunk[2]];
					}
				}
			}
			
			if ($iMultiplier != 0){
				$sReturn .= ' '.self::$a_Config[self::$sCurrentLanguage]['Multipliers'][$iMultiplier][0];
				if ((integer) $sChunk > 1 && self::$a_Config[self::$sCurrentLanguage]['Multipliers'][$iMultiplier][1] == self::bDontAppendS){
					$sReturn .= 's'; 
				}
			}
			return $sReturn;
		}
		
		private static function toChunk ($iNumber){
			$sNumber = sprintf('%.0F',$iNumber);
			$iModulus = strlen($sNumber)%3;
			if ($iModulus != 0) {
				$sNumber = str_pad($sNumber,strlen($sNumber)+3-$iModulus,'0',STR_PAD_LEFT); //pad amount with zeroes
			}
			return array_reverse(array_reverse(str_split($sNumber,3)),true);
			
		}
		
		public static function convert ($iNumber, $sLanguage){
			$sLanguage = strtoupper($sLanguage);
			
			if (!isset(self::$a_Config[$sLanguage])){
				throw new Exception('Language undefined');
			}
			
			self::$sCurrentLanguage = $sLanguage;
			
			if (!is_integer($iNumber)){
				if (is_float($iNumber)){ //php threats very large numbers as float
					$iNumber = round($iNumber);
				}
				else {
					throw new Exception('iNumber should be an integer');
				}
			}
			if (-999999999999999 > $iNumber || $iNumber > 999999999999999){
				throw new Exception('iNumber is out of range');
			}
			
			$sReturn = '';
			
			if ($iNumber < 0) {
				$sReturn .= self::$a_Config[self::$sCurrentLanguage]['Minus'].' ';
				$iNumber = abs($iNumber);
			}
			
			if ($iNumber == 0){
				$sReturn .= self::$a_Config[self::$sCurrentLanguage]['Units'][0].' ';
			}
			else {
				$a_sChunks = self::toChunk($iNumber);
				
				foreach ($a_sChunks as $key => $value){
					if ($value != 000){
						$sReturn .= self::chunkToWord ($value, $key).' ';
					}
				}
			}				
			//converts first chars to Upper
			$sReturn [0] = strtoupper($sReturn [0]);
			
			//strips the last space
			$sReturn = substr($sReturn,0,strlen($sReturn) - 1);
			return $sReturn;
			
		}
	}
?>