;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;                                                                             ;;
;;;                Rules to map tokens to words    	                        ;;
;;;                                                                             ;;
;;;  Copyright (c) 2005, Chaitanya Kamisetty <chaitanya@atc.tcs.co.in>          ;;
;;;                                                                             ;;
;;;  This program is a part of festival-te.					;;
;;;  										;;
;;;  festival-te is free software; you can redistribute it and/or modify        ;;
;;;  it under the terms of the GNU General Public License as published by	;;
;;;  the Free Software Foundation; either version 2 of the License, or		;;
;;;  (at your option) any later version.					;;
;;;										;;
;;;  This program is distributed in the hope that it will be useful,		;;
;;;  but WITHOUT ANY WARRANTY; without even the implied warranty of		;;
;;;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the		;;
;;;  GNU General Public License for more details.				;;
;;;										;;
;;;  You should have received a copy of the GNU General Public License		;;
;;;  along with this program; if not, write to the Free Software		;;
;;;  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA  ;;
;;;										;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defvar telugu_dotted_abbr_list
  '(
;;Time
    ("గం" "గంటలు")
;;Designations
    ("డా" "డాక్టర్")
    ("చి" "చిరంజీవి")
    ("సౌ" "సౌభాగ్యవతి")
    ("కు" "కుమారి")
    ;; Morning, Afternoon, Evening, Night
    ("తె" "తెల్లవారుజాము")
    ("ఉ" "ఉదయం")
    ("మ" "మధ్యాహ్నం")    
    ("సా" "సాయంత్రం")    
    ("రా" "రాత్రి")    
;;AM and PM
    ("పూ" "పూర్వాహ్న")    
    ("అ" "అపరాహ్న") 
;;Miscellaneous
    ("ని" "నిమిషాలు")
    ("గం" "గంటలు")
))

(defvar telugu_abbr_list
  '(
;;Weekdays (as defined by the POSIX te_IN locale
    ("ఆది" "ఆదివారం")
    ("సోమ" "సోమవారం")    
    ("మంగళ" "మగళవారం")    
    ("బుధ" "బుధవారం")    
    ("గురు" "గురువారం")    
    ("శుక్ర" "శుక్రవారం")    
    ("శని" "శనివారం")    
    ("1/2"  "సగం")
    ("1/4" "పావు")
))

(defvar telugu_denominations_list
  '(
   ("బిలియన్లు") ("బిలియన్ల")  ("బిలియన్")   
  ("మిలియన్లు")   ("మిలియన్ల")  ("మిలియన్")
   ("కోట్లు")   ("కోట్ల")     ("కోటి")
    ("లక్షలు")     ("లక్షల")     ("లక్ష")
    ("వేలు")     ("వేల")     ("వెయ్యి")
     ("వందలు")     ("వందల")     ("వంద")
))

(defvar telugu_common_symbols_table
  '(
    ("#" "హ్యాష్")
    ("$" "డాలర్")
    ("%" "శాతం")
    ("&" "మరియూ")
    ("*" "ఇంటు")
    ("+" "ప్లస్")
    ("," "కామ")
    ("-" "మైనస్")
    ("." "")
    ("/" "బై")
    (":" "కోలన్")
    (";" "")
    ("<" ("లెస్" "ద్యాన్"))
    ("=" ("ఈక్వల్" "టు"))
    (">" ("గ్రేటర్" "ద్యాన్"))
    ("?" "")
    ("@" "ఎట్")
    ("^" "క్యారట్")
    ("_" ("అండర్" "స్కోర్"))
    ("`" ("బ్యాక్" "కోట్"))
    ("~" "టిల్డా")
    ("©" "కాపీరైట్") ;copyright sign
    ("®" "రెజిస్టర్డ్") ;registered sign
    ("£" "పౌండ్")
    ("€" "యూరో")
    ("¥" "యెన్")
    ("¢" "సెంట్")
    ("।" "")	;Devanagari Danda
    ("॥" "")	;Devanagari Double Danda
    ;;ensure digits are always at the end of the table
    ;;so that they will be used as separators after other symbols have been considered
    ("0" "సున్నా")
    ("1" "ఒకటి")
    ("2" "రెండు")
    ("3" "మూడు")
    ("4" "నాలుగు")
    ("5" "ఐదు")
    ("6" "ఆరు")
    ("7" "ఏడు")
    ("8" "ఎనిమిది")
    ("9" "తొమ్మిది")
))

