/**********************************
* author: Mario Balibrera
*    web: mariobalibrera.com
*  email: mario.balibrera@gmail.com
**********************************/

js.io.require('js.io.protocols.mics');
js.io.require('js.io.tools.stripper');

OPCOL = {'white':'black','black':'white'};

function Clock() {
    var WHITE = document.getElementById('white');
    var BLACK = document.getElementById('black');
    var self = this;
    var on = false;
    var increment = null;
    var timeout_cb = null;
    var timer = null;
    var time = {'white':300,'black':300}
    var turn = 'white';
    var enemy = 'black';
    var display = function() {
        WHITE.innerHTML = self.minutize(time['white']);
        BLACK.innerHTML = self.minutize(time['black']);
    }
    var update = function() {
        time[turn] -= 1;
        display();
        if (turn == enemy && time[turn] < 0) {
            timeout_cb();
        }
    }
    var start = function() {
        on = true;
        timer = setInterval(update, 1000);
    }
    self.minutize = function(s) {
        var m = 0;
        while (s > 59) {
            s -= 60;
            m += 1;
        }
        if (s < 10) {
            if (s < 0) {
                s = '0';
            }
            s = '0'+s;
        }
        return m+':'+s;
    }
    self.set_timeout_cb = function(cb) {
        timeout_cb = cb;
    }
    self.set = function(initial, inc, color) {
        turn = 'white';
        enemy = OPCOL[color];
        time['white'] = initial;
        time['black'] = initial;
        increment = inc;
        display();
    }
    self.stop = function() {
        on = false;
        clearInterval(timer);
    }
    self.change = function() {
        turn = OPCOL[turn];
        if (! on) { start(); }
    }
    self.sync = function(w, b) {
        time['white'] = Math.round(w);
        time['black'] = Math.round(b);
    }
}

function Board() {
    var LETTERS = 'abcdefgh';
    var BOARD = document.getElementById('board');
    var BLACK = '#744528';
    var WHITE = '#cca655';
    var CASTLESQUARES = {
        "white": {"g1": "f1", "c1": "d1"},
        "black": {"g8": "f8", "c8": "d8"}
    }
    var PROMOTIONS = {
    'white': {
            'Q':'&#9813;',
            'R':'&#9814;',
            'B':'&#9815;',
            'N':'&#9816;'
        },
    'black': {
            'Q':'&#9819;',
            'R':'&#9820;',
            'B':'&#9821;',
            'N':'&#9822;'
        }
    }
    var PIECES = {
    'white': [9812,9813,9814,9815,9816,9817],
    'black': [9818,9819,9820,9821,9822,9823]
    }
    var START = [
              ['&#9820;','&#9822;','&#9821;','&#9819;','&#9818;','&#9821;','&#9822;','&#9820;'],
              ['&#9823;','&#9823;','&#9823;','&#9823;','&#9823;','&#9823;','&#9823;','&#9823;'],
              ['','','','','','','',''],
              ['','','','','','','',''],
              ['','','','','','','',''],
              ['','','','','','','',''],
              ['&#9817;','&#9817;','&#9817;','&#9817;','&#9817;','&#9817;','&#9817;','&#9817;'],
              ['&#9814;','&#9816;','&#9815;','&#9813;','&#9812;','&#9815;','&#9816;','&#9814;']]
    var self = this;
    var color = null;
    var move_cb = null;
    var rooks = null;
    var squares = {};
    var click_one = null;
    var turn = null;
    var get_coordinates = function(row,col) {
        if (color=='white') {
            return LETTERS.charAt(col) + (8-row);
        }
        else {
            return LETTERS.charAt(7-col) + (row+1);
        }
    }
    self.is_rook = function(square_id) {
        var p = squares[square_id].innerHTML.charCodeAt(0);
        return p == 9814 || p == 9820;
    }
    self.is_pawn = function(square_id) {
        var p = squares[square_id].innerHTML.charCodeAt(0);
        return p == 9817 || p == 9823;
    }
    self.is_king = function(square_id) {
        var p = squares[square_id].innerHTML.charCodeAt(0);
        return p == 9812 || p == 9818;
    }
    self.set_move_cb = function(cb) {
        move_cb = cb;
    }
    self.reset = function(c, lineup) {
        var B = [];
        if (lineup) {
            for (var i = 1; i < 7; i++)
                B.push(START[i]);
            var line1 = []; // black
            var line2 = []; // white
            for (var i = 0; i < 8; i++) {
                var letter = lineup.charAt(i);
                if (letter == "K") {
                    line1.push('&#9818;');
                    line2.push('&#9812;');
                }
                else {
                    line1.push(PROMOTIONS['black'][letter]);
                    line2.push(PROMOTIONS['white'][letter]);
                }
            }
            B.unshift(line1);
            B.push(line2);
        }
        else
            B = START;
        color = c;
        turn = 'white';
        rooks = {'d1':null, 'd8':null, 'f1':null, 'f8':null};
        BOARD.innerHTML = '';
        for (var x=0; x<8; x++) {
            var r = document.createElement('tr');
            for (var y=0; y<8; y++) {
                if (B[x][y] == '&#9820;') {
                    var rook = LETTERS.charAt(y);
                    if (rooks.d1 == null) {
                        rooks.d1 = rook + "1";
                        rooks.d8 = rook + "8";
                    }
                    else {
                        rooks.f1 = rook + "1";
                        rooks.f8 = rook + "8";
                    }
                }
                var d = document.createElement('td');
                if (color == 'white') {
                    d.innerHTML = B[x][y];
                }
                else {
                    d.innerHTML = B[7-x][7-y];
                }
                if ((x+y)%2) {
                    d.class = BLACK;
                }
                else {
                    d.class = WHITE;
                }
                d.style.background = d.class;
                var coordinates = get_coordinates(x,y);
                d.id = coordinates;
                squares[coordinates] = d;
                d.onclick = Function("chess.click_event('"+coordinates+"')");
                r.appendChild(d);
            }
            BOARD.appendChild(r);
        }
    }
    self.click_event = function(square_id) {
        var square = squares[square_id];
        var piece = square.innerHTML.charCodeAt(0);
        if (click_one) {
            if (PIECES[color].indexOf(piece) == -1) {
                move_cb(click_one.id, square_id);
            }
            click_one.style.background = click_one.class;
            click_one = null;
        }
        else if (PIECES[color].indexOf(piece) != -1) {
            square.style.background = "green";
            click_one = square;
        }
    }
    self.move = function(f, t, p) {
        var f0 = f.charAt(0);
        var f1 = f.charAt(1);
        var t0 = t.charAt(0);
        var t1 = t.charAt(1);
        if (self.is_pawn(f)) {
            if (p) { // promotion
                squares[t].innerHTML = PROMOTIONS[turn][p];
            }
            else { // en passant
                if (f0 != t0 && ! squares[t].innerHTML) {
                    squares[t0+f1].innerHTML = '';
                }
                squares[t].innerHTML = squares[f].innerHTML;
            }
        }
        else {
            squares[t].innerHTML = squares[f].innerHTML;
        }
        if (self.is_rook(t))
            rooks[f] = null;
        else if (self.is_king(t)) {
            // check for castle (including 960)
            if (t in CASTLESQUARES[turn]) {
                var rkey = CASTLESQUARES[turn][t];
                if (rooks[rkey] != null) {
                    squares[rkey].innerHTML = squares[rooks[rkey]].innerHTML;
                    squares[rooks[rkey]].innerHTML = '';
                }
            }
            // king has moved - no further castling allowed!
            for (var rkey in CASTLESQUARES[turn])
                rooks[CASTLESQUARES[turn][rkey]] = null;
        }
        squares[f].innerHTML = '';
        turn = OPCOL[turn];
    }
}

