// -------------------------------------------------------------------
// 'req_lang' is defined in the HTML file.

var appl;
var lang_data = new Object;
var arrows;

function init() {
    body = document.getElementsByTagName('body')[0];
    window.removeEventListener('load', init, false);
    arrows = document.getElementById('arrows');
        // For debugging.
    if (0) {
        appl = new Appl(false);
        appl.use_arrows = false;
        appl.use_bitmap_text = false;
    }
    else {
        appl = new Appl(true);
        appl.use_arrows = true;
        appl.use_bitmap_text = true;
    }
    appl.init();
    appl.set_up_langs();
    appl.load_lang(req_lang);
    appl.wac.reset('b01', 'b07');
}
window.addEventListener('load', init, false);

// -------------------------------------------------------------------
function Appl (use_colors) {
    this.boxes = new Object;
    this.pathers = new Object;
    this.clickers = new Object;
    this.div_boxes = document.getElementById('div_boxes');
    this.cow_box = document.getElementById('cow_box');
    this.div_graphics = document.getElementById('div_graphics');
    this.langs = new Object;
    this.lang_curr = null;
    this.colors = new Object;
    this.set_colors(use_colors);
    this.lang_ids = new Array('fr', 'en');
}

// -------------------------------------------------------------------
Appl.prototype.init = function () {
    this.wac = new Wac();
    new Box('b01', 'b02', 'b09', null, 0, 0, 0, 1, 1, 0, 0, 0, 0);
    new Box('b01', 'b02', 'b09', null, 0, 0, 0, 1, 1, 0, 0, 0, 0);
    new Box('b02', 'b07', 'b15', null, 1, 0, 0, 1, 0, 0, 1, 0, 0);
    new Box('b05', 'b25', 'b02', null, 0, 0, 0, 1, 1, 0, 1, 0, 1);
    new Box('b07', 'b26', 'b05', null, 0, 0, 1, 0, 0, 0, 0, 0, 0);
    new Box('b09', 'b02', 'b35', null, 0, 0, 1, 0, 0, 0, 0, 0, 0);
    new Box('b15', 'b05', 'b40', null, 0, 0, 0, 0, 0, 0, 0, 0, 1);
    new Box('b25', 'b07', 'b50', null, 0, 0, 1, 1, 1, 0, 0, 0, 1);
    new Box('b26', 'b61', 'b55', null, 1, 0, 1, 0, 0, 0, 0, 1, 0);
    new Box('b35', 'b40', 'b01', null, 0, 0, 0, 0, 0, 0, 1, 0, 1);
    new Box('b40', 'b65', 'b60', null, 1, 0, 1, 1, 0, 0, 0, 0, 1);
    new Box('b50', 'bGo', 'b26', null, 1, 0, 1, 0, 0, 1, 0, 0, 1);
    new Box('b55', 'b15', null, 'b07', 0, 0, 0, 0, 0, 0, 0, 0, 1);
    new Box('b60', 'b25', null,  null, 1, 1, 0, 0, 1, 0, 0, 0, 1);
    new Box('b61', 'b01', null,  null, 0, 0, 1, 0, 0, 0, 0, 1, 0);
    new Box('b65', 'b75', null,  null, 0, 0, 0, 1, 1, 0, 0, 1, 1);
    new Box('b75', 'b01', 'b50', null, 0, 0, 0, 0, 0, 0, 0, 0, 1);
    new Box('bGo', null,  null,  null, 0, 0, 0, 0, 0, 1, 0, 0, 0);

    new Pather ('c01', 'b01', function (this_pen, other_pen) {
        return new Poise(this_pen, this_pen.box.id, (
            other_pen.box.has_red_text ||
            other_pen.box.has_green_text
        ) ? 'yes' : 'no'
        );
    });

    new Pather('c02', 'b02', function (this_pen, other_pen) {
        return new Poise(this_pen, this_pen.box.id, (
            other_pen.box.has_word_green ||
            other_pen.box.has_green_text
        ) ? 'yes' : 'no'
        );
    });

    new Pather('c05', 'b05', function (this_pen, other_pen) {
        return new Poise(this_pen, this_pen.box.id, (
            other_pen.box.has_word_red ||
            other_pen.box.has_word_green
        ) ? 'yes' : 'no'
        );
    });

    new Pather('c07', 'b07', function (this_pen, other_pen) {
        if (appl.wac.rule60state()) {
            return new Poise(this_pen, this_pen.box.id, 'yes');
        }
        return new Poise(this_pen, this_pen.box.id,
          (other_pen.box.is_even ? 'no' : 'yes'));
    });

        // Logically, a single pen can appear in cell 09, so a single
        // clicker is necessary for it, contrary to cell 55, which
        // needs two, one for each possible exit path.
    new Pather('c09', 'b09', function (this_pen, other_pen) {
        if (appl.wac.rule60state()) {
            return new Poise(this_pen, this_pen.box.id, 'yes');
        }
        return new Poise(this_pen, this_pen.box.id,
          (appl.wac.get_last_pen_moved() == other_pen.id ? 'yes' : 'no'));
    });

    new Pather('c15', 'b15', function (this_pen, other_pen) {
        return new Poise(this_pen, this_pen.box.id,
          (other_pen.box.divis_by_five ? 'yes' : 'no'));
    });

    new Pather('c25', 'b25', function (this_pen, other_pen) {
        if (appl.wac.rule60state()) {
            return new Poise(this_pen, this_pen.box.id, 'yes');
        }
        return new Poise(this_pen, this_pen.box.id, (
            other_pen.box.has_red_text ||
            other_pen.box.has_green_text
        ) ? 'yes' : 'no'
        );
    });

    new Pather('c26', 'b26', function (this_pen, other_pen) {
        if (appl.wac.rule60state()) {
            return new Poise(this_pen, this_pen.box.id, 'yes');
        }
        else if (other_pen.box.id == '55') {
            return new Poise(this_pen, this_pen.box.id, 'no');
        }
        var other_clicker;
        other_clicker = appl.boxes[other_pen.box.id].clicker;
        var other_poise = other_clicker.init_poise();
        return new Poise(this_pen, this_pen.box.id,
          (other_poise.PBP[0].path == 'no' ? 'yes' : 'no'));
    });

    new Pather('c35', 'b35', function (this_pen, other_pen) {
        return new Poise(this_pen, this_pen.box.id,
          (other_pen.box.has_word_word ? 'yes' : 'no'));
    });

    new Pather('c40', 'b40', function (this_pen, other_pen) {
        if (appl.wac.rule60state()) {
            return new Poise(this_pen, this_pen.box.id, 'yes');
        }
        return new Poise(this_pen, this_pen.box.id,
          (other_pen.box.has_green_text ? 'yes' : 'no'));
    });

    new Pather('c50', 'b50', function (this_pen, other_pen) {
        if (appl.wac.rule60state()) {
            return new Poise(this_pen, this_pen.box.id, 'yes');
        }
        return new Poise(this_pen, this_pen.box.id,
          (other_pen.box.refers_to_cows ? 'yes' : 'no'));
    });

    new Pather('c60', 'b60', function (this_pen, other_pen) {
        return new Poise(this_pen, this_pen.box.id, 'yes', 'rule60on');
    });

    new Pather('c61', 'b61', function (this_pen, other_pen) {
        if (appl.wac.rule60state()) {
            return new Poise(this_pen, this_pen.box.id, 'yes');
        }
        else {
            return new Poise(
                this_pen, this_pen.box.id, 'yes',
                other_pen, other_pen.box.id, 'yes'
            );
        }
    });

    new Pather('c65', 'b65', function (this_pen, other_pen) {
        return new Poise(this_pen, this_pen.box.id, 'yes', 'rule60off');
    });

    new Pather('c75', 'b75', function (this_pen, other_pen) {
        return new Poise(this_pen, this_pen.box.id,
          (other_pen.box.begins_with_if ? 'yes' : 'no'));
    });

    new Pather('c55yes', 'b55', function (this_pen, other_pen) {
        return new Poise(this_pen, this_pen.box.id, 'yes');
    });

    new Pather('c55lugnut', 'b55', function (this_pen, other_pen) {
        return new Poise(this_pen, this_pen.box.id, 'lugnut');
    });

        // Build the stylesheets.
    document.styleSheets[0].insertRule(
        'body {' +
          'background: ' + this.colors.wac_bg + ';' +
          'color: ' + this.colors.text + ';' +
          '}'
        , 0
    );

    document.styleSheets[0].insertRule(
        '.box {' +
          'position: absolute;' +
          'background: '      + this.colors.box_bg_no_pens  + ';' +
          'padding-bottom: '  + Box.padding_verti + 'px;' +
          'padding-top: '     + Box.padding_verti + 'px;' +
          'padding-left: '    + Box.padding_horiz + 'px;' +
          'padding-right: '   + Box.padding_horiz + 'px;' +
          'border-width: '    + Box.border_width  + 'px;' +
          'border-style: '    + Box.border_style  + ';' +
          'border-color: '    + this.colors.box_border_pens00 + ';' +
          'font-family: arial, helvetica, sans-serif;' +
          'font-size: medium;' +
          'z-index: 2;' +
          '}'
        , 0
    );

    document.styleSheets[0].insertRule(
        '.clicker {' +
          'background: transparent;' +
          'position: absolute;' +
          'padding-bottom: '  + Box.padding_verti + 'px;' +
          'padding-top: '     + Box.padding_verti + 'px;' +
          'padding-left: '    + Box.padding_horiz + 'px;' +
          'padding-right: '   + Box.padding_horiz + 'px;' +
          'z-index: 4;' +
          'border: solid;' +
          'border-width: ' + Box.border_width + 'px;' +
          'border-color: transparent;' +
          '}'
        , 0
    );

        // Load and adjust the arrows.
    if (this.use_arrows) {
        this.div_graphics.appendChild(arrows);
    }
    else {
        this.remove_arrows();
    }

        // To store the language dependent text strings.
    this.text = new Object;
    this.text.title = document.createTextNode("");
    var TITLE = document.getElementById('title');
    TITLE.appendChild(this.text.title);
    this.text.intro = document.createTextNode("");
    var INTRO = document.getElementById('intro');
    INTRO.appendChild(this.text.intro);

        // Build the control display elements.
    var LANG = document.getElementById('language');
    var lang_names = new Array('Français', 'English');
    for (j in this.lang_ids) {
        var i0 = document.createElement('span');
        i0.setAttribute('id', 'lang_' + this.lang_ids[j]);
        i0.setAttribute('onclick', 'appl.load_lang("' + this.lang_ids[j] + '")');
        LANG.appendChild(i0);
        var i0t = document.createTextNode(lang_names[j]);
        i0.appendChild(i0t);
        var inp = document.createElement('input');
        inp.setAttribute('type', 'radio');
        inp.setAttribute('id', 'lang_' + this.lang_ids[j] + '_radio');
        inp.setAttribute('name', 'language');
        inp.setAttribute('value', this.lang_ids[j]);
        i0.appendChild(inp);
    }
        // Last move indicator.
    this.text.last_move_desc = document.createTextNode("");
    var LMD = document.getElementById('last_move_desc');
    this.text.last_move_text = document.createTextNode("");
    var LMT = document.getElementById('last_move_text');
    LMD.insertBefore(this.text.last_move_desc, LMT);
    LMT.appendChild(this.text.last_move_text);
    LMD.style.marginLeft = '22px';
    LMT.style.paddingLeft = '2px';
    LMT.style.paddingRight = '2px';
    LMT.style.marginRight = '22px';
};

