function DbEntry(path, label, id, titleParentIds) {
	this.path = path;
	this.label = label;
	this.id = id;

	this.titleParentIds = titleParentIds;
}

function ContentsDictionaryEntry(title, content) {
	this.title = title;
	this.content = content;
}

function callSearch(keyword, usesWildcard) {
	if (event.keyCode == 0xD) search(keyword, usesWildcard);
}

function search(keyword, usesWildcard) {
	keyword = trim(keyword);

	if (keyword.length == 0) {
		alert(keywordEmptyMessage);
		return;
	}

	var pattern = keywordToPattern(keyword, usesWildcard);

	if (pattern == null) {
		alert(keywordEmptyMessage);
		return;
	}

	var found = false;
	var targetIsTitle = (getCheckedRadioValue(document.targetForm) == "title");
	var rangeIsCurrent = (getCheckedRadioValue(document.rangeForm) == "current");

	var head = getHead(rangeIsCurrent);
	var tail = getTail(rangeIsCurrent, dbArray);

	var resultArray = createResultArray(dbArray);

	var matchedDictionary = {};

	for (var i = head; i < tail; i++) {
		for (var j = 0; j < dbArray[i].length; j++) {
			var dbItem = dbArray[i][j];
			var matchedItems = [];
			var targets = getTargets(dbItem, targetIsTitle);

			if (!targets || targets.length == 0) continue;

			for (var k = 0; k < targets.length; k++) {
				var targetItem = targets[k];
				var matches = targetItem.target.match(pattern);

				if (matches) {
					found = true;
					matches = uniqArray(matches);
					var matchedItem = {targetId: targetItem.targetId, matches: matches};
					matchedItems[matchedItems.length] = matchedItem;

					if (dbItem.id == targetItem.targetId) {
						var len = resultArray[i].length;
						resultArray[i][len] = dbItem;
					}
				}
			}

			if (matchedItems.length > 0) {
				var key = dbItem.titleParentIds[0];
				matchedDictionary[key] = matchedItems;
			}
		}
	}

	var resultContent = buildResultContent(resultArray, found, targetIsTitle);

	var mainFrame = parent.main;
	writeResult(resultContent, mainFrame);

	mainFrame.matchedDictionary = matchedDictionary;
}

function buildResultContent(resultArray, found, targetIsTitle) {
	var resultContent = "<html><head><meta http-equiv='X-UA-Compatible' content='IE=EmulateIE9'/><link rel='stylesheet' type='text/css' href='../src/sub.css'><script type='text/javascript' src='../src/search.js'></script></head><body class='result'><div class='title'>" + searchResultTitle + "</div>\n";

	if (found) {
		for (var i = 0; i < resultArray.length; i++) {
			if (resultArray[i].length > 0) {
				resultContent += "<div class='manualTitle'>" + resultArray[i].manualTitle + "</div>\n";
			}

			for (var j = 0; j < resultArray[i].length; j++) {
				var resultItem = resultArray[i][j];
				var label = resultItem.label;
				var path = resultItem.path;
				var id = resultItem.titleParentIds[0];
				resultContent += "<div class='heading'><a";
				resultContent += " href='../" + path + "'";
				resultContent += " target='subWin'";
				resultContent += " onclick='setMatches(\"" + id + "\", " + targetIsTitle + ");'";
				resultContent += ">" + label + "</a></div>\n";
			}
		}
	}
	else {
		resultContent += "<div>" + notFoundMessage + "</div>\n";
	}

	resultContent += "</body></html>";

	return resultContent;
}

function writeResult(resultContent, frame) {
	frame.document.open();
	frame.document.write(resultContent);
	frame.document.close();
}

function keywordToPattern(keyword, usesWildcard) {
	if (usesWildcard) {
		keyword = wildcardToRegex(keyword);
	} else {
		keyword = plainToRegex(keyword);
	}

	keyword = trim(keyword);

	if (keyword.length == 0) {
		return null;
	}
	else {
		var pattern = new RegExp(keyword, "ig");
		return pattern;
	}
}

function plainToRegex(patternStr) {

	return patternStr.replace(/(\W)/g, "\\$1");
}

