var color;
var basex;
var basey;
var coverx;
var covery;

var width = 0;
var height = 0;

var isCtrl = false;
var isShift = false;

var reg = new RegExp("\\s+", "g");

document.onkeyup = function(e) { 
    if (e.which == 17) { isCtrl = false; return true; }
    if (e.which == 16) { isShift = false; return true; }
};

document.onkeydown = function(e) {
    if (e.which == 17) { isCtrl = true; return true; }
    if (e.which == 16) { isShift = true; return true; }
    if (e.which == 37) { hslide(isShift ? -5 : -1); return false; }
    if (e.which == 39) { hslide(isShift ? +5 : +1); return false; }
    if (e.which == 38) { vslide(isShift ? -5 : -1); return false; }
    if (e.which == 40) { vslide(isShift ? +5 : +1); return false; }
};

function getClass(elem, cls) {
    var current = elem.getAttribute("class");
    if (current == null) return false;

    var classes = current.split(reg);

    current = "";
    for (var i in classes)
	if (classes[i] == cls)
	    return true;

    return false;
}

function changeClasses(elem, clears, toggles) {
    // Normalize the arguments.
    if (typeof(clears) == "string")
	clears = clears.split(reg);
    if (typeof(toggles) == "string")
	toggles = toggles.split(reg);

    // Get the current set of classes.
    var text = elem.getAttribute("class");
    var current = { };
    if (text) {
	var classes = text.split(reg);
	for (var s in classes)
	    current[classes[s]] = true;
    }

    var oldtext = null;
    for (var s in current) {
	if (oldtext == null)
	    oldtext = s;
	else
	    oldtext += " " + s;
    }

    if (clears)
	for (var s in clears)
	    delete current[clears[s]];

    if (toggles)
	for (var s in toggles)
	    if (toggles[s] in current)
		delete current[toggles[s]];
	    else
		current[toggles[s]] = true;

    text = null;
    for (var s in current) {
	if (text == null)
	    text = s;
	else
	    text += " " + s;
    }
    if (false)
	alert("Clearing " + clears + "; toggling " + toggles + "\n" +
	      "Old state: [" + oldtext + "]\n" +
	      "New state: [" + text + "]");

    if (text)
	elem.setAttribute("class", text);
    else
	elem.removeAttribute("class");
}

function addClass(elem, cls) {
    changeClasses(elem, cls, cls);
}

function removeClass(elem, cls) {
    changeClasses(elem, cls, null);
}

function toggleClass(elem, cls) {
    changeClasses(elem, null, cls);
}


function boardUp(e, cell, fx, fy) {
    if (e.button != 0) return;
    if (color == null) return;

    // Clear the selected area.
    //alert("Clearing [" + basex + "," + coverx + "] [" + basey + "," + covery + "]");
    changeArea(basex, coverx, false, basey, covery, false, "selected", null);

    // Set the cells as requested.
    //alert("Changing [" + basex + "," + fx + "] [" + basey + "," + fy + "]");
    changeArea(basex, fx, false, basey, fy, false,
	       "on", color ? null : "on");
    
    color = null;
}

function boardDown(e, cell, x, y) {
    if (e.button != 0) return;

    if (color)
	// Cancel previous drag.
	changeArea(basex, coverx, false,
		   basey, covery, false,
		   "selected", null);

    color = isCtrl;
    coverx = basex = x;
    covery = basey = y;
    addClass(cell, "selected");
}

function changeCell(x, y, clears, toggles) {
    var elem = document.getElementById("cell" + x + "_" + y);
    if (!elem) return;
    changeClasses(elem, clears, toggles);
}

function sign(x) {
    if (x < 0) return -1;
    if (x > 0) return +1;
    return 0;
}