// -------------------------------------------------------------------
function Pather (clicker_id, box_id, exit_path_func) {
    appl.pathers[clicker_id] = this;
    this.clicker_id = clicker_id;
    this.box_id = box_id;
    this.exit_path_func = exit_path_func
}

// -------------------------------------------------------------------
Appl.prototype.set_up_langs = function () {
    for (var i in this.lang_ids) {
        var lang_id = this.lang_ids[i];
        this.langs[lang_id] = new Object;
            // Save and unload the initial bitmap texts.
        this.div_graphics.removeChild(
            this.langs[lang_id].bitmap_text
              = document.getElementById('bitmap_text_' + lang_id)
        );
            // Read in box properties.
        this.langs[lang_id].boxes = new Object;
        var BDPs = lang_data[lang_id].box_display_properties;
        var box_id;
        var box;
        for (box_id in BDPs) {
            box = this.langs[lang_id].boxes[box_id] = new Object;
            box.bdp = BDPs[box_id];
            var para = document.createElement('p');
            para.innerHTML = this.use_bitmap_text
                ? ''
                : '<span class="cell_num">' + box.bdp.cell_name
                  + '</span> ' + box.bdp.text;
            para.setAttribute('lang_id', box_id);
            para.setAttribute('class', 'box');
            para.style.width  = box.bdp.ltwh.w + 'px';
            para.style.height = box.bdp.ltwh.h + 'px';
            para.style.left   = box.bdp.ltwh.l + 'px';
            para.style.top    = box.bdp.ltwh.t + 'px';
            box.para = para;
        }

        var go = lang_data[lang_id].box_display_properties['bGo'];
        this.langs[lang_id].cow_box = new Object;
        this.langs[lang_id].cow_box.top  = go.ltwh.t + 15 + 'px';
        this.langs[lang_id].cow_box.left = go.ltwh.l + 13 + 'px';
            // Build the, language dependent, clicker buttons.
        this.langs[lang_id].clickers = new Object;
        for (var clicker_id in appl.pathers) {
            box = this.langs[lang_id].boxes[appl.pathers[clicker_id].box_id];
            var left  = box.bdp.ltwh.l;
            var width = box.bdp.ltwh.w;
            var upper_top = box.bdp.ltwh.t
            var height = (box.bdp.ltwh.h -
              (2 * (Box.border_width + Box.padding_verti))) / 2;
            var lower_top = upper_top + height +
              (2 * (Box.border_width + Box.padding_verti));
            if (clicker_id == 'c55yes' || clicker_id == 'c55lugnut') {
                    // This half-width hacking is necessary to have the clickers
                    // over boxes 55 positioned correctly.
                var B = this.langs[lang_id].boxes;
                var hw55 = (B.b55.bdp.ltwh.w -
                  (2 * (Box.border_width + Box.padding_horiz))) / 2;
                width = hw55;
                if (clicker_id == 'c55lugnut') {
                    left = B.b55.bdp.ltwh.l + hw55 +
                      (2 * (Box.border_width + Box.padding_horiz));
                }
            }
                // Build one for the top half, and one for the bottom.
            var foo = {T: upper_top, B: lower_top};
            for (var what in foo) {
                var button = document.createElement('p');
                button.setAttribute('id', this.id + what);
                button.setAttribute('class', 'clicker');
                button.style.left   = left      + 'px';
                button.style.top    = foo[what] + 'px';
                button.style.width  = width     + 'px';
                button.style.height = height    + 'px';
                button.box = box;
                var clicker = this.langs[lang_id].clickers[clicker_id + what]
                  = new Clicker(clicker_id, what, button);
                button.clicker = clicker;
                clicker.button = button;
            }
        }
    }
}