function wildcardToRegex(patternStr) {

	var map1 = [
		[/\\\\/g, "\b"],
		[/\\\?/g, "\f"],
		[/\\\*/g, "\x0B"]
	];

	var map2 = [
		[/\\/g, ""],
		[/\//g, "\\/"],
		[/\./g, "\\."],
		[/\|/g, "\\|"],
		[/\+/g, "\\+"],
		[/\(/g, "\\("],
		[/\)/g, "\\)"],
		[/\[/g, "\\["],
		[/\]/g, "\\]"],
		[/\{/g, "\\{"],
		[/\}/g, "\\}"],
		[/\^/g, "\\^"],
		[/\$/g, "\\$"],

		[/\?/g, "."],
		[/\*/g, ".*?"]
	];

	var map3 = [
		[/[\b]/g, "\\\\"],
		[/\f/g, "\\?"],
		[/\v/g, "\\*"]
	];

	for (var i = 0; i < map1.length; i++) {
		patternStr = patternStr.replace(map1[i][0], map1[i][1]);
	}

	for (var i = 0; i < map2.length; i++) {
		patternStr = patternStr.replace(map2[i][0], map2[i][1]);
	}

	for (var i = 0; i < map3.length; i++) {
		patternStr = patternStr.replace(map3[i][0], map3[i][1]);
	}

	return patternStr;
}

function showCodes(str) {
	var codes = "";
	for (var i = 0; i < str.length; i++) {
		codes += str.charCodeAt(i).toString(16) + " ";
	}
	alert(codes.slice(0, codes.length - 1));
}

function uniqArray(arr) {
	var hash = {};
	for (var i = 0; i < arr.length; i++) {
		hash[arr[i]] = arr[i];
	}
	var newArray = [];
	for (var key in hash) {
		newArray[newArray.length] = hash[key];
	}
	return newArray;
}

function getCheckedRadioValue(form) {
	var val = null;

	for (var i = 0; i < form.elements.length; i++) {
		if (form.elements[i].checked) val = form.elements[i].value;
	}

	return val;
}

function trim(str) {
	return str.replace(/^\s+|\s+$/g, "");
}

function createResultArray(dbArr) {
	var resultArr = [];
	for (var i = 0; i < dbArr.length; i++) {
		resultArr[i] = [];
		resultArr[i].manualTitle = dbArr[i].manualTitle;
	}
	return resultArr;
}

function getTargets(dbItem, targetIsTitle) {
	var targets = [];

	for (var i = 0; i < dbItem.titleParentIds.length; i++) {
		var targetId = dbItem.titleParentIds[i];
		if (targetIsTitle) {
			targets[targets.length] = {targetId: targetId, target: contentsDictionary[targetId].title};
		}
		else {
			targets[targets.length] = {targetId: targetId, target: contentsDictionary[targetId].content};
		}
	}

	return targets;
}

function getHead(rangeIsCurrent) {
	var head;
	if (rangeIsCurrent) {
		head = top.currentIndex;
	}
	else {
		head = 0;
	}
	return head;
}

function getTail(rangeIsCurrent, dbArray) {
	var tail;
	if (rangeIsCurrent) {
		tail = top.currentIndex + 1;
	}
	else {
		tail = dbArray.length;
	}
	return tail;
}

function setMatches(matchedItemsId, targetIsTitle) {
	self.matchedItemsId = matchedItemsId;
	self.targetIsTitle = targetIsTitle;

}

function showMatches(matchedItemsId) {
	var msg = "";
	if (!self.matchedDictionary) {
		msg = "No dictionary.";
	}
	else {
		var matchedItems = self.matchedDictionary[matchedItemsId];	
		var matches = [];
		for (var i = 0; i < matchedItems.length; i++) {
			for (var j = 0; j < matchedItems[i].matches.length; j++) {
				matches[matches.length] = matchedItems[i].matches[j];
			}
		}
		matches = uniqArray(matches);
		msg = matches.join(", ");
	}
	status = msg;
}

function callHighlight() {
	if (!top.opener || typeof top.opener.document != 'object' || !top.opener.matchedDictionary || !top.opener.matchedItemsId) return;

	var targetFrame = self;
	var matchedItemsId = top.opener.matchedItemsId;
	var matchedItems = top.opener.matchedDictionary[matchedItemsId];
	var targetIsTitle = top.opener.targetIsTitle;

	if (targetIsTitle) {
		for (var i = 0; i < matchedItems.length; i++) {
			var targetId = matchedItems[i].targetId;
			var matches = matchedItems[i].matches;
			var targetElement = getTargetElement(targetFrame, targetId, targetIsTitle);
			if (!targetElement) continue;
			highlight(matches, targetFrame, targetElement);
		}
	}
	else {

		var matches = [];
		for (var i = 0; i < matchedItems.length; i++) {
			for (var j = 0; j < matchedItems[i].matches.length; j++) {
				matches[matches.length] = matchedItems[i].matches[j];
			}
		}
		matches = uniqArray(matches);
		var targetElement = targetFrame.document.body;
		highlight(matches, targetFrame, targetElement);
	}

	top.opener.matchedItemsId = null;
	top.opener.targetIsTitle = null;
}

function highlight(matches, targetFrame, targetElement) {

	for (var i = 0; i < matches.length; i++) {
		var matched = matches[i];
		if (matched.length == 0) continue;

		var targetRange = targetFrame.document.body.createTextRange();

		targetRange.moveToElementText(targetElement);

		var range = targetRange.duplicate();

		range.collapse(true);

		while (range.findText(matched) && targetRange.inRange(range)) {
			range.execCommand("backColor", false, "aqua");

			range.collapse(false);
		}
	}
}

function getTargetElement(targetFrame, targetId, targetIsTitle) {
	var targetElement = targetFrame.document.getElementById(targetId);

	if (targetIsTitle) {

		var titleIsFound = false;
		var childArray = targetElement.childNodes;

		for (var i = 0; i < childArray.length; i++) {
			if (doesContain(childArray[i], "title")) {
				targetElement = childArray[i];
				titleIsFound = true;
				break;
			}
		}

		if (!titleIsFound) {
			var divArray = targetFrame.document.getElementsByTagName("div");

			for (var i = 0; i < divArray.length; i++) {
				if (doesContain(divArray[i], "manualtitle")) {
					targetElement = divArray[i];
					break;
				}
			}
		}
	}

	return targetElement;
}

function doesContain(node, targetClass) {
	var pattern = new RegExp("\\b" + targetClass + "\\b");
	return pattern.test(node.className);
}