function changeArea(x0, x1, excx, y0, y1, excy, clears, toggles) {
    if (!clears && !toggles) return;

    //alert("Clearing " + clears + "; toggling " + toggles);

    // Decide whether the upper bound of each dimension should be
    // regarded as exclusive.
    if (excy)
	if (y1 == y0)
	    return;
	else
	    y1 -= sign(y1 - y0);
    if (excx)
	if (x1 == x0)
	    return;
	else
	    x1 -= sign(x1 - x0);

    // Order the bounds for iteration.
    var tmp;
    if (x1 < x0) {
	tmp = x1;
	x1 = x0;
	x0 = tmp;
    }
    if (y1 < y0) {
	tmp = y1;
	y1 = y0;
	y0 = tmp;
    }

    // Set the cells.
    for (var y = y0; y <= y1; y++)
	for (var x  = x0; x <= x1; x++)
	    changeCell(x, y, clears, toggles);
}

function between(v, a, b) {
    if (v < a && v < b) return false;
    if (v > a && v > b) return false;
    return true;
}

function intoCell(cell, ex, ey) {
    if (color == null) return;

    var minx = ex < basex ?
	coverx < ex ? coverx : ex : coverx < basex ? coverx : basex;
    var miny = ey < basey ?
	covery < ey ? covery : ey : covery < basey ? covery : basey;
    var maxx = ex > basex ?
	coverx > ex ? coverx : ex : coverx > basex ? coverx : basex;
    var maxy = ey > basey ?
	covery > ey ? covery : ey : covery > basey ? covery : basey;

    coverx = ex;
    covery = ey;

    for (var y = miny; y <= maxy; y++) {
	for (var x = minx; x <= maxx; x++) {
	    changeCell(x, y, "selected",
		       between(x, basex, coverx) &&
		       between(y, basey, covery) ? "selected" : null);
	}
    }
}

function correctAttrs(cell, x, y) {
    cell.onmouseover = function(e) { intoCell(cell, x, y); return true; };
    cell.onmousedown = function(e) { boardDown(e, cell, x, y); return true; };
    cell.onmouseup = function(e) { boardUp(e, cell, x, y); return true; };
    //cell.onclick = function(e) { toggleClass(cell, "on"); return true; };
    cell.setAttribute("id", "cell" + x + "_" + y);
}

function createCell(row, x, y) {
    var cell = document.createElement("span");
    if (x % 5 == 0)
	addClass(cell, "fifth");
    correctAttrs(cell, x, y);
    row.appendChild(cell);
}

function createCells(row, y, cw, nw) {
    while (cw < nw) {
	createCell(row, cw, y);
	cw++;
    }
}

function resizeGrid(nw, nh) {
    if (nw < 1) nw = 1;
    else if (nw > 100) nw = 100;
    if (nh < 1) nh = 1;
    else if (nh > 100) nh = 100;

    var container = document.getElementById("table");

    // Delete excess rows.  
    for (var row = container.lastChild; row && height > nh; row = next) {
	var next = row.previousSibling;
	if (row.nodeType != Node.ELEMENT_NODE)
	    continue;
	container.removeChild(row);
	height--;
    }

    // Resize existing rows.
    if (nw != width) {
	var y = 0;
	for (var row = container.firstChild; row; row = row.nextSibling) {
	    var cw = width;

	    // Remove excess cells.
	    for (var cell = row.lastChild; cell && cw > nw; cell = next) {
		var next = cell.previousSibling;
		if (cell.nodeType != Node.ELEMENT_NODE)
		    continue;
		row.removeChild(cell);
		cw--;
	    }

	    // Create additional cells.
	    createCells(row, y, cw, nw);
	    y++;
	}
    }

    // Create additional rows.
    while (height < nh) {
	var row = document.createElement("div");
	if (height % 5 == 0)
	    addClass(row, "fifth");
	container.appendChild(row);

	// Create cells.
	createCells(row, height, 0, nw);

	height++;
    }

    width = nw;
}


function hslide(n) {
    if (width < 2) return;

    n %= width;
    if (!n) return;

    if (n > width / 2)
	n -= width;
    else if (n < -width / 2)
	n += width;

    var container = document.getElementById("table");

    if (n < 0)
	for (var row = container.firstChild; row; row = row.nextSibling)
	    hslideRowLeft(row, -n);
    else
	for (var row = container.firstChild; row; row = row.nextSibling)
	    hslideRowRight(row, n);
}