// -------------------------------------------------------------------
Appl.prototype.load_lang = function (lang_id) {
    body = document.getElementsByTagName('body')[0];
    body.style.visibility = 'hidden';
    this.unload_lang_curr();

        // Note the current language.
    this.lang_curr = lang_id;
    document.getElementById('lang_' + lang_id + '_radio').checked = true;
        // Set up the text nodes.
    document.getElementById('title').innerHTML = lang_data[lang_id].title;
    document.getElementById('intro').innerHTML = lang_data[lang_id].intro;
        // Load and adjust the bitmap text.
    if (this.use_bitmap_text) {
        this.div_graphics.appendChild(this.langs[lang_id].bitmap_text);
        var bitmap_text = document.getElementById('bitmap_text_' + lang_id);
        bitmap_text.style.top = lang_data[lang_id].bitmap_text_top + 'px';
        bitmap_text.style.left = lang_data[lang_id].bitmap_text_left + 'px';
    }
        // Set up the control element text nodes.
    change_text(lang_id, this.text.last_move_desc, 'last_move_desc', ': ');
    document.getElementById('restart').value = lang_data[lang_id].restart_desc;
        // Plug in and show the boxes.
    var b = this.langs[lang_id].boxes;
    for (m in b) {
        this.div_boxes.appendChild(b[m].para);
        this.boxes[m].para = b[m].para;
        this.boxes[m].highlight();
    }
    this.cow_box.style.top = this.langs[lang_id].cow_box.top;
    this.cow_box.style.left = this.langs[lang_id].cow_box.left;
        // Plug in and show the clickers.
    var CBs = this.langs[lang_id].clickers;
    for (clicker_id in CBs) {
        this.div_boxes.appendChild(CBs[clicker_id].button);
        var button = CBs[clicker_id].button;
        button.addEventListener('mouseover', mouseover_clicker, false);
        button.addEventListener('mouseout',  mouseout_clicker, false);
        button.addEventListener('mousedown', mousedown_clicker, false);
        button.addEventListener('mouseup',   mouseup_clicker, false);
    }
    body.style.visibility = 'visible';
};