(defvar telugu_supplementary_char_list
  '("£" "€" "¥" "©" "®" "¢" "।" "॥"))

(defvar telugu_currency_list
  '( "$" "£" "€" "¥"))

(defvar telugu_abbr_markers_table
   '( ("." "") ("।" "") ("॥" "") ))
 
(defvar telugu_numbers_table
  '(
    ("౦" "0")
    ("౧" "1")
    ("౨" "2")
    ("౩" "3")
    ("౪" "4")
    ("౫" "5")
    ("౬" "6")
    ("౭" "7")
    ("౮" "8")
    ("౯" "9")
))


(define (telugu_token_to_words token name)
(flatten (telugu_token_to_words_ token name))
)

(defvar ascii_char_hash (cons-array 256));
(defvar defined_ascii_char_hash)
(define (initialize_ascii_char_hash)
(let ((char 0))
      (while (<= char 255)
        (hset ascii_char_hash (string-append (format nil "%c" char)) char)
        (set! char (+ char 1)))
(set! defined_ascii_char_hash t)
))

(define (ascii_of char)
(if (not defined_ascii_char_hash) (initialize_ascii_char_hash))
      (href ascii_char_hash char)
)

(define (telugu_match_string_start_with_list input_string string_list)
  ;;checks is any of the strings in string_list match the start of input_string
  ;;returns the matched string from string_list
  (let (substr (matched_substr ""))
  (while (and (not (null? string_list)) (string-equal matched_substr ""))
  	(set! substr (car string_list))
	(set! string_list (cdr string_list))
	(if (string-equal substr "$") 
	  	(set! match_string (string-append "\\" substr ".*"))
	  	(set! match_string (string-append substr ".*")))
  	(if (string-matches input_string match_string) (set! matched_substr substr)))
  matched_substr
)) 

(define (telugu_string_cleanup input_string)
  ;;ensures the string has characters with are either in the telugu unicode range,
  ;;ascii punctuation or represent a currency symbol
(let (curr_char next_char next_next_char (clean_string "") matched_string)
  ;;convert telugu unicode digits to ascii
  (while (and (set! matched_string (telugu_match_string_with_table input_string telugu_numbers_table))
	      (not (string-equal matched_string "")))
	(set! input_string (string-append (string-before input_string matched_string)
  	    (car (telugu_table_lookup matched_string telugu_numbers_table)) (string-after input_string matched_string))))
    (while (not (equal? input_string ""))
	(set! curr_char (ascii_of (substring input_string 0 1)))
	(set! next_char (ascii_of (substring input_string 1 1)))
	(set! next_next_char (ascii_of (substring input_string 2 1)))
	(cond
      	     ;;basic ascii
	     ((or (or (and (>= curr_char 33) (<= curr_char 64)) (and (>= curr_char 91) (<= curr_char 96)))
		  (and (>= curr_char 123) (<= curr_char 126)))
	          (set! num_chars_valid 1))
	     ;;telugu unicode block (of the form \340 {\260 \261} [\200 .. \277]) 
	     ((and (equal? curr_char 224)
		           (or (equal? next_char 176) (equal? next_char 177))
		          (and (>= next_next_char 128) (<= next_next_char 191)))
	          	  (set! num_chars_valid 3))
	     ;;Supplementary chars like yen, pound, cent, copyright, registered mark, Euro
	     ((not (string-equal (set! matched_string 
		   (telugu_match_string_start_with_list input_string telugu_supplementary_char_list)) ""))
  	           (set! num_chars_valid (string-length matched_string)))
	     ;;does not match anything, throw away the char
	     (t (set! num_chars_valid 0))
    	)
	(set! clean_string (string-append clean_string (substring input_string 0 num_chars_valid)))
	;;does not match anything, throw away the char
        (if (equal? num_chars_valid 0) (set! num_chars_valid 1))
	(set! input_string (substring input_string num_chars_valid (- (string-length input_string) num_chars_valid))))
    clean_string
))

(define (telugu_match_string_with_table input_string string_table)
  ;;checks is any of the strings in string_table match a substring of input_string
  ;;returns the matched string from string_table
  (let (substr (matched_substr ""))
  (while (and (not (null? string_table)) (string-equal matched_substr ""))
  	(set! substr (car (car string_table)))
	(set! string_table (cdr string_table))
	(if (or (string-equal substr "$") (string-equal substr "^") (string-equal substr "?")
		(string-equal substr "*") (string-equal substr "+") (string-equal substr "."))
	  	(set! match_string (string-append ".*\\" substr ".*"))
	  	(set! match_string (string-append ".*" substr ".*")))
  	(if (string-matches input_string match_string) (set! matched_substr substr)))
  matched_substr
)) 

(define (telugu_token_to_words_ token name)
  (set! name (telugu_string_cleanup name))
  (let ((number_regex "[0-9,]+\\(\\.[0-9]+\\)?") matched_substr matched_start_string prev_token_matched_start_string
						 currency_name time_segments date_segments)
  (set! matched_start_string (telugu_match_string_start_with_list name telugu_currency_list))
  (cond
    ;;currencies
   ((and (not (string-equal matched_start_string ""))
	 (string-matches (string-after name matched_start_string) number_regex))
      (set! currency_name (string-append (car (telugu_table_lookup matched_start_string telugu_common_symbols_table)) "లు"))
      (if (telugu_list_lookup (item.feat token "n.name") telugu_denominations_list)
	  (telugu_token_to_words_ token (substring name 1  (- (string-length name) 1)))
	  (list (telugu_token_to_words_ token (substring name 1  (- (string-length name) 1))) currency_name)))
   ((and (not (string-equal (set! prev_token_matched_start_string (telugu_match_string_start_with_list 
			(item.feat token "p.name") telugu_currency_list)) ""))
       (string-matches (string-after (item.feat token "p.name") prev_token_matched_start_string) number_regex)
       (telugu_list_lookup name telugu_denominations_list)) 
           (list name (string-append 
			(car (telugu_table_lookup prev_token_matched_start_string telugu_common_symbols_table)) "లు")))
   ;;rupees
   ((string-matches name (string-append "రూ\\.[" number_regex))
        (if (telugu_list_lookup (item.feat token "n.name") telugu_denominations_list)
	  (telugu_token_to_words_ token (string-after name "రూ."))
	  (list (telugu_token_to_words_ token (string-after name "రూ.")) "రూపాయలు")))
    ((and (string-matches (item.feat token "p.name") (string-append "రూ\\.[" number_regex))
	          (telugu_list_lookup name telugu_denominations_list) ) (list name "రూపాయలు"))
   ;;cents
   ((string-matches name (string-append number_regex "¢"))
        (list (telugu_token_to_words_ token (string-before name "¢")) "సెంట్లు"))
    ((string-matches name "[0-9]+") (telugu_number_to_words name))
    ((string-matches name "[0-9]*\\.[0-9]+") 
        (list (telugu_number_to_words (string-before name ".")) '("పాయన్ట్") 
		    (mapcar telugu_number_to_words (mapcar string-append (symbolexplode (string-after name "."))))))
    ((string-matches name "\\([0-9]+,[0-9]*\\)+\\(\.[0-9]+\\)?") 
		     (telugu_token_to_words_ token (telugu_removechar name ","))) ;dd,dd,ddd.dd form
   ((string-matches name (string-append "-" number_regex)) (list '("మైనస్") 
							    (telugu_token_to_words_ token (string-after name "-"))))
   ((string-matches name (string-append "\\+" number_regex)) (list '("ప్లస్") 
							    (telugu_token_to_words_ token (string-after name "+")))) 
    ;;line of characters
    ((string-matches name "_____+") (list '"అండర్" "స్కోర్లతో" "గీత"))
    ((string-matches name "=====+") (list "ఈక్వల్" "టు"  "లతో" "గీత"))
    ((string-matches name "-----+") (list "హైఫన్" "లతో" "గీత"))
    ((string-matches name "\\*\\*\\*\\*\\*+") (list "line" "of" "asterisks"))
    ;;time and date
    ((set! time_segments (telugu_string_matches_time name)) (mapcar telugu_token_to_words_ 
						  	  (list token token token) (mapcar string-append time_segments)))
    ((set! date_segments (telugu_string_matches_date name)) (mapcar telugu_token_to_words_ 
						  	  (list token token token) (mapcar string-append date_segments)))
    ;;abbreviations
    ;;dotted abbreviations when followed by space ex: "dr. hello"
    ((and (telugu_table_lookup name telugu_dotted_abbr_list)  (string-equal (item.feat token "punc") "."))
		(telugu_table_lookup name telugu_dotted_abbr_list))
    ;;abbreviation follwed by markers .,:,devanagari danda, double danda 
    ((and (set! matched_substr (telugu_match_string_with_table name telugu_abbr_markers_table))
	  (not (string-equal matched_substr ""))
     	  (telugu_table_lookup (string-before name matched_substr) telugu_dotted_abbr_list))
       	     (list (telugu_table_lookup (string-before name matched_substr) telugu_dotted_abbr_list)
	           (telugu_token_to_words_ token (string-after name matched_substr))))   
    ;;abbreviations not followed by .
    ((telugu_table_lookup name telugu_abbr_list) (telugu_table_lookup name telugu_abbr_list))
    ;;special characters
    ((string-matches name (string-append number_regex "%")) 
     		(list (telugu_token_to_words_ token (string-before name "%")) '("సాతం")))
    ;;separators and connectors {#,$,%,&,*,+,-,/,<,=,>,@,\,^,_,`,copyright,registered trademark} 
    ((and (set! matched_substr (telugu_match_string_with_table name telugu_common_symbols_table) )
	  (not (string-equal matched_substr "")))
     	(list (telugu_token_to_words_ token (string-before name matched_substr))
	      (telugu_table_lookup matched_substr telugu_common_symbols_table)
	      (telugu_token_to_words_ token (string-after name matched_substr))))   
    (t (if (lts.in.alphabet (telugu_string_cleanup name) 'telugu) (list (telugu_string_cleanup name)) (list '(""))))
)))

(define (telugu_string_matches_time input_string)
  ;;returns input string is in HH:MM(:SS) format
  (let ((hrs (parse-number (string-before input_string ":"))) mins secs)
  (cond 
    ((not (string-matches input_string "[0-9][0-9]?:[0-9][0-9]\\(:[0-9][0-9]\\)?")) nil)
    (t
    (set! input_string (string-after input_string ":"))
    (set! mins (string-before input_string ":"))
    (if (string-equal mins "") (set! mins (parse-number input_string)) (set! mins (parse-number mins)))
    (set! secs (string-after input_string ":"))

    ;;checking for HH,MM,SS to be in valid 24 hour format
    (if (and (>= hrs 0) (<= hrs 23) (>= mins 0) (<= mins 59) (>= (parse-number secs) 0) (<= (parse-number secs) 59))
      		(list hrs mins secs) nil)
    )
)))

(defvar telugu_no_of_days_in_month
  '( (1 31) (2 29)(3 31) (4 30)
     (5 31) (6 31) (7 31) (8 31)
     (9 30) (10 31) (11 30) (12 31)))

(define (telugu_string_matches_date input_string)
  ;;returns true is the input string is in DD/MM(/YY/YYYY) format,
  (let ((date_segment1 (parse-number (string-before input_string "/"))) date_segment2 date_segment3 days_in_month)
  (cond
    ((not (string-matches input_string "[0-9][0-9]?/[0-9][0-9]?\\(/[0-9][0-9]\\([0-9][0-9]\\)?\\)?")) nil)
    (t
    (set! input_string (string-after input_string "/"))
    (set! date_segment2 (string-before input_string "/"))
    (if (string-equal date_segment2 "") (set! date_segment2 (parse-number input_string))
      					(set! date_segment2 (parse-number date_segment2)))
    (set! date_segment3 (string-after input_string "/"))
    ;;checking for DD,MM to be in valid 
    (set! days_in_month (car (telugu_table_lookup date_segment2 telugu_no_of_days_in_month)))
    (if (and days_in_month (> date_segment1 0) (<= date_segment1 days_in_month)) 
      				(list date_segment1 date_segment2 date_segment3) nil))
)))

(define (telugu_number_to_words number)
  (let (input_string)
    (flatten (telugu_number_to_words_rec input_string number))
))

(define (telugu_number_to_words_rec token_list name)
  (set! name (telugu_strip_leading_zeros name)) ;remove leading zeros
  (let ((number_length (string-length name)))
  (cond
    ((>= number_length 8)
     (if (string-equal name "10000000") (append token_list '("కోటి"))
       (if (string-matches name "[0-9]+0000000") ; 2 crores, 20 crores 
	 (append (telugu_number_to_words_rec token_list (substring name 0 (- number_length 7))) '("కోట్ల")) ;or kootlu
	 (if (string-matches name "1[0-9][0-9][0-9][0-9][0-9][0-9][0-9]") ;1 followed by other digits
	     (append  token_list '("ఒక" "కోటి") (telugu_number_to_words_rec token_list (substring name (- number_length 7) 7)))
	     (append (telugu_number_to_words_rec token_list (substring name 0 (- number_length 7))) '("కోట్ల") 
	           (telugu_number_to_words_rec token_list (substring name (- number_length 7) 7)))))))
    ((and (<= number_length 7) (>= number_length 6))
     (if (string-equal name "100000") (append token_list '("లక్ష"))
       (if (string-matches name "[0-9]+00000") 
	 	(append token_list (telugu_two_digit_number_to_words 
				     (substring name 0 (- number_length 5))) '("లక్షల"));or laskalu 
		(if (string-matches name "1[0-9][0-9][0-9][0-9][0-9]")
        	   (telugu_number_to_words_rec (append token_list '("ఒక" "లక్ష")) (substring name (- number_length 5) 5))
	           (telugu_number_to_words_rec (append token_list (telugu_two_digit_number_to_words 
			(substring name 0 (- number_length 5))) '("లక్షల")) (substring name (- number_length 5) 5))))))
    ((and (<= number_length 5) (>= number_length 4))
     (if (string-equal name "1000") (append token_list '("వెయ్యి"))
       (if (string-matches name "[0-9]+000") 
	 	(append token_list (telugu_two_digit_number_to_words (substring name 0 (- number_length 3))) '("వేలు"))
     	 (if (string-matches name "[0-9][1-9]00") ;4 digit numbers like 1100, 1200, 2300 
	   	(append token_list (telugu_two_digit_number_to_words 
				     (substring name 0 (- number_length 2))) '("వందల"));or vandala
		(if (string-matches name "1[0-9][0-9][0-9]")
                   (telugu_number_to_words_rec (append (telugu_two_digit_number_to_words (substring name 0 2))
														token_list '("వందల"))  (substring name 2 2))
	           (telugu_number_to_words_rec (append token_list (telugu_two_digit_number_to_words 
			(substring name 0 (- number_length 3))) '("వేల")) (substring name (- number_length 3) 3)))))))
    ((eq number_length 3) 
     (if (string-equal name "100") (append token_list '("వంద"))	;only 100
        (if (string-matches name "[2-9]00") ;200,300 ...
	   (append token_list (telugu_two_digit_number_to_words 
				(substring name 0 (- number_length 2))) '("వందల"));or vandala
           (if (string-matches name "1[0-9][0-9]") (append token_list '("నూట")	;101-199
					        	(telugu_two_digit_number_to_words (substring name 1 2)))
	    (telugu_number_to_words_rec (append token_list (telugu_two_digit_number_to_words ;remaining numbers
			(substring name 0 (- number_length 2))) '("వందల")) (substring name (- number_length 2) 2))))))
    ((<= number_length 2) (append token_list (telugu_two_digit_number_to_words name)))
)))

(defvar telugu_basic_number_table
  '(
    ("0" "సున్నా")
    ("1" "ఒకటి")
    ("2" "రెండు")
    ("3" "మూడు")
    ("4" "నాలుగు")
    ("5" "ఐదు")
    ("6" "ఆరు")
    ("7" "ఏడు")
    ("8" "ఎనిమిది")
    ("9" "తొమ్మిది")
))

(defvar telugu_two_digit_numbers_table
  '(
    ("10" "పది")
    ("11" "పదకొండు")
    ("12" "పన్నెండు")
    ("13" "పదమూడు")
    ("14" "పద్నాలుగు")
    ("15" "పదహైదు")
    ("16" "పదహారు")
    ("17" "పదిహేడు")
    ("18" "పద్యెనిమిది")
    ("19" "పంతొమ్మిది")
    ("2" "ఇరవై")
    ("3" "ముప్పై")
    ("4" "నలభై")
    ("5" "యాభై")
    ("6" "అరవై")
    ("7" "డెబ్భై")
    ("8" "ఎనభై")
    ("9" "తొంభై")
))

(define (telugu_list_lookup abbr lst)
 (assoc_string abbr lst))

(define (telugu_table_lookup abbr table)
  (cdr (assoc_string abbr table)))

(define (telugu_two_digit_number_to_words name)
  ;number to words for 0-99
  (let (lst units tens) 
    (set! lst (reverse (symbolexplode name)))
    (set! units (car lst))
    (set! tens (car (cdr lst)))
    ;compress the units digit if it is 0
    (if (and (string-equal units "0") (not (or (eq tens nil) (string-equal tens "1")))) (set! units "")) 
    ;remove leading zero
    (if (string-equal tens "0") (set! tens "")) 
    (if (string-equal tens "1") (telugu_table_lookup name telugu_two_digit_numbers_table)
        (cons (telugu_table_lookup tens telugu_two_digit_numbers_table) (telugu_table_lookup units telugu_basic_number_table))
)))

(define (telugu_strip_leading_zeros number)
  ;removes leading zeros for a number, if single zero, leave it as it is
  (if (string-matches number "0+") (set! number "0")
       (while (string-matches number "0[0-9]*") (set! number (string-after number "0"))))
  number
)

(define (telugu_removechar input_string char)
  ;removes all occurences of char from input_string
  (let ((has_matched 1) match_string)
  (if (string-equal char "\\") (set! match_string (string-append ".*\\\\.*"))
      				 (set! match_string (string-append ".*" char ".*")))
  (while has_matched
    (if (string-matches input_string match_string)
	 (set! input_string (string-append (string-before input_string char) (string-after input_string char)))
	 (set! has_matched nil)))
    input_string))

(provide 'telugu_token)
