//	FindinSite-CD-JS Rules English
//	Copyright (C) 2013 PHD Computer Consultants Ltd
//	v7.003 25 November 2013
//
//	Permutations allowed in rules fairly limited

/*global fisLogging */
/*jslint continue: true, plusplus: true, todo: true, white: true */

var findinsite_rules_english = function () {
	"use strict";

	// PRIVATE variables and functions - some made public at end

	var parsedRules = false;
	var givenLength;
	var addWord;

	/////////////////////////////////////
	function log(msg) {
		if (typeof fisLogging !== 'undefined') {
			if (fisLogging && window.console) {
				window.console.log(msg);
			}
		}
	}

	/////////////////////////////////////

	var blocks = [
		"the"
	];

	var synonyms = [
	['color', 'colour'],
	['licence', 'license'],
	['language', 'langauge'],
	['a', 'an'],
	['his', 'her', 'their'],
	['affect', 'effect'],
	['francais', 'français']
	];

	var corrections = [
	['recieve', 'receive'],
	['teh', 'the'],
	['neccesary', 'necessary'],
	['recieve', 'receive']
	];

	var rules = [
	['*s', '*'],
	['*er', '*'],
	//['*ers', '*'],
	['*ed', '*'],
	['*ing', '*'],
	['*eer', '*'],
	['*ier', '*'],
	['*ly', '*'],
	['*ion', '*'],
	['*ise', '*'],
	['*ize', '*'],

	['*er', '*e'],
	['*ed', '*e'],
	['*ion', '*e'],

	['*##ing', '*#'],
	['*##er', '*#'],
	['*##ed', '*#'],

	['*ise', '*ize'],
	['*ize', '*ise'],
	['*or', '*er'],
	['*er', '*or'],
	['*our', '*or'],
	['*or', '*our'],
	['*y', '*ies'],
	['*able', '*ible'],
	['*ible', '*able'],
	['*ance', '*ence'],
	['*ence', '*ance'],
	['*g', '*gue'],
	['*gue', '*g'],

	['*', '*s', '*es'],
	['*#', '*#e', '*#er', '*#ers', '*#ed', '*#ing', '*#eer', '*#ier', '*#ly', '*#ise', '*#ize', '*#ion'],
	['*e', '*er', '*ers', '*ed', '*ing', '*ion'],
	['*#', '*##er', '*##ers', '*##ed', '*##ing']
	];

	/////////////////////////////////////

	function isUnicodeVowel(lch) {
		return lch === 'a' || lch === 'e' || lch === 'i' || lch === 'o' || lch === 'u';
	}
	function isUnicodeConsonant(lch) {
		var lchcode = lch.charCodeAt(0);
		if (lchcode < 0x61 || lchcode > 0x7a) { return false; } // a..z
		return !isUnicodeVowel(lch);
	}

	/////////////////////////////////////

	function generate_synonyms_or_correction(lcword, word_list, morphs, isCorrection) {
		var ix, jx, kx, morph, maxjx;
		for (ix = 0; ix < morphs.length; ix++) {
			morph = morphs[ix];
			maxjx = morph.length;
			if (isCorrection) { maxjx = 1; }
			for (jx = 0; jx < maxjx; jx++) {
				if (morph[jx] === lcword) {
					for (kx = 0; kx < morph.length; kx++) {
						if (kx === jx) { continue; }
						addWord(word_list, morph[kx]);
					}
					break;
				}
			}
		}
	}

	/////////////////////////////////////

	// Either:	str is set
	// Or:			str false then asterisk followed by consonentCount
	var WildItem = function (str, consonentCount) {
		//log(wildType + " - " + str + " - "+consonentCount);
		if (consonentCount > 2) { throw "consonentCount>2"; }
		this.str = str;
		this.consonentCount = consonentCount;
	};

	/////////////////////////////////////

	var Wildie = function (spec) {
		var wildItems, str, ix, jx, ch, consonentCount, nextch;
		this.spec = spec;
		wildItems = [];
		str = '';
		for (ix = 0; ix < spec.length; ix++) {
			ch = spec.charAt(ix);

			if (ch === '*') {
				if (str.length > 0) {
					wildItems.push(new WildItem(str, 0));
					str = '';
				}
				consonentCount = 0;
				for (jx = ix + 1; jx < spec.length; jx++) {
					nextch = spec.charAt(jx);
					if (nextch === '#') {
						consonentCount++;
					}
					else { break; }
				}
				ix = jx - 1;
				wildItems.push(new WildItem(false, consonentCount));
			}
			else {
				str += ch;
			}
		}
		if (str.length > 0) {
			wildItems.push(new WildItem(str, 0));
		}
		if (wildItems.length > 2) { throw "WildItem: too many elements"; }
		this.wildItems = wildItems;
	};

	/////////////////////////////////////
	function parseTheRules() {
		//log("parseTheRules");
		var ix, jx, rule, raw_rule;
		parsedRules = [];

		for (ix = 0; ix < rules.length; ix++) {
			raw_rule = rules[ix];
			rule = [];
			for (jx = 0; jx < raw_rule.length; jx++) {
				rule.push(new Wildie(raw_rule[jx]));
			}
			parsedRules.push(rule);
		}

		/*
		var kx, wi, wildie;
		for (ix = 0; ix < parsedRules.length; ix++) {
			rule = parsedRules[ix];
			var rulesummary = "";
			for (jx = 0; jx < rule.length; jx++) {
				wildie = rule[jx];
				rulesummary += wildie.spec + ":";
				for (kx = 0; kx < wildie.wildItems.length; kx++) {
					wi = wildie.wildItems[kx];
					rulesummary += "<" + wi.str + ":" + wi.consonentCount + ">";
				}
				rulesummary += " ||| ";
			}
			log(rulesummary);
		}*/
	}

	/////////////////////////////////////
	addWord = function (word_list, lcword) {
		var wx;
		if ((lcword.length <= 1) || (lcword.length < (givenLength - 4))) { return; }
		for (wx = 0; wx < word_list.length; wx++) {
			if (lcword === word_list[wx]) {
				return;
			}
		}
		//log("addWord: " + lcword);
		word_list.push(lcword);
	};

	/////////////////////////////////////
	function runRules(lcword, word_list) {
		var len, ix, jx, rule, matches, wildie0, wildie0_0, lcword2, wildie0_1, w1_1str, w1_1strlen, lcwordend, len2, lastch, lcword3, wildie, wildieN_0, wildieN_1;
		len = lcword.length;
		for (ix = 0; ix < parsedRules.length; ix++) {
			rule = parsedRules[ix];

			// Look for match in first wildie
			matches = false;
			wildie0 = rule[0];
			wildie0_0 = wildie0.wildItems[0];
			//log(ix + " wildie0.spec: " + wildie0.spec);

			lcword2 = lcword;
			// If word ending given, match that first
			if (wildie0.wildItems.length === 2) {	// Just *s, *##ing, etc
				wildie0_1 = wildie0.wildItems[1];
				w1_1str = wildie0_1.str;
				if (!w1_1str) { throw "unexpected 1_1 !str"; }
				w1_1strlen = w1_1str.length;
				lcword2 = false;
				if (len >= w1_1strlen) {
					lcwordend = lcword.substr(len - w1_1strlen, len);
					if (lcwordend === w1_1str) {
						lcword2 = lcword.substr(0, len - w1_1strlen);
						//log("SHORT " + lcword2);
					}
				}
			}

			if (lcword2) {
				len2 = lcword2.length;
				//log("XXX: " + lcword2);
				if (wildie0_0.str) { throw "unexpected 1_0 str"; }
				if (wildie0_0.consonentCount === 0) {
					matches = true;
				}
				if (wildie0_0.consonentCount > 0) {
					if (wildie0_0.consonentCount <= len2) {
						lastch = lcword2.charAt(len2 - 1);
						if (isUnicodeConsonant(lastch)) {
							if ((wildie0_0.consonentCount === 1) || (lcword2.charAt(len2 - 2) === lastch)) {
								matches = true;
							}
						}
					}
				}
			}

			if (matches) {
				//log("MATCH!");
				for (jx = 1; jx < rule.length; jx++) {
					wildie = rule[jx];
					if (wildie.wildItems.length === 1) {
						//log("ADD1 " + lcword2);
						if (wildie0_0.consonentCount > wildie.wildItems[0].consonentCount) {
							lcword3 = lcword2.substr(0, lcword2.length + wildie.wildItems[0].consonentCount - wildie0_0.consonentCount);
							addWord(word_list, lcword3);
							//log("PUSH1 " + lcword3);
						}
						else {
							addWord(word_list, lcword2);
							//log("PUSH2 " + lcword2);
						}
					}
					//['*##ing', '*#'],
					//['*', '*s', '*es'],
					//['*#', '*##er', '*##ers', '*##ed', '*##ing']
					lcword3 = lcword2;
					//log("wildie0_0.consonentCount " + wildie0_0.consonentCount);
					wildieN_0 = wildie.wildItems[0];
					//log("wildieN_0.consonentCount " + wildieN_0.consonentCount);
					if (wildie0_0.consonentCount > wildieN_0.consonentCount) {
						lcword3 = lcword3.substr(0, lcword3.length + wildieN_0.consonentCount - wildie0_0.consonentCount);
					}
					else if (wildie0_0.consonentCount < wildieN_0.consonentCount) {
						lcword3 += lcword3.charAt(lcword3.length - 1);
					}

					if (wildie.wildItems.length > 1) {
						wildieN_1 = wildie.wildItems[1];
						lcword3 += wildieN_1.str;
					}
					addWord(word_list, lcword3);
					//log("ADD2 " + lcword3);
				}
			}
		}
	}

	/////////////////////////////////////

	function generate(lcword, word_list) {
		var ix, wx, len, lcWordN;
		//log("generate " + lcword);
		givenLength = lcword.length;
		if (givenLength <= 1) { return; }

		for (ix = 0; ix < blocks.length; ix++) {
			if (lcword === blocks[ix]) { return; }
		}

		// Generate synonyms
		generate_synonyms_or_correction(lcword, word_list, synonyms, false);
		// Generate corrections
		generate_synonyms_or_correction(lcword, word_list, corrections, true);

		// Load rules
		if (!parsedRules) { parseTheRules(); }

		runRules(lcword, word_list);

		len = lcword.length;
		for (wx = 1; wx < word_list.length; wx++) {
			lcWordN = word_list[wx];
			if (lcWordN.length < len) {
				runRules(lcWordN, word_list);
			}
		}
	}


	/////////////////////////////////////
	// PUBLIC methods
	return {
		generate: generate
	};
	/////////////////////////////////////
} ();