// -------------------------------------------------------------------
function change_text(lang_id, text_node, lang_property, extra_text) {
    var new_text = lang_data[lang_id][lang_property];
    if (extra_text) {
        new_text += extra_text;
    }
    text_node.replaceData(0, text_node.data.length, new_text);
}

// -------------------------------------------------------------------
Appl.prototype.unload_lang_curr = function () {
    this.remove_bitmap_text();
   // this.remove_arrows();
    this.remove_boxes();
    this.lang_curr = null;
};

// -------------------------------------------------------------------
Appl.prototype.remove_boxes = function () {
    try {
        var high_ndx= this.div_boxes.childNodes.length;
        for (var i = high_ndx - 1; i >= 0; --i) {
            this.div_boxes.removeChild(this.div_boxes.childNodes[i]);
        }
    }
    catch (e) { }
}

// -------------------------------------------------------------------
Appl.prototype.remove_bitmap_text = function () {
    try {
        this.div_graphics.removeChild(this.langs[this.lang_curr].bitmap_text);
    }
    catch (e) { }
};

// -------------------------------------------------------------------
Appl.prototype.remove_arrows = function () {
    try {
        this.div_graphics.removeChild(arrows);
    }
    catch (e) { }
};

// -------------------------------------------------------------------
Appl.prototype.set_colors = function (foo) {
    if (foo) {
        this.colors.wac_bg            = '#00ff00';
        this.colors.text              = 'black';
        this.colors.box_bg_with_pens  = 'white';
        this.colors.box_bg_no_pens    = '#ccddee';
        this.colors.box_bg_rule60on_with_pens  = '#ccddee';
        this.colors.box_bg_rule60on_no_pens  = '#eeffff';
        this.colors.pen1              = 'white';
        this.colors.pen2              = 'white';
        this.colors.box_border_pens00 = '#bbbbbb';
        this.colors.box_border_pens10 = 'black';
        this.colors.box_border_pens02 = 'black';
        this.colors.box_border_pens12 = '#bbbbbb';
    }
    else {
        this.colors.wac_bg            = 'white';
        this.colors.text              = 'black';
        this.colors.box_bg_no_pens    = 'white';
        this.colors.box_bg_with_pens  = 'white';
        this.colors.box_bg_rule60on_no_pens  = 'white';
        this.colors.box_bg_rule60on_with_pens  = 'white';
        this.colors.pen1              = 'white';
        this.colors.pen2              = 'white';
        this.colors.box_border_pens00 = 'white';
        this.colors.box_border_pens10 = 'white';
        this.colors.box_border_pens02 = 'white';
        this.colors.box_border_pens12 = 'white';
    }
};

// -------------------------------------------------------------------
Appl.quote_word = function (word) {
    return '&#147;' + word + '&#148;';
};

Appl.quote_path = function (path) {
    return '<span style="font-style: italic">' + path + '</span>';
};

Appl.bold_text = function (text) {
    return '<span style="font-weight: bold">' + text + '</span>';
};

// -------------------------------------------------------------------
// To convert user screen units to pixels.

function LTWH (left, top, width, height) {
    var x_scale  = 21.5;
    var y_scale  = 21.5;
    this.l = left   * x_scale;
    this.t = top    * y_scale;
    this.w = width  * x_scale;
    this.h = height * y_scale;
    this.toString = function () {
        return 'LTWH: ' +
        this.l + ' ' +
        this.t + ' ' +
        this.w + ' ' +
        this.h;
    }
};

    // Arguments are already pixel values.
function LTWH_pixels (left, top, width, height) {
    this.l = left;
    this.t = top;
    this.w = width;
    this.h = height;
    this.toString = function () {
        return 'LTWH_pixels: ' +
        this.l + ' ' +
        this.t + ' ' +
        this.w + ' ' +
        this.h;
    }
};

