var messages = {};
var lang;

var literal = true;

var app;
var tv;
var tree;

var cel;
var clist;

var blocklists = {
	"block.element": ["statement.obj", "statement.select"],
	"block.filter": ["statement.filter.type", "statement.filter.exp"],
	"block.object": ["statement.comp", "statement.prop", "statement.filter"],
	"block.root": ["statement.select", "statement.define"],
	"block.select": ["statement.select.type", "statement.select.exp"],
};

var ilid = 0;
var inputlists = {
	"input.comp": ["image", "polysec", "rect", "scale3", "text"],
	"input.prop": [
		"pos", "rot", "scale",
		"image.fit", "image.frame", "image.opacity",
		"polysec.body", "polysec.head", "polysec.shape", "polysec.tail", "polysec.transparent",
		"rect.color",
		"scale3.border",
		"sprite.bound", "sprite.pivot", "sprite.scale", "sprite.transparent", "sprite.ui", "sprite.zindex",
		"text.frames", "text.opacity", "text.size", "text.spacing", "text.value",
	],
	"input.type": ["chart", "group", "track", "note"],
};

var statementlists = {
	"statement.comp": ["input.comp"],
	"statement.define": ["input.ident", "exp"],
	"statement.filter": ["block.filter", "block.object"],
	"statement.filter.exp": ["exp"],
	"statement.filter.type": ["input.type"],
	"statement.obj": ["block.object"],
	"statement.prop": ["input.prop", "exp"],
	"statement.select": ["block.select", "block.element"],
	"statement.select.exp": ["exp"],
	"statement.select.type": ["input.type"],
}

window.onload = function () {
	lang = navigator.language.replace("-", "_");
	if (!messages[lang]) lang = "en_US";

	app = $("#app");
	tv = $("#treeview");
	tree = $("#tree");
	app.find("tbr").replaceWith(function () {
		return msg($(this).text());
	});
	addAddButton(tree).on("click", function (event) {
		if (clist) clist.remove();
	});
	tv.on("mousemove", null, null, onMouseMove);

	$(".tgbtn").on("click", function (event) {
		var btn = $(this);
		btn.toggleClass("tgbtn-active");
		onToggleButtonClick(btn.attr("id"), btn.hasClass("tgbtn-active"));
	});
};

function addAddButton(el) {
	return el.append($("<span></span>").addClass("btn btn-add").text("+").on("click", null, el, onAddButtonClick));
}

function addAddExpButton(el) {
	return el.append($("<span></span>").addClass("btn btn-add").text("+").on("click", null, el, onAddExpButtonClick));
}

function addAddButtonLiteral(el) {
	return el.append($("<span></span>").addClass("btn btn-add t-inline").text("+").on("click", null, el, onAddButtonLiteralClick));
}

function addDeleteButton(el) {
	return el.append($("<span></span>").addClass("btn btn-delete").text("x").on("click", null, el, onDeleteButtonClick));
}

function addDeleteButtonLiteral(el) {
	return el.append($("<span></span>").addClass("btn btn-delete t-inline").text("x").on("click", null, el, onDeleteButtonLiteralClick));
}

function onAddButtonClick(event) {
	cel = el = event.data;
	generateAddList(event, blocklists[el.attr("key")]);
}

function onAddExpButtonClick(event) {
	cel = el = event.data;
	generateAddExpList(event);
}

function onAddButtonLiteralClick(event) {
	var el = event.data.children(".t-internal");
	if (!el.hasClass("t-node"))
		el = el.children(".t-node");
	event.data = el;
	onAddButtonClick(event);
}

function onDeleteButtonClick(event) {
	var el = event.data.parents(".t-literal").first();
	event.data.remove();
	if (el) updateLiteral(el);
}

function onDeleteButtonLiteralClick(event) {
	var el = event.data.parents(".t-literal").first();
	fetchInternalStatements(el).eq(parseInt(event.data.attr("target"))).remove();
	updateLiteral(el);
}

var tblist = {
	"tb-debug": "mode-debug",
	"tb-hide-add": "mode-hide-add",
	"tb-hide-delete": "mode-hide-delete",
	"tb-sort": "mode-sort",
}
function onToggleButtonClick(id, state) {
	var cls = tblist[id];
	if (state) tv.addClass(cls);
	else tv.removeClass(cls);
	if (cls == "mode-sort") {
		var il = tv.find(".t-input");
		var sl = tv.find(".t-statement").not(".t-inline");
		if (state) {
			il.each(function (i) {
				$(il[i]).children("input").attr("disabled", "");
			});
			sl.each(function (i) {
				$(sl[i]).on("click", null, $(sl[i]), onStatementSort);
			});
		}
		else {
			il.each(function (i) {
				var el = $(il[i]);
				if (!el.parents(".t-internal").length)
					el.children("input").attr("disabled", null);
			});
			sl.each(function (i) {
				$(sl[i]).off("click", null, onStatementSort);
			});
			tv.removeClass("state-grabbing");
			if (grabbing) {
				grabbing.removeClass("t-grabbing");
				grabbing = null;
				grabPointer.remove();
			}
		}
	}
}

