// -*- Mode: c++; c-basic-offset: 4; -*- // hrmph, no javaspit mode in emacs ...
/* Code to implement the puzzle-solver for BlackSphinx.html
 * $Id: BlackSphinx.js,v 1.4 2007-09-16 22:26:39 eddy Exp $
 */
var status = null;
function warn (text) { status.data = text; }

function tokenClass () {
    function Token (letter, input) {
	this.cipher = this.transcript[letter];
	this.input = input;
	this.rule = null;

	var token = this; // for the sake of:
	input.onchange = function () {
	    warn("");
	    if (!this.value) {
		token.Clear();
	    } else if (token.reverse.hasOwnProperty(this.value.toLowerCase())) {
		if (token.Set(this.value.toLowerCase())) {
		    warn("More than one cipher token mapped to: " + this.value);
		}
	    } else { // reject value:
		warn("Only letters are allowed, not: " + this.value);
		this.value = "";
	    }
	}
	input.onreset = input.onchange; // wrong method name ?
    }

    // Scripted CSS, see: http://www.w3.org/TR/2000/REC-DOM-Level-2-Style-20001113/
    function Rule (who, what) {
	this.who = who;
	this.what = what;
	this.styles.insertRule('.poem .' + who + ' { content: "' +
			       what + '"; color: green; }',
			       this.styles.cssRules.length);
    }
    Rule.prototype = {
	'styles': document.styleSheets[0],
	'remove': function () {
	    var seek = "." + this.who + " ";
	    var row = this.styles.cssRules;
	    for (var i = this.first; i < row.length; i++)
		if (row[i].cssText.indexOf(seek, 0) >= 0) {
		    this.styles.deleteRule(i);
		    return;
		}
	    warn("Failed to remove poem ." + who + " rule.");
	}
    };
    Rule.prototype.first = Rule.prototype.styles.cssRules.length;

    Token.prototype = {
	"transcript": {
	    "A": "eye",
	    "B": "square",
	    "C": "square-spiral",
	    "D": "duck",
	    "E": "zig-zag",
	    "F": "goose",
	    "G": "snow-drop",
	    "H": "flag",
	    "I": "dome",
	    "J": "oven",
	    "K": "hawk",
	    "L": "snail",
	    "M": "lozenge",
	    "N": "dove",
	    "O": "mitten",
	    "P": "owl",
	    "Q": "curve-spiral",
	    "R": "stork",
	    "S": "hound",
	    "T": "man",
	    "U": "bowl",
	    "V": "hole",
	    "W": "two-flags" },

	'reverse': {
	    "a": [],
	    "b": [],
	    "c": [],
	    "d": [],
	    "e": [],
	    "f": [],
	    "g": [],
	    "h": [],
	    "i": [],
	    "j": [],
	    "k": [],
	    "l": [],
	    "m": [],
	    "n": [],
	    "o": [],
	    "p": [],
	    "q": [],
	    "r": [],
	    "s": [],
	    "t": [],
	    "u": [],
	    "v": [],
	    "w": [],
	    "x": [],
	    "y": [],
	    "z": [] },

	'Set': function (value) {
	    if (this.rule != null) this.Clear();

	    var peers = this.reverse[value];
	    var i = peers.length;
	    peers[i] = this;
	    peers.length = 1 + i; // needed !

	    if (this.rule != null)
		warn("Uncleared rule ! " +
		     this.rule.who + " -> " + this.rule.what);
	    this.rule = new Rule(this.cipher, value);

	    if (i <= 0) return false;
	    for (var i in peers)
		peers[i].input.className = "error";
	    return true;
	},

	'Clear': function () {
	    if (this.rule == null) alert("Clearing unset Token " + this.cipher + " !");
	    var peers = this.reverse[this.rule.what];

	    this.rule.remove();
	    delete this.rule;
	    this.rule = null;

	    var i = peers.length;
	    while (i-- > 0)
		if (peers[i] == this) {
		    while (++i < peers.length)
			peers[i-1] = peers[i];
		    delete peers[--i];
		    peers.length = i; // needed !
		    this.input.className = "";
		    delete this.input.className;
		    break;
		}
	    if (i < 0) alert("Failed to find Token in reverse lookup !");
	    else if (peers.length == 1) { // Clear error indicator on remaining ex-peer:
		peers[0].input.className = "";
		delete peers[0].input.className;
	    }
	}
    };

    return Token;
}

function Initialize() {
    var toy = document.getElementById("toy");
    status = toy.lastChild.firstChild; // Text node in final span of toy div.
    status.data = toy.className = "";

    var Token = tokenClass();
    var tokens = [];

    /* Bind inputs to Token objects handling letters. */
    for (var key in Token.prototype.transcript)
	tokens[tokens.length] = new Token(key, document.getElementById("use" + key));

    /* TODO: reduce table's second row to provide one cell, with no id
     * on its INPUT; clone enough copies of it to match first row,
     * binding each to a suitable Token (and maybe giving each a
     * suitable name attribute).
     */

    // onreset doesn't seem to work :-(
    toy.lastChild.previousSibling.lastChild.firstChild.onclick = function () {
	warn("");
	for (var i in tokens)
	    if (tokens[i].rule != null)
		tokens[i].Clear();
    }
}

function Update() {
    /* A no-op; form needs an action to be valid, but we don't need it
     * to do anything, since all action is handled by .onchange of
     * inputs.. */
}