// -------------------------------------------------------------------
function Wac () {
    var rule60on = false;
    Wac.prototype.rule60state = function () {
        return rule60on;
    };
    this.rule60state = function () {
        return rule60on;
    };
    this.toggle_rule60 = function () {
        this.set_rule60(! rule60on);
    };
    this.set_rule60 = function (bool) {
        rule60on = bool;
        appl.boxes.b60.highlight();
    };

    this.pens = new Pens();
    var last_pen_moved_ndx = null;

    this.get_last_pen_moved  = function () {
        return last_pen_moved_ndx;
    };

    this.set_last_pen_moved  = function (pen_ndx, src_box_id) {
        if (pen_ndx == null) pen_ndx = 0;
        if (src_box_id == null) src_box_id = '___';
        last_pen_moved_ndx = pen_ndx;
        var t = appl.text.last_move_text;
        var src_box_id_num = src_box_id.substr(1);
        t.replaceData(0, t.data.length, src_box_id_num);
    };
};

// -------------------------------------------------------------------
Wac.prototype.reset = function (
    pen1_box_id,
    pen2_box_id,
    last_pen_moved_ndx,
    rule60state
) {
    this.move_pen_to_box(1, pen1_box_id);
    this.move_pen_to_box(2, pen2_box_id);
    this.set_last_pen_moved();
    this.set_rule60(rule60state == null ? false : rule60state);
};

// -------------------------------------------------------------------
Wac.prototype.move_pen_to_box = function (pen_ndx, dst_box_id) {
    if (! (pen_ndx == 1 || pen_ndx == 2)) return;
    var pen = this.pens['pen' + pen_ndx];
        // Remove the pen from where it is.
    var src_box_id = '   ';
    if (pen != null && pen.box != null) {
        var box = pen.box;
        src_box_id = box.id;
        box.release_pen(pen_ndx);
    }
    this.pens['pen' + pen_ndx].box = null;;
        // Add pen to the destination box.
    var dst_box = appl.boxes[dst_box_id];
    dst_box.receive_pen(pen_ndx);
    this.pens['pen' + pen_ndx].box = dst_box;
    this.set_last_pen_moved(pen_ndx, src_box_id);
};

// -------------------------------------------------------------------
function Pen (id, pens) {
    this.id = id;
    this.box = null;
    this.pens = pens;
    this.toString = function () { return 'Pen ' + id };
}

// -------------------------------------------------------------------
function Pens (appl) {
    this.pen = new Array();
        // Skip index 0 to allow representing 'none' (or something).
    this.pen1 = new Pen(1, this);
    this.pen2 = new Pen(2, this);
    this.toString = function () { return 'pens ' + this.pen1};
}

// -------------------------------------------------------------------
function Box (
    box_id,
    yes_box_id,
    no_box_id,
    lugnut_box_id,
    is_even,
    has_green_text,
    has_red_text,
    has_word_green,
    has_word_red,
    refers_to_cows,
    has_word_word,
    begins_with_if,
    divis_by_five
) {
    appl.boxes[box_id] = this;
    this.id = box_id;
    this.nb_pens_held = 0;
    this.pens_held = new Array;
        // Hold null or a ref to a Pen.
    this.pens_held[1] = null;
    this.pens_held[2] = null;
    this.path = new Array;
    this.path['yes']    = yes_box_id;
    this.path['no']     = no_box_id;
    this.path['lugnut'] = lugnut_box_id;
    this.is_even        = is_even == 1;
    this.has_green_text = has_green_text == 1;
    this.has_red_text   = has_red_text == 1;
    this.has_word_green = has_word_green == 1;
    this.has_word_red   = has_word_red == 1;
    this.refers_to_cows = refers_to_cows == 1;
    this.has_word_word  = has_word_word == 1;
    this.begins_with_if = begins_with_if == 1;
    this.divis_by_five  = divis_by_five == 1;
};

// -------------------------------------------------------------------
    // Cell heights in user screen units, according to the number of
    // lines they hold.
Box.L2  = 1.6;
Box.L3  = 2.34;
Box.L4  = 3.1;
Box.L5  = 3.8;
Box.L6  = 4.4;
Box.L7  = 5.2;
Box.L8  = 6.0;
Box.L9  = 6.7;
Box.L10 = 7.4;
Box.padding_horiz = 5;
Box.padding_verti = 1;
Box.border_width  = 3;
Box.border_style  = 'solid';

Box.prototype.receive_pen = function (pen_ndx) {
    this.pens_held[pen_ndx] = appl.wac.pens['pen' + pen_ndx];
    ++this.nb_pens_held;
    this.highlight();
};

Box.prototype.release_pen = function (pen_ndx) {
    this.pens_held[pen_ndx] = null;
    --this.nb_pens_held;
    this.highlight();
};

Box.prototype.get_box_id_from_path = function (path) {
    return appl.boxes[this.path[path]].id;
};