var grabbing;
var grabPointer;
function onStatementSort(event) {
	event.stopPropagation();
	if (grabbing) {
		var l = grabbing.parent().children(".t-statement");
		var i = getGrabPtrIndex(event.pageY + tv.scrollTop() - tv[0].offsetTop);
		if (i == l.length)
			grabbing.insertAfter(l.last());
		else
			grabbing.insertBefore(l.eq(i));
		tv.removeClass("state-grabbing");
		grabbing.removeClass("t-grabbing");
		grabPointer.remove();
		updateLiteral(grabbing.parents(".t-literal").first());
		grabbing = null;
	}
	else {
		grabbing = event.data;
		grabbing.addClass("t-grabbing");
		tv.addClass("state-grabbing");
		grabPointer = $("<hr />").addClass("grab-ptr");
	}
}

function onMouseMove(event) {
	if (grabbing) {
		var l = grabbing.parent().children(".t-statement");
		var i = getGrabPtrIndex(event.pageY + tv.scrollTop() - tv[0].offsetTop);
		if (i == l.length)
			grabPointer.remove().insertAfter(l.last());
		else
			grabPointer.remove().insertBefore(l.eq(i));
	}
}

function getGrabPtrIndex(y) {
	var l = grabbing.parent().children(".t-statement");
	var tl = l.map(function (i) {
		return l[i].offsetTop - y;
	}).toArray();
	tl.push(l.last()[0].offsetTop + l.last().height() - y);
	return ArrayUtil.binarySearch(tl, 0, function (a, b) {
		return a - b;
	}, true);
}

function generateAddList(event, items) {
	event.stopPropagation();
	if (clist) clist.remove();
	var ul = $("<ul></ul>").addClass("t-list");
	for (var i in items) {
		var item = items[i];
		ul.append($("<li></li>").addClass("t-li").attr("key", item).text(msg("list." + item)).on("click", null, item, onAddItemClick));
	}
	popup(event, ul);
}

function generateAddExpList(event, items) {
	event.stopPropagation();
	if (clist) clist.remove();
	var ul = $("<ul></ul>").addClass("t-list t-exp-list");
	for (var i in items) {
		var item = items[i];
		ul.append($("<li></li>").addClass("t-li").attr("key", item).text(msg("list." + item)).on("click", null, item, onAddItemClick));
	}
	popup(event, ul);
}

function popup(event, ul) {
	clist = ul;
	app.append(ul);
	var left = event.pageX;
	if (left + ul.outerWidth() > window.innerWidth)
		left = Math.max(0, left - ul.outerWidth());
	var top = event.pageY;
	if (top + ul.outerHeight() > window.innerHeight)
		top = Math.max(0, top - ul.outerHeight());
	ul.css({
		"left": left,
		"top": top
	});
}

function generateInputList(el, key) {
	var il = inputlists[key];
	if (!il) return;
	var id = "il-" + ilid.toString();
	var dl = $("<datalist></datalist>").attr("id", id);
	for (var i in il) {
		var li = il[i];
		dl.append($("<option />").attr({
			"label": msg(key + "." + li),
			"value": li,
		}));
	}
	dl.insertAfter(el);
	el.attr("list", id);
	ilid++;
}

function onAddItemClick(event) {
	var nel = create(event.data).insertBefore(cel.children(":last"));
	if (cel.parents(".t-node").first().hasClass("t-literal"))
		updateLiteral(cel.parents(".t-node").first());
	var il = nel.find(".t-input");
	il.each(function (i) {
		var iel = $(il[i]);
		Stretchy.resize(iel.children("input")[0]);
	});
	cel = null;
	clist.remove();
}

function onInputLiteralUpdate(event) {
	var ib = event.data.children("input");
	var el = event.data.parents(".t-literal").first();
	var lel = fetchInternalStatements(el).eq(parseInt(event.data.attr("target"))).children("[pid=\"" + event.data.attr("pid") + "\"]").children("input").val(ib.val());
	Stretchy.resize(lel[0]);
}

function syncLiteral(iel) {
	if (iel.attr("target") == -1) return;
	var ib = iel.children("input");
	var el = iel.parents(".t-literal").first();
	ib.val(fetchInternalStatements(el).eq(parseInt(iel.attr("target"))).children("[pid=\"" + iel.attr("pid") + "\"]").children("input").val());
}