function Chess() {
    var OUTPUT = document.getElementById('output');
    var INPUT = document.getElementById('input');
    var ENTER_KEY = 13;
    var INITIAL_TIMES = ['2','5','10','20'];
    var INCREMENT_TIMES = ['0','2','5','12'];
    var self = this;
    var name = null;
    var curr_move = [null, null, null];
    var color = 'white';
    var stripper = new js.io.tools.stripper.Stripper();
    var client = new js.io.protocols.mics.Client();
    var clock = new Clock();
    board = new Board();
    var new_game = function(c, ini, inc, lineup) {
        color = c;
        board.reset(color, lineup);
        clock.set(ini, inc, color);
        notice('new game found');
        notice('you are '+color);
    }
    var gameover = function(outcome, reason) {
        clock.stop();
        notice('game over');
        notice(outcome+" ("+reason+")");
    }
    var list = function(n, ini, inc) {
        notice(n+" seeks "+clock.minutize(ini)+" "+inc+" game");
    }
    var output = function(data) {
        OUTPUT.appendChild(data);
        OUTPUT.scrollTop = OUTPUT.scrollHeight;
    }
    var post_chat = function(n, m) {
        var d = document.createElement('div');
        d.className = 'chat';
        d.innerHTML = "<b>"+n+":</b> "+m;
        output(d);
    }
    var notice = function(n) {
        var d = document.createElement('div');
        d.className = 'notice';
        d.innerHTML = n;
        output(d);
    }
    var move = function(f, t) {
        p = null;
        if ((t[1] == '1' || t[1] == '8') && board.is_pawn(f)) {
            var p = null;
            while (p != 'Q' && p != 'R' && p != 'B' && p != 'N') {
                p = prompt("Promote to which piece? (Q, R, B, N)")
                if (p) { p = p[0].toUpperCase(); }
            }
        }
        curr_move = [f, t, p];
        client.move(f, t, p);
    }
    var chat = function(e) {
        e = e || window.event;
        var str = INPUT.value;
        if (name && str && e.keyCode == ENTER_KEY) {
            post_chat(name, str);
            client.chat(str);
            INPUT.value = "";
        }
    }
    var recv_draw = function() {
        notice('your opponent offers a draw');
    }
    var move_piece = function(f, t, p) {
        clock.change();
        board.move(f, t, p);
    }
    var conf = function() {
        clock.change();
        board.move(curr_move[0], curr_move[1], curr_move[2]);
    }
    self.click_event = function(square_id) {
        board.click_event(square_id);
    }
    self.initialize = function() {
        document.onkeydown = chat;
        board.set_move_cb(move);
        board.reset(color)
        clock.set_timeout_cb(client.timeout);
        clock.set(300);
        client.set_cb('chat', post_chat);
        client.set_cb('confirm', conf);
        client.set_cb('draw', recv_draw);
        client.set_cb('game', new_game);
        client.set_cb('gameover', gameover);
        client.set_cb('list', list);
        client.set_cb('move', move_piece);
        client.set_cb('notice', notice);
        client.set_cb('time', clock.sync);
        client.connect('localhost', 7777);
    }
    self.new_seek = function() {
        if (! name) {
            name = stripper.strip(stripper.alpha(prompt("Enter name"),' '));
            if (! name) {
                return alert('invalid name');
            }
        }
        var initial = prompt("Select initial time (choices: 2, 5, 10, 20)");
        if (INITIAL_TIMES.indexOf(initial) == -1) {
            return alert('invalid initial time');
        }
        var increment = prompt("Select time increment (choices: 0, 2, 5, 12)");
        if (INCREMENT_TIMES.indexOf(increment) == -1) {
            return alert('invalid time increment');
        }
        var variant = prompt("Select chess variant (choices: 'standard', '960')");
        if (variant != '960')
            variant = 'standard';
        client.seek(name, parseInt(initial)*60, increment, variant);
    }
    self.draw = function() {
        notice('you offer a draw');
        client.draw();
    }
    self.forfeit = function() {
        notice('you forfeit');
        client.forfeit();
    }
}

function play() {
    chess = new Chess();
    chess.initialize();
}

function quit() {
    chess.forfeit();
}