Box.prototype.highlight = function () {
    var NPH = this.nb_pens_held;
        // Set the border color.
    var border_color = appl.colors.box_border_pens00;
    if (NPH == 1) {
        border_color = this.pens_held[1]
            ? appl.colors.box_border_pens10
            : appl.colors.box_border_pens02;
    }
    else if (NPH == 2) {
        border_color = appl.colors.box_border_pens12;
    }
    this.para.style.borderColor = border_color;
        // Set the background color (or image, in case of reached goal cell).
    if (this.id == 'bGo') {
        var cows = document.getElementById('cows');
        if (NPH == 0) {
            cows.src = 'lang.' + appl.lang_curr + '.goal.png';
        }
        else if (NPH == 2) {
            cows.src = 'cow.both.png';
        }
        else if (this.pens_held[1]) {
            cows.src = 'cow.mage.png';
        }
        else {
            cows.src = 'cow.cyan.png';
        }
        cows.z_index = 6;
        return;
    }
    var bg_color;
    bg_color = (NPH == 0)
        ? appl.colors.box_bg_no_pens
        : appl.colors.box_bg_with_pens;
    if (this.id == 'b60' && appl.wac.rule60state()) {
        bg_color = (NPH == 0)
            ? appl.colors.box_bg_rule60on_no_pens
            : appl.colors.box_bg_rule60on_with_pens;
    }
    this.para.style.background = bg_color;
};

// -------------------------------------------------------------------
function Clicker (clicker_id, top_or_bottom, button) {
    this.id = clicker_id;
    this.top_or_bottom = top_or_bottom;
    this.box = appl.boxes[appl.pathers[clicker_id].box_id];
    appl.clickers[clicker_id] = this;
    appl.boxes[this.box.id].clicker = this;
};

Clicker.has_mouseover = null;
Clicker.has_mousedown = null;

function mouseover_clicker () {
    Clicker.has_mousedown = null;
    Clicker.has_mouseover = this.clicker;
    this.clicker.enter();
};

function mouseout_clicker  () {
    Clicker.has_mousedown = null;
    Clicker.has_mouseover = null;
    this.clicker.leave();
};

function mousedown_clicker  () {
    Clicker.has_mousedown = this.clicker;
};

function mouseup_clicker () {
    if (
        ! Clicker.has_mousedown ||
        Clicker.has_mousedown !== this.clicker
    ) return;
    Clicker.has_mousedown = null;
    Clicker.has_mouseover = null;
    this.clicker.select();
    if (this.clicker.box.nb_pens_held != 0) {
        this.clicker.init_poise();
    }
};

Clicker.prototype.enter = function () {
   // this.button.style.background = 'red';
    if (this.box.nb_pens_held == 2) {
        if (this.top_or_bottom == 'T') {
            this.box.para.style.borderColor = appl.colors.box_border_pens10;
        }
        else {
            this.box.para.style.borderColor = appl.colors.box_border_pens02;
        }
    }
    this.init_poise();
    this.poise.show();
};

Clicker.prototype.leave = function () {
    this.button.style.background = 'transparent';
    if (this.box.nb_pens_held == 2) {
        this.box.para.style.borderColor = appl.colors.box_border_pens12;
    }
    this.poise.hide();
};

Clicker.prototype.select = function () {
   // this.button.style.background = 'blue';
    this.poise.hide();
    this.poise.execute();
};

// -------------------------------------------------------------------
Clicker.prototype.init_poise = function () {
    // Calculates self's Poise object, in this.poise and returns a
    // copy.

        // Which pens are involved?
    var this_pen = null;

    if (this.box.nb_pens_held == 2) {
        if (this.top_or_bottom == 'T') {
            this_pen = appl.wac.pens['pen1'];
        }
        else {
            this_pen = appl.wac.pens['pen2'];
        }
    }
    else {
        if (this.box.pens_held[1] !== null) {
            this_pen = this.box.pens_held[1];
        }
        else if (this.box.pens_held[2] !== null) {
            this_pen = this.box.pens_held[2];
        }
        else {
            return this.poise = new Poise();
        }
    }
    var other_pen = appl.wac.other_pen(this_pen);
    return this.poise = appl.pathers[this.id].exit_path_func(this_pen, other_pen);
};

// -------------------------------------------------------------------
Wac.prototype.other_pen = function (pen) {
    if (pen === this.pens.pen1) {
        return this.pens.pen2;
    }
    else if (pen === this.pens.pen2) {
        return this.pens.pen1;
    }
    else {
        return null;
    }
}

// -------------------------------------------------------------------
function Poise () {
        // PenBoxPath`s holder.
    this.PBP = new Array();
    this.turn_rule60 = '';
    for (var i = 0; i < arguments.length; ++i) {
        var val = arguments[i];
        if (val == 'rule60on') {
            if (! appl.wac.rule60state()) {
                this.turn_rule60 = 'on';
            }
        }
        else if (val == 'rule60off') {
            if (appl.wac.rule60state()) {
                this.turn_rule60 = 'off';
            }
        }
        else {
            var pen = val;
            var box_id = arguments[++i];
            var path = arguments[++i];
            this.PBP.push(new PenBoxPath(pen, box_id, path));
        }
    }
}

Poise.prototype.show = function () {
    return;
    for (i in this.PBP) {
        var PBP = this.PBP[i];
        var src_path = PBP.path;
        var src_box_id = PBP.box_id;
        var src_pen = PBP.pen;
        var dst_box = src_box.get_box_id_from_path(src_path);
        var dst_box_pen = dst_box.pen;
        var box_poised_color = (PBP.pen === appl.wac.pen1)
            ? Display.box_poised_pen1
            : Display.box_poised_pen2;
        dst_box.para.style.background = box_poised_color;
    }
};

Poise.prototype.hide = function () {
    return;
    for (i in this.PBP) {
        var src_path = this.PBP[i].path;
        var src_box = this.PBP[i].box_id;
        src_box.get_box_id_from_path(src_path).para.style.background
          = Display.box_background_no_pens;
    }
};