function fetchInternalStatements(el, key) {
	var l = el.children(".t-internal");
	if (!l.hasClass("t-node"))
		l = l.children(".t-node").first();
	if (key)
		return l.children("[key=\"" + key + "\"]");
	else
		return l.children();
}

function create(key, target) {
	var k0 = key.split(".")[0];
	var flag = msg("literal." + key);
	if (k0 == "input") flag = true;
	if (target != null && literal && flag)
		switch (k0) {
			case "block":
				return createBlockLiteral(key, target);
			case "input":
				return createInput(key, target);
			case "statement":
				return createStatementLiteral(key, target);
		}
	else
		switch (k0) {
			case "block":
				return createBlock(key);
			case "statement":
				return createStatement(key);
			case "exp":
				return createExp(key);
		}
}

function createBlock(key) {
	var m = msg(key);
	var b = addAddButton($("<div></div>").attr("key", key).addClass("t-node t-block"));
	if (!m) return b;
	else {
		var n = $("<div></div>").addClass("t-node-sup t-block-sup").html(m);
		n.children("tbr").replaceWith(b);
		return n;
	}
}

function createBlockLiteral(key, target) {
	var o = createBlock(key).addClass("t-internal");
	var el = $("<div></div>").attr({
		"key": key,
		"target": target.index()
	}).addClass("t-node t-literal");
	el.append(o);
	updateLiteral(el);
	return el;
}

function createExp(key) {
	var m = msg(key);
	var el = addAddExpButton($("<div></div>").attr("key", key).addClass("t-node t-exp t-inline").html(m));
	return el;
}

function createInput(key, target) {
	var el = $("<input />").attr({
		"type": "text",
		"placeholder": msg(key + ".default")
	});
	var r = $("<span></span>").attr({
		"key": key,
		"target": target.index()
	}).addClass("t-node t-input t-inline").append(el);
	generateInputList(el, key);
	applyResizeHandler(el);
	el.on("change input", null, r, onInputLiteralUpdate);
	return r;
}

function createStatement(key) {
	var r = msg(key);
	var el = addDeleteButton($("<div></div>").attr("key", key).addClass("t-node t-statement").html(r));
	el.children("tbr").replaceWith(function () {
		var m = $(this).text();
		var id = parseInt(m);
		if (isNaN(id))
			return create(m, $(this).parent());
		else
			return create(statementlists[key][id], $(this).parent()).attr("pid", id);
	});
	return el;
}

function createStatementLiteral(key, target) {
	var r = msg("literal." + key);
	var el = addDeleteButtonLiteral($("<div></div>").attr({
		"key": key,
		"target": target.index()
	}).addClass("t-node t-statement t-inline").html(r));
	el.children("tbr").replaceWith(function () {
		var m = $(this).text();
		var id = parseInt(m);
		if (isNaN(id))
			return create(m, target);
		else
			return create(statementlists[key][id], target).attr("pid", id);
	});
	return el;
}

function applyResizeHandler(el) {
	el.on("change input", function () {
		Stretchy.resize(this);
	});
}

function updateLiteral(el) {
	var key = el.attr("key");
	var m = msg("literal." + key);
	var o = el.children(".t-internal");
	tv.append(o); // Stashes the supplement blocks to prevent the event listeners from being detached
	addAddButtonLiteral(el.html(m));
	el.append(o); // Restores the blocks
	el.children("tbr").replaceWith(function () {
		var m = $(this).text().split("?");
		var def;
		if (m.length > 1) {
			def = m[1];
			m = m[0];
		}
		else
			m = m[0];
		var l = fetchInternalStatements(el, m);
		var s = l.map(function (si) {
			return create(m, l.eq(si));
		});
		return conjAnd(s, def);
	});
	var il = el.find(".t-input");
	il.each(function (i) {
		var iel = $(il[i]);
		if (iel.parents(".t-internal").length || tv.hasClass("mode-sort"))
			iel.children("input").attr("disabled", "");
		syncLiteral(iel);
		Stretchy.resize(iel.children("input")[0]);
	});
	var cl = el.contents();
	for (var t in cl) {
		var ci = cl[t];
		if (ci.nodeName == "#text" && ci.nodeValue == " ") {
			ci.remove();
		}
	}
}

function conjAnd(list, def) {
	var el = $("<div></div>").addClass("t-conj t-inline");
	for (var i = 0; i < list.length; i++)
		el.append(list[i]);
	if (list.length == 0)
		el.text(def);
	return el;
}

function msg(key) {
	var m = messages[lang][key];
	if (!m) return null;
	return m.replace(/{(.*?)}/g, "<tbr>$1</tbr>");
}
