/*	kanji_base.js
	copyleft Thomas Baspeyras 2005

requires :
	dom.js
	js_lib.js
	unicode.js

index :
	KanjiBase()
		add(index, character)
		addKanjiRange(id, onchange)
		ex()
		indexInRange(index)
		inRange(word)
		randomKanji()
		randomOnReading()
		randomKanjiWord()
		randomKanaWord()
		randomTranslation()
*/

function KanjiBase() {
/*	prototype of the kanji base
	prototype de la base des kanji
*/
	/*	allIndex stores kanji according to handbook index, allKanji according to character
		allIndex contient les kanji selon l'index du manuel, allKanji selon le caractère
	*/
	var allIndexes = new Array;
	var allKanji = new Array;

	function Kanji(index, character) {
		this.index = index;
		this.character = character;
		this.onReadings = new Array;
		this.kanjiWords = new Array;
	}

	var allOnReadings = new Array;
	var allKanjiWords = new Array;
	var allKanaWords = new Array;
	var allTranslations = new Array;
	/*	javascript arrays have a 'sort' method which collides with french word 'sort' (fate) in file kanji_390_584.js
		les tableaux javascript ont une méthode appelée "sort" (trier) en conflit avec le mot "sort" dans le fichier kanji_390_584.js
	*/
	allTranslations.sort = null;

	function OnReading(reading) {
		this.reading = reading;
		this.indexes = new Array;
		this.kanjis = new Array;
	}

	function KanjiWord(writing) {
		this.writing = writing;
		this.indexes = new Array;
		this.kanaWords = new Array;
		this.translations = new Array;
	}

	function KanaWord(writing) {
		this.writing = writing;
		this.indexes = new Array;
		this.kanjiWords = new Array;
		this.translations = new Array;
	}

	function Translation(writing) {
		this.writing = writing;
		this.indexes = new Array;
		this.kanjiWords = new Array;
		this.kanaWords = new Array;
	}

	/*	first and last kanji available in the base
		this is intended to split the 1945 jouyou kanji in ten files rather than one big file

		le premier et le dernier kanji disponible dans la base
		ceci à pour but de répartir les 1945 jouyou kanji en dix fichiers plutôt qu'un seul gros
	*/
	var firstIndex = 1945;
	var lastIndex = 1;

	/*	some operations apply to current index and current kanji
		certaines operations s'appliquent à l'index et au kanji courant
	*/
	var currentIndex;
	var currentKanji;

	this.add = function(index, character) {
	/*	creates a new (non-existing) kanji in the base
		optional arguments are 'on' readings
		créé un nouveau kanji (non-existant) dans la base
		les arguments optionnels sont les lectures "on"
	*/
		if (index < firstIndex) firstIndex = index;
		if (index > lastIndex) lastIndex = index;
		allIndexes[currentIndex = index] = allKanji[character] = currentKanji = new Kanji(index, character);

		if (arguments.length > 2) for (var i = 2; i < arguments.length; i++) {
		/*	optional on readings
			lectures on optionnelles
		*/
			var reading = arguments[i];
			var onReading = allOnReadings[reading];
			if (!onReading) allOnReadings[reading] = onReading = new OnReading(reading);
			onReading.indexes.push(currentIndex);
			onReading.kanjis.push(currentKanji);
			currentKanji.onReadings.push(onReading);
		}
	}

	this.ex = function() {
	/*	adds a new (non-existing) example to current kanji
		arguments are dispatched according to order and occurence of kanji and kana
		first arguments are kanji writings (usually a single one)
		other arguments are prononciations followed by corresponding meanings

		ajoute un nouvel exemple (non-existant) au kanji courant
		les arguments sont répartis selon l'ordre et l'occurence des kanji et des kana
		les premiers arguments sont des écritures en kanji (habituellement une seule)
		les autres arguments sont des prononciations suivies par les traductions correspondantes

		EXAMPLE -> <kanji writing> [<kanji writing>...] ARTICLE [ARTICLE...]
		ARTICLE -> <kana writing> [<kana writing>...] <translation> [<translation>...]
	*/
		var kanjiWords = new Array;
		var i = 0;
		do {
		/*	harvest kanji words
			récolter les mots en kanji
		*/
			var writing = arguments[i];
			var kanjiWord = allKanjiWords[writing];
			if (!kanjiWord) allKanjiWords[writing] = kanjiWord = new KanjiWord(writing);
			kanjiWord.indexes.add(currentIndex);
			kanjiWords.push(kanjiWord);
			currentKanji.kanjiWords.push(kanjiWord);
			i += 1;
		} while (arguments[i].hasKanji());

		var first = true;
		do {
		/*	now processing prononciations and translations
			traitons maintenant les prononciations et les traductions
		*/
			var kanaWords = new Array;
			var translations = new Array;
			do {
			/*	harvest kana words
				récolter les mots en kana
			*/
				var writing = arguments[i];
				var kanaWord = allKanaWords[writing];
				if (!kanaWord) allKanaWords[writing] = kanaWord = new KanaWord(writing);
				kanaWord.indexes.add(currentIndex);
				kanaWords.push(kanaWord);
				i += 1;
			} while (arguments[i].hasKana());
			do {
			/*	harvest translations
				récolter les traductions
			*/
				var writing = arguments[i];
				var translation = allTranslations[writing];
				if (!translation) allTranslations[writing] = translation = new Translation(writing);
				translation.indexes.add(currentIndex);
				translations.push(translation);
				i += 1;
			} while ((i < arguments.length) && (!arguments[i].hasKana()));

			/*	cross-link everything
				tout relier
			*/
			for (var j = 0; j < kanaWords.length; j++) for(var k = 0; k < translations.length; k++) {
				kanaWords[j].translations.add(translations[k]);
				translations[k].kanaWords.add(kanaWords[j]);
			}
			for (var j = 0; j < kanjiWords.length; j++) for(var k = 0; k < kanaWords.length; k++) {
				kanjiWords[j].kanaWords.add(kanaWords[k]);
				kanaWords[k].kanjiWords.add(kanjiWords[j]);
			}
			for (var j = 0; j < kanjiWords.length; j++) for(var k = 0; k < translations.length; k++) {
				kanjiWords[j].translations.add(translations[k]);
				translations[k].kanjiWords.add(kanjiWords[j]);
			}
		} while (i < arguments.length);
	}

	/*	range of kanji to study according to handbook indexation
		plage des kanji à étudier d'après l'indexation du manuel
	*/
	var fromKanjiTextInput;
	var toKanjiTexInput;

	this.addKanjiRange = function(id, onchange) {
	/*	creates text inputs and selectors used to specify a range of kanji among those available
		créé des zones de saisie et les sélecteurs utilisés pour spécifier une plage de kanji parmi ceux disponibles
	*/
		function onChangeTextInput() {
			with (this) {
				if (isNaN(value)) {
					value = select.value;
					return;
				}
				select.value = value = Math.range(limit, value, other.limit);
				if (Math.nxor(1*value > 1*other.value, 1*limit < 1*other.limit)) other.select.value = other.value = value;
			}
			onchange();
		}

		function onChangeSelect() {
			with(this.textInput) {
				value = this.value;
				if (Math.nxor(1*value > 1*other.value, 1*limit < 1*other.limit)) other.select.value = other.value = value;
			}
			onchange();
		}

		function rangeInput(value) {
		/*	create a text input and a kanji selector with proper interactions
			créé une zone de saisie et un sélecteur de kanji avec les interactions appropriées
		*/
			var input = textInput(4, value);
			input.limit = value;
			input.style.textAlign = "right";

			var selector = select();
			for (var i = firstIndex; i <= lastIndex; i++) selector.appendChild(option(allIndexes[i].character, i));
			/*	Unfortunately Opera cannot do this immediately (see hack later in the file) :
				Malheureusement Opera ne peut pas faire ceci immédiatement (voir l'astuce plus loin) :
			*/
			selector.value = value;

			input.select = selector;
			selector.textInput = input;

			input.onchange = onChangeTextInput;

			selector.onchange = onChangeSelect;

			var kanjiSpan = span(input, selector);
			kanjiSpan.textInput = input;
			kanjiSpan.select = selector;
			return kanjiSpan;
		}

		fromKanjiSpan = rangeInput(firstIndex);
		toKanjiSpan = rangeInput(lastIndex);
		fromKanjiTextInput = fromKanjiSpan.textInput;
		toKanjiTextInput = toKanjiSpan.textInput;
		fromKanjiTextInput.other = toKanjiTextInput;
		toKanjiTextInput.other = fromKanjiTextInput;

		/*	Opera hack (I want this fucking value set and I'm gonna have it set) :
			Astuce pour Opera (je veux fixer cette valeur et je la fixerai, nom d'une pipe) :
		*/
		fromKanjiTextInput.id = "fromKanjiTextInput";
		setTimeout("document.getElementById('fromKanjiTextInput').value = " + firstIndex + ";", 100);
		toKanjiTextInput.id = "toKanjiTextInput";
		setTimeout("document.getElementById('toKanjiTextInput').value = " + lastIndex + ";", 100);

		return addTo(id, textNode("kanji de "), fromKanjiSpan, textNode(" à "), toKanjiSpan);
	}

	this.inRange = function(word) {
	/*	return true if word belongs to studied range
		rends vrai si le mot appartient à la plage étudiée
	*/
		var indexes = word.indexes;
		for (i = 0; i < indexes.length; i++)
			if (indexes[i] >= fromKanjiTextInput.value) if (indexes[i] <= toKanjiTextInput.value) return true;
		return false;
	}

	this.indexInRange = function(index) {
	/*	return true if index belongs to studied range
		rends vrai si l'index appartient à la plage étudiée
	*/
		if (index >= fromKanjiTextInput.value) if (index <= toKanjiTextInput.value) return true;
		return false;
	}

	this.randomKanji = function() {
	/*	get a kanji at random within studied range
		tire au sort un kanji dans la plage étudiée
	*/
		return allIndexes.random(fromKanjiTextInput.value, toKanjiTextInput.value);
	}

	this.randomOnReading = function() {
	/*	get an on reading at random
		tire au sort une lecture on
	*/
		return this.randomKanji().onReadings.random();
	}

	this.randomKanjiWord = function() {
	/*	get a kanji word at random
		tire au sort un mot en kanji
	*/
		return this.randomKanji().kanjiWords.random();
	}

	this.randomKanaWord = function() {
	/*	get a kana word at random
		tire au sort un mot en kana
	*/
		return this.randomKanjiWord().kanaWords.random();
	}

	this.randomTranslation = function() {
	/*	get a translation at random
		tire au sort une traduction
	*/
		return this.randomKanjiWord().translations.random();
	}
}