function hslideRowLeft(row, n) {
    var arr = new Array();

    var to = row.firstChild;
    var from = to;
    while (arr.length < n) {
	arr.push(getClass(from, "on"));
	from = from.nextSibling;
    }

    while (from) {
	changeClasses(to, "on", getClass(from, "on") ? "on" : null);
	from = from.nextSibling;
	to = to.nextSibling;
    }

    for (var i = 0; i < n; i++) {
	changeClasses(to, "on", arr[i] ? "on" : null);
	to = to.nextSibling;
    }
}

function hslideRowRight(row, n) {
    var arr = new Array();

    var to = row.lastChild;
    var from = to;
    while (arr.length < n) {
	arr.push(getClass(from, "on"));
	from = from.previousSibling;
    }

    while (from) {
	changeClasses(to, "on", getClass(from, "on") ? "on" : null);
	from = from.previousSibling;
	to = to.previousSibling;
    }

    for (var i = 0; i < n; i++) {
	changeClasses(to, "on", arr[i] ? "on" : null);
	to = to.previousSibling;
    }
}

function vslide(n) {
    if (height < 2) return;

    n %= height;
    if (!n) return;

    if (n > height / 2)
	n -= height;
    else if (n < -height / 2)
	n += height;

    var container = document.getElementById("table");

    while (n < 0) {
	if (vslideRowsUp(container))
	    n++;
    }

    while (n > 0) {
	if (vslideRowsDown(container))
	    n--;
    }

    var rows = container.getElementsByTagName("div");
    for (var n = 0; n < rows.length; n++) {
	var row = rows.item(n);
	changeClasses(row, "fifth", n % 5 ? null : "fifth");
	var cells = row.getElementsByTagName("span");
	for (var c = 0; c < cells.length; c++) {
	    var cell = cells.item(c);
	    correctAttrs(cell, c, n);
	}
    }
}

function vslideRowsUp(container) {
    var row = container.firstChild;
    container.removeChild(row);
    container.appendChild(row);
    return row.nodeType == Node.ELEMENT_NODE;
}

function vslideRowsDown(container) {
    var row = container.lastChild;
    container.removeChild(row);
    container.insertBefore(row, container.firstChild);
    return row.nodeType == Node.ELEMENT_NODE;
}

function revealUtility() {
    document.getElementById("utility").removeAttribute("class");
}

function resize() {
    var nw = document.getElementById("width").value;
    var nh = document.getElementById("height").value;
    resizeGrid(nw, nh);
}

function generate() {
    var grid = new Array();

    var text = "";

    var title = document.getElementById("title").value;
    if (title && title != "")
	text += "title \"" + title + "\"\n";
    var author = document.getElementById("author").value;
    if (author && author != "")
	text += "by \"" + author + "\"\n";

    text += "width " + width + "\n";
    text += "height " + height + "\n";

    var container = document.getElementById("table");

    var rows = container.getElementsByTagName("div");
    for (var n = 0; n < rows.length; n++) {
	var row = rows.item(n);
	var line = new Array();
	grid.push(line);

	var cells = row.getElementsByTagName("span");
	for (var c = 0; c < cells.length; c++) {
	    var cell = cells.item(c);
	    line.push(getClass(cell, "on"));
	}
    }


    text += "\nrows\n";
    for (var y in grid) {
	var row = grid[y];
	var sep = "";
	var count = 0;
	for (var x in row) {
	    if (row[x]) {
		count++;
	    } else {
		if (count > 0) {
		    text += sep + count;
		    count = 0;
		    sep = ",";
		}
	    }
	}
	if (count > 0) {
	    text += sep + count;
	    count = 0;
	    sep = ",";
	}
	if (sep == "")
	    text += "0";
	text += "\n";
    }

    text += "\ncolumns\n";
    for (var x = 0; x < width; x++) {
	var sep = "";
	var count = 0;
	for (var y = 0; y < height; y++) {
	    if (grid[y][x]) {
		count++;
	    } else {
		if (count > 0) {
		    text += sep + count;
		    count = 0;
		    sep = ",";
		}
	    }
	}
	if (count > 0) {
	    text += sep + count;
	    count = 0;
	    sep = ",";
	}
	if (sep == "")
	    text += "0";
	text += "\n";
    }

    document.getElementById("result").textContent = text;
}