Poise.prototype.execute = function () {
    if (this.turn_rule60 == 'on') {
        appl.wac.set_rule60(true);
    }
    else if (this.turn_rule60 == 'off') {
        appl.wac.set_rule60(false);
    }
    for (i in this.PBP) {
        var PBP = this.PBP[i];
        var src_pen = PBP.pen;
        var src_box = appl.boxes[PBP.box_id];
        var src_path =PBP.path;
        var dst_box_id = src_box.get_box_id_from_path(src_path);
        appl.wac.move_pen_to_box(src_pen.id, dst_box_id);
    }
};

function PenBoxPath (pen, box_id, path) {
    this.pen = pen;
    this.box_id = box_id;
    this.path = path;
}

// -------------------------------------------------------------------
function BoxDispProps (
    cell_name,
    left, top, width, height,
    text
) {
    this.cell_name = cell_name;
    this.ltwh = new LTWH(left, top, width, height);
    this.text = text;
}

// -------------------------------------------------------------------
lang_data.fr = {
    title : "Où sont les vaches?",
    intro :
      "Dans ce labyrinthe (adapté d'un original de <a " +
      'href="http://www.logicmazes.com/super.html">Robert Abbott</a>), ' +
      "lorsque vous cliquez l'une ou l'autre des deux cellules en évidence, " +
      "ses instructions sont examinées et déterminent lorsqu'appliquées " +
      "le chemin par lequel vous quitterez la cellule. " +
      "Les flèches vertes représentent " +
      'les chemins ' + Appl.quote_path("oui") + ', et les rouges, ' +
      'les chemins ' + Appl.quote_path("non") + '. ' +
      "Votre objectif est d'atteindre la cellule " + Appl.bold_text("But") +
      ". Notez que les instructions des cellules 60 et 65 commencent à " +
      "s'appliquer seulement quand vous quittez la cellule, pas quand " +
      "vous y entrez. " +
      "Notez aussi que lorsque les cellules actives se chevauchent, " +
      "l'«autre cellule» se trouve être au même endroit. " +
      " (Postez vos questions ou vos commentaires à " +
      "<a href=\"mailto:lucs@pobox.com\"> Luc St-Louis</a>.)"
    ,
    last_move_desc  : "Dernier coup",
    restart_desc  : "Recommencer",
    bitmap_text_top : 13,
    bitmap_text_left : 14,

    box_display_properties : {

      b01 : new BoxDispProps("01", 0, 0, 7.0, Box.L3,
        "L'autre cellule contient-elle " +
        "du texte rouge ou du texte vert?"
      ),

      b02 : new BoxDispProps("02", 10, 0, 6.37, Box.L3,
        "L'autre cellule contient-elle " +
        "du texte vert ou le mot " + Appl.quote_word("vert") + "?"
      ),

      b05 : new BoxDispProps("05", 18.9, 0.7, 7.25, Box.L3,
        "L'autre cellule contient-elle " +
        "le mot " + Appl.quote_word("rouge") + " ou le mot " +
        Appl.quote_word("vert") + "?"
      ),

      b07 : new BoxDispProps("07", 30, 2.2, 5.45, Box.L3,
        "L'autre cellule porte-t-elle " +
        'un <span style="color: red">numéro</span> impair?'
      ),

      b09 : new BoxDispProps('09', 1.4, 4.3, 4.50, Box.L3,
        '<span style="color: red">Venez-vous</span> de quitter ' +
        'l\'autre cellule?'
      ),

      b15 : new BoxDispProps('15', 11.70, 4.3, 5.10, Box.L4,
        "L'autre cellule porte-t-elle un " +
        'numéro qui est un multiple de cinq?'
      ),

      b25 : new BoxDispProps('25', 22, 7.6, 6.90, Box.L3,
        'L\'autre cellule contient-elle du texte rouge ' +
        'ou du <span style="color: red">texte vert?</span>'
      ),

      b26 : new BoxDispProps('26', 30.6, 10, 5.6, Box.L4,
        'Si vous quittiez l\'autre cellule, '
        + '<span style="color:red">quitteriez-vous</span> par ' +
        ' un chemin ' + Appl.quote_path("non") + '?'
      ),

      b35 : new BoxDispProps('35', 2, 11.9, 5.32, Box.L3,
        'L\'autre cellule contient-elle le mot ' + Appl.quote_word('mot') + '?'
      ),

      b40 : new BoxDispProps('40', 4.5, 7.8, 4.37, Box.L3,
        'Y a-t-il du <span style="color: red">texte vert</span> dans cette cellule?'
      ),

      b50 : new BoxDispProps('50', 21.3, 11.7, 5.00, Box.L3,
        'L\'autre cellule réfère-t-elle à des ' +
        '<span style="color: red">vaches?</span>'
      ),

      b55 : new BoxDispProps('55', 21, 4.2, 6.2, Box.L3,
        'Quittez par le chemin ' + Appl.quote_path("oui") + ' ou par le ' +
        'chemin violet.'
      ),

      b60 : new BoxDispProps('60', 11.4, 9.1, 8.25, Box.L6,
        "Dorénavant, " +
        'si vous quittez une cellule ' +
        'contenant du <span style="color: green">texte ' +
        'rouge</span>, ses instructions ne seront pas appliquées, ' +
        'et vous la quitterez par son chemin ' + Appl.quote_path("oui") + '.'
      ),

      b61 : new BoxDispProps('61', 30, 16.4, 5.50, Box.L5,
        'Si vous quittez cette cellule, ' +
        'vous quitterez <span style="color: red">aussi</span> ' +
        'l\'autre, par son chemin ' + Appl.quote_path("oui") + '.'
      ),

      b65 : new BoxDispProps('65', 2, 15.2, 7.60, Box.L5,
        'Si dorénavant vous quittez une cellule contenant du texte rouge ' +
        '(pas vert), ses instructions seront appliquées.'
      ),

      b75 : new BoxDispProps('75', 12.8, 15.3, 6.15, Box.L3,
        'Le texte de l\'autre cellule commence-t-il par ' +
        Appl.quote_word('Si') + '?'
      ),

      bGo : new BoxDispProps('But', 22.6, 16, 5, 3.5, '')

    }
  };

