//////////////////////////////////////////////////////////////////////
//												//
//				Transliterator Component					//
//												//
//////////////////////////////////////////////////////////////////////

/*
*@author Aleksandr Burnazyan
*/
window.Transliterator = function() {	
	//Public
	this.addLanguage = function(name, transliterationMap) {
		LANGUAGES[name] = transliterationMap;
	}

	this.removeLanguage = function(name) {
		delete LANGUAGES[name];
	}	
	
	this.getLanguages = function() {
		return LANGUAGES;
	}
	
	this.getLanguage = function(langName) {
		return LANGUAGES[langName];	
	}
	
	this.getLanguageTransliterationMap = function(langName) {
		var lang = this.getLanguage(langName);
		
		return lang ? lang.translitMap : null;
	}

	this.getLanguageRulesFunc = function(langName) {
		var lang = this.getLanguage(langName);
		
		return lang ? lang.applyTranslitRules : null;	
	}	
	
	this.transliterate = function(word, lang, limit) {
		if(!limit) {
			limit = -1;
		}	
	
		var translitArray;	
		
		var language = this.getLanguage(lang);
		
		if(!language) {
			throw new Error("ERROR: Unrecongnized language: " + lang);
		}		
		
		var translitCharsArray = getTranslitCharsArrayForWord(word, language);
		translitArray = getAllTransliterations(translitCharsArray, limit)
		
		return translitArray;
	}
	
	//Private	
	var LANGUAGES = {};

	var getTranslitCharsArrayForWord = function(word, language) {
		var result = [];
		var translitMap = language.translitMap;
		word = word.toLowerCase();
		
		var currChar;
		for(var i=0; i<word.length; i++) {
			currChar = word.charAt(i);
			
			if(language.applyTranslitRules) {
				var retVal = language.applyTranslitRules(word, i);
				
				if(retVal && retVal.latinChar) {
					result.push(retVal.latinChar);
					
					if(retVal.moveCursor && !isNaN(parseInt(retVal.moveCursor))) {
						i += retVal.moveCursor;
					}
					
					continue;
				}
			}	
			
			if(translitMap[currChar]) {
				result.push(translitMap[currChar]);
			} else if(!isNaN(parseInt(currChar))) { //Checking wether the character is a digit
				result.push(currChar);
			} else if(currChar == "-" && i != 0 && i != word.length -1) {//Domain names can also contain hyphens, though they can't start or end with a hyphen.
				result.push(currChar);
			}
		}
		
		return result;	
	}
	
	/*
	* @ translitCharsArray {Array} 
	* @limit Maximum count of results to be returned. To get all results pass -1.
	* Recursively generates all possible transliterations for the word based on given translitCharsArray
	*/
	var getAllTransliterations = function(translitCharsArray, limit) {
		var getAllTransliterationsRec = function(translitCharsArray, recursionLevel) {
		    if(translitCharsArray.length == 0) {
		        return []; 
		    }

			var firstLetter = translitCharsArray[0];
			var restPart = translitCharsArray.slice(1);

			if(!Util.isArray(firstLetter)) {
			     firstLetter = [firstLetter]
			}   

			var allResults = [], currResults;
			for(var i=0; i<firstLetter.length; i++) {
				 currResults = getAllTransliterationsRec(restPart, recursionLevel+1);    

				if(currResults.length == 0) {
					if(recursionLevel == 0 && limit != -1 && allResults.length == limit) {
						return allResults;
					}

					allResults.push(firstLetter[i]);
				} else {
					for(var j=0; j<currResults.length; j++) {
						if(recursionLevel == 0 && limit != -1 && allResults.length == limit) {
							return allResults;
						}

						allResults.push(firstLetter[i]+currResults[j])
					}
				}
			}

			return allResults;	
		}
		
		return getAllTransliterationsRec(translitCharsArray, 0);
	}
}

//TODO: this class must be in separate file... keeping it here while it is still small.. |aburnazy 16.08.08
window.Util = {    
	/**
	* Checks "Arrayness" of an object
	*/
	isArray : function(o){
		var defined = function(o){
			return typeof(o) != "undefined";
		}	
		/*
		 * If these conditions aren't met, it certainly isn't an Array
		 */
		if (o == null || typeof(o) != "object" || typeof(o.length) != "number") {
			return false;
		}
		
		/*
		 * Check to see if the object is an instance of the window's Array object
		 */
		if (defined(Array) && defined(o.constructor) && o.constructor == Array) {
			return true;
		}
		
		/*
		 * It might be an array defined from another window object -
		 * check to see if it has an Array's methods
		 */
		if (typeof(o.join) == "function" && typeof(o.sort) == "function" &&
			typeof(o.reverse) == "function") {
				return true;
		}
		
		/*
		 * As a last resort, let's see if index [0] is defined
		 */
		return (o.length == 0 || defined(o[0]));
	}
}