// -------------------------------------------------------------------
lang_data.en = {
    title : 'Where Are The Cows?',
    intro :
      "In this maze (adapted from <a " +
      'href="http://www.logicmazes.com/super.html">Robert Abbott</a>\'s original), ' +
      "when you click on either of the two highlighted cells, " +
      "its instructions are examined and determine, when applied, the path by which " +
      "you will leave the cell. The green arrows represent " +
      Appl.quote_path("yes") + ' paths, and the red ones, ' +
      Appl.quote_path("no") + ' paths. ' +
      "Your task is to reach the " + Appl.bold_text("Goal") + " cell. " +
      "Note that the instructions in cells 60 and 65 begin to apply " +
      "only when you leave the cell, not when you enter it. " +
      "Also, when the two cells overlap, the \"other cell\" turns out " +
      "to refer to the same location. " +
      "(Mail your questions or comments to <a href=\"mailto:lucs@pobox.com\"> Luc St-Louis</a>.)"
    ,
    last_move_desc  : 'Last move',
    restart_desc  : "Restart",
    bitmap_text_top : 12,
    bitmap_text_left : 14,

    box_display_properties : {

      b01 : new BoxDispProps("01", 0, 0, 6.0, Box.L3,
        'Does the other cell ' +
        'have red text or green text?'
      ),

      b02 : new BoxDispProps("02", 10, 0, 6.37, Box.L3,
        'Does the other cell have ' +
        'green text or the word "green"?'
      ),

      b05 : new BoxDispProps("05", 18.9, 0.7, 7.20, Box.L3,
        'Does the other cell have the ' +
        'word ' + Appl.quote_word('red') + ' or the word ' +
        Appl.quote_word('green') + '?'
      ),

      b07 : new BoxDispProps("07", 30, 2.2, 4.65, Box.L3,
        'Is the other cell\'s <span style="color: red">number</span> odd?'
      ),

      b09 : new BoxDispProps('09', 1.4, 4.3, 4.50, Box.L3,
        'Did you <span style="color: red">just leave</span> the other cell?'
      ),

      b15 : new BoxDispProps('15', 12.40, 4.3, 4.40, Box.L4,
        'Is the other cell\'s number ' +
        'evenly divisible by five?'
      ),

      b25 : new BoxDispProps('25', 22, 7.6, 6.90, Box.L3,
        'Does the other cell have ' +
        'red text or <span style="color: red">green text?</span>'
      ),

      b26 : new BoxDispProps('26', 30.6, 10, 5.0, Box.L4,
        'If you left the other cell, ' +
        'would you <span style="color: red">leave</span> along a ' +
        Appl.quote_path("no") + ' path?'
      ),

      b35 : new BoxDispProps('35', 2, 11.9, 5.32, Box.L3,
        'Does the other cell have the word ' + Appl.quote_word('word') + '?'
      ),

      b40 : new BoxDispProps('40', 4.5, 7.8, 4.37, Box.L3,
        'Does this cell have <span style="color: red">green text</span>?'
      ),

      b50 : new BoxDispProps('50', 21.3, 11.7, 4.25, Box.L3,
        'Does the other cell refer to <span style="color: red">cows</span>?'
      ),

      b55 : new BoxDispProps('55', 20.7, 4.2, 6.5, Box.L3,
        'Leave this cell along either its ' + Appl.quote_path('yes') +
        ' path or its purple path.'
      ),

      b60 : new BoxDispProps('60', 11.4, 9.1, 7.75, Box.L5,
        'Until further notice, if you leave a cell that has ' +
        '<span style="color: green">red text</span>, ' +
        'its instructions will be ignored, and you will leave along its ' +
        Appl.quote_path("yes") + ' path.'
      ),

      b61 : new BoxDispProps('61', 30, 16.4, 5.00, Box.L5,
        'If you leave this cell, you will also ' +
        'leave the <span style="color: red">other</span> ' +
        'one, along its ' +
        Appl.quote_path("yes") + ' path.'
      ),

      b65 : new BoxDispProps('65', 2, 15.2, 7.63, Box.L4,
        'If you leave a cell that has red text (not green), until further ' +
        'notice, its instructions will be applied.'
      ),

      b75 : new BoxDispProps('75', 12.8, 15.3, 5.25, Box.L3,
        'Does the other cell\'s text begin with ' +
        Appl.quote_word('If') + '?'
      ),

      bGo : new BoxDispProps('Goal', 22.6, 16, 5, 3.5, '')

    }
  };

