var myeng = new qengine();
var qont;

var FROM;
var PHASE = 0;
var LAST_POS = null;
var LAST_POS_MOVE;
var STATS = {
    min   : null,              // minimum time
    max   : null,              // maximum time
    avg   : 0,                 // average time
    all   : 0,                 // all time
    nodes : 0,                 // searched nodes
    calls : -1                  // number of calls
};

function initGame(dom) {
    qont = $('#' + dom); //document.getElementById("container");
    var htm = ['<p class="boardnav">'];
    htm.push('<button title="Reverse board">&uarr;&darr;</button> ');
    htm.push('<button title="Force the engine to move in this position" onclick="goMove()">Go!</button> ');
    htm.push('<span title="set the minimum depth search">depth:</span><input size="1" value="' + max_DEPTH + '"/> ');
    htm.push('<button title="Save position as FEN">FEN</button> ');
    htm.push('<img src="/static/left1.gif" alt="Prev" onclick="prevMove();"/> ');
    htm.push('<img src="/static/right1.gif" alt="Next" onclick="nextMove();"/>');
    htm.push('  <a href="">Help</a>           <span id="think" style="display:none"><img src="/static/spinner.gif" alt="think"/></span>');
    htm.push('</p>');
    
    htm.push('<div style="position:relative">');
    htm.push('<div class="ChessBoard">');
    htm.push('<div class="ChessFields">');
    for (var row = 0; row < 8; row++) {
        for (var col = 0; col < 8; col++) {
            var colID = String.fromCharCode(65 + col); // A, B, C, ..., H
            var rowID = row + 1; // 1, 2, 3, ..., 8
            var id = colID + rowID;
            var fieldColor = (row + col) & 1 ? "WhiteField" : "BlackField";
            htm.push("<div chessField='", id, "' class='Field ", id, " Row-", rowID, " Col-", colID, " ", fieldColor, "'></div>");
        }
    }
    for (var i = 0; i < 8; i++) {
        rowID = i + 1;
        colID = String.fromCharCode(65 + i);
        htm.push("<div class='Notation-Row Row-", rowID, "'>", rowID, "</div>");
        htm.push("<div class='Notation-Col Col-", colID, "'>", colID.toLowerCase(), "</div>");
    }
    htm.push('</div></div><div class="moves"></div><div class="stats"></div>');
    htm.push('<div class="setposition"><p>FEN:<input type="text" value="" size="32"/> <button onclick="fenToPos();">Set position</button> <span style="font-size:0.9em">to your FEN</span></p></div>');
    htm.push('<div class="statsorfen"></div></div>');
    qont.html(htm.join(''));
    
    initPos(FEN_STD);
    myeng.setBOARD(FEN_STD);
    updateStats(0.1);
    
// add to 'statsorfen' INPUT FEN     

    $('p button:first', qont).click( function() {
                                         $('.ChessFields', qont).toggleClass("ReverseFields");
                                         return false;
                                     });
    $('p input:first', qont).change( function() { max_DEPTH = this.value; });
    $('p button:contains(FEN)', qont).click( 
        function() {
            //var htm = higFEN(); // highlight FEN
            var fen = myeng.getFEN();
            var stfen = $('.statsorfen', qont);
            var lastfen = stfen.find('p:last a').text();
            if(fen != lastfen) { 
                var htm = '<a href="" class="TF-Link-r" onclick="initPos(\'' + fen + '\');return false;">' + fen + '</a>  ';
                stfen.append($('<p></p>').html(htm));
            }
            return false;
        });

    $('.ChessFields', qont).click( 
        function(ev) {
            if(PHASE == 2) return false; //thinking
            var div = ev.target;
            var field = div.getAttribute("chessField"); 
            
            if (!field) {
                div = div.parentNode;
                field = div.getAttribute("chessField");
            }
            if(!field) return false;
            var index = (field.charCodeAt(0) - 65) + 16 * (field.charCodeAt(1) - 49);
            if (PHASE == 0 || PHASE == 3) {
                FROM = index;
                PHASE++;
                $(div).addClass("Start");
            } else {
                var move = (FROM << 8) | index; 
                $('a.oneMoveSel', div.moves).removeClass("oneMoveSel");
                if (PHASE == 4) {
                    return false;
                    // must reset game!
                    // resetGame(LAST_POS);
                }
                var u = myeng.validate(move); 
                if (u) {
                    PHASE = 2;
                    myeng.makemove(u);
                    addToHistory(moveToNotation(u));
                    initPos(myeng.getFEN());
                    if (!gameEnded(true)) {
                        $('#think').show();
                        setTimeout(function() { goMove(); }, 5);
                    }
                } else {
                    PHASE--;
                    initPos(myeng.getFEN());
                }
            }
            return false;
        });

    $('.moves').live('click',
                     function(ev) {
                         var tg = ev.target;
                         if($(tg).is('a')) { // alert(tg.attr('chessFen'));
                             var fen = tg.getAttribute("chessFen");
                             if (LAST_POS_MOVE != null) {
                                 var tmp = tg.parentNode.childNodes[LAST_POS_MOVE];
                                 $(tmp).removeClass("oneMoveSel");
                             }
                             initPos(fen);
                             LAST_POS_MOVE = parseInt(tg.getAttribute("chessMoveId"));
                             var tmp = tg.parentNode.childNodes[LAST_POS_MOVE];
                             $(tmp).addClass("oneMoveSel");
                             if (fen != myeng.getFEN()) {
                                 PHASE = 3;      // forbid moves
                                 // document.getElementById("resetBtn").disabled = false;
                             } else {
                                 PHASE = 0;
                                 // document.getElementById("resetBtn").disabled = true;
                             }
                         }
                         return false;
                     });


};

function initPos(fen) {
    var PIECE_CLASS_NAMES = { P: "pawn", N: "knight", B: "bishop", R: "rook", Q: "queen", K: "king" },
    SPACES = { 1 : " ", 2 : "  ", 3 : "   ", 4 : "    ", 5 : "     ", 6 : "      ", 7 : "       ", 8 : "        " };
    var cont = $(qont).find('.ChessFields div[chessField]'); // '.ChessFields div:lt(64)'
    this.LAST_POS = fen;
    var pos = fen.split(/\s+/)[0];
    pos = pos.replace(/\x2f/g, "").replace(/[1-8]/g, function(s) { return SPACES[s] });
    cont.each(function() {
                  var id = this.getAttribute("chessField").toUpperCase();
                  var index = (id.charCodeAt(0) - 65) + (56 - id.charCodeAt(1)) * 8;
                  var piece = pos.charAt(index);
                  if (piece && piece != " ") {
                      var pieceUC = piece.toUpperCase(); 
                      var color =  pieceUC == piece;
                      color = color ? "white" : "black";
                      var pieceClass = "Piece Piece-" + color + "-" + PIECE_CLASS_NAMES[pieceUC];
                      this.innerHTML = "<div class='" + pieceClass + "'></div>";
                  } else {
                      this.innerHTML = "";
                  }
                  $(this).removeClass("Start");
              });
    LAST_POS_MOVE = null;
};

function gameEnded(computer) {
    var s = myeng.status(); // status(GLOBMOVES);
    var STATUS = myeng.STATUS;
    if (s) {
        switch (s) {
        case STATUS.CHECKMATE:
            alert("Checkmate - " + (computer ? "you won" : "you lost"));
            break;
        case STATUS.STALEMATE:
            alert("Stalemate");
            break;
        case STATUS.DRAW_REP:
            alert("Draw by repetition");
            break;
        case STATUS.DRAW_50:
            alert("Draw by fifty rule");
            break;
        }
    }
    // return s; -- wrong!
};

function moveToNotation(move) {
    if(!move) return ''; //alert(moveToSAN(move));
    //    alert((move & 0x000000F0) >> 4);
    move = (move >> 8) & 0xffff;
    var to = move & 0xff;
    var from = (move >> 8) & 0xff;
    to = String.fromCharCode(97 + (to & 7)) + String.fromCharCode(49 + ((to >> 4) & 7));
    from = String.fromCharCode(97 + (from & 7)) + String.fromCharCode(49 + ((from >> 4) & 7));
    return from + "-" + to;
};

function moveToSAN(move) {
    var fr = (move >>> 16) & 0xFF, to = (move >>> 8) & 0xFF, 
    p0 = (move & 0x000000F0) >> 4, p1 = move & 0x0000000F, sp = (move >> 24) & 0x0F;
    var frf = String.fromCharCode(97 + (fr & 7)), frr = String.fromCharCode(49 + ((fr >> 4) & 7)),
    tof = String.fromCharCode(97 + (to & 7)), tor = String.fromCharCode(49 + ((to >> 4) & 7));
    var san = to_san();
    // is_checkmate: san += "#"; RESULT_TAG = "1-0" | "0-1"
    // is_check: san += "+";
    // is_drawing_position: san += "1/2-1/2"; RESULT_TAG
    return san;

    function to_san() {
        if(sp == 1) return "O-O";
        if(sp == 2) return "O-O-O";
        var san = "";
        if(p0 < 4) { //pawn move
            if(p1) 
                san += frf + "×"; // e×d6
            san +=  tof + tor;    // d4, e×d6
            if(sp == 4 || sp == 5 || (sp >= 8 && sp <= 13)) // promoted move
                san += "=" + pc_char[sp & 0xFE]; //String(pc_char[sp]).toUpperCase();  // d8=Q, e×d8=R
            return san;
        }
        san += pc_char[p0 & 0xFE]; // Nf3, not nf3
         // Ambiguity...
         // _FILE: san += frf; // 'a-h' din From
         // _RANK: san += frr;  // '1-8' din From
         // _BOTH: san += frf + frr;
        if(p1)
            san += "×";
        san += tof + tor; // Nf3, N×f3
        return san;
    }
};

function addToHistory(move) {
    var mdiv = $('.moves', qont); 
    var fen = myeng.getFEN();
    var count = $('a', mdiv).length, wb = count % 2 == 0 ? count / 2 + 1 : 0;
    var tmp = $('<a href="#" class="oneMove' + (wb > 0 ? ' whiteMove"' : '"') + ' chessFen="' + fen + '" chessMoveId="' + count + '">' + (wb > 0 ? wb + '.' : '') + move + '</a>');  
    mdiv.append(tmp);
    if (LAST_POS_MOVE != null) {
        tmp = mdiv.find('a:last-child');
        $(tmp).removeClass("oneMoveSel");
        LAST_POS_MOVE = null;
    }
    mdiv.attr('scrollTop', 32000);
};

function updateStats(time) {
    if (STATS.min == null || time < STATS.min) {
        STATS.min = time;
    }
    if (STATS.max == null || time > STATS.max) {
        STATS.max = time;
    }
    STATS.all += time;
    STATS.calls++;
    STATS.avg = STATS.calls ? (STATS.avg * (STATS.calls - 1) + time) / STATS.calls : 0;
    var nods = myeng.nodes;
    STATS.nodes += nods;   // global in qchess.js!

    var HTML = [
        "min: ", (STATS.min / 1000).toFixed(3), ", ",
        "max: ", (STATS.max / 1000).toFixed(3), ", ",
        "avg: ", (STATS.avg / 1000).toFixed(3), "<br />",
        "   Totals: ", (STATS.all / 1000).toFixed(3), "sec, ",
        STATS.calls, " calls, ",
        STATS.nodes, " nodes evaluated<br />",
        "Nodes per second (average): <b>" , Math.round(STATS.nodes * 1000 / STATS.all), "</b>"
    ];
    $('.stats', qont).html("Last move: " + (time / 1000).toFixed(3) + "sec (" + nods + " nodes)<br />    Stats: " + HTML.join(""));
};

function goMove() {
    PHASE = 2;
    var start_time = new Date().getTime();
    //$('#think').show();
    var m = myeng.findBestMove();
    myeng.makemove(m);
    var end_time = new Date().getTime();
//    $('#think').hide(); 
//    updateStats(end_time - start_time);
//    initPos(myeng.getFEN());
    addToHistory(moveToNotation(m));
    $('#think').hide(); 
    PHASE = 0;
    initPos(myeng.getFEN());
    updateStats(end_time - start_time);
    if (gameEnded(false)) {
        // nothing  TODO ?
    }
};

function nextMove() {
    var mvs = $('.moves a', qont);    
    if (mvs.length) {
        if (LAST_POS_MOVE != null && LAST_POS_MOVE < mvs.length - 1) {
            var arr = $.makeArray(mvs);
            var at = arr[LAST_POS_MOVE + 1]; 
            $(at).click();
        }
    }
};

function prevMove() {
    var mvs = $('.moves a', qont);    
    if (mvs.length) {
        if (LAST_POS_MOVE == null)
            LAST_POS_MOVE = length - 1;
        if (LAST_POS_MOVE > 0) {
            var arr = $.makeArray(mvs);
            var at = arr[LAST_POS_MOVE - 1]; 
            $(at).click();
        }
    }
};

function fenToPos() {
    var fen = $('div.setposition').find(':input').val();
    if(!fen) return false;
    var bad = validFen(fen);
    if(bad.pos) { alert(bad.pos + '\n' + bad.mess); return false; }
    initPos(fen);
    myeng.setBOARD(fen);
    $('div.moves', qont).html('');
    updateStats(0.1);
    return false;
};
function validFen(fen) {
    var pos = 0, c = fen.charAt(pos), piece = /[kqrnbp]/i, bad = { pos: undefined, mess: '' };
    var cont = { 'K':0,'Q':0,'R':0,'N':0,'B':0,'P':0, 'k':0,'q':0,'r':0,'n':0,'b':0,'p':0};
    var SPACES = { 1 : " ", 2 : "  ", 3 : "   ", 4 : "    ", 5 : "     ", 6 : "      ", 7 : "       ", 8 : "        " };
    for(var r = 8; r >= 1; r--) {
        for(var f = 1; f <= 8; ) {
            if(c >= '1' && c <= '8') { //empty square(s)
                for(var i = 0, len = c - '0'; i < len; i++) {
                    if(f > 8) { bad.pos = pos; bad.mess = 'linie/coloană cu mai mult de 8 câmpuri'; return bad; }
                    f++;
                }
            } else { //piece
                if(!piece.test(c)) { bad.pos = pos; bad.mess = 'piesă notată greşit'; return bad; }
                cont[c]++;
                f++;
            }
            c = fen.charAt(++pos);
        }
        if(r > 1) {
            if(c != '/') { bad.pos = pos; bad.mess = 'mai mult de 8 câmpuri pe linie? (separatorul de linii corect: /)'; return bad; } 
            c = fen.charAt(++pos);
        }
    }
    if(cont['K'] != 1 || cont['k'] != 1) { bad.pos = pos; bad.mess = 'prea mulţi regi'; return bad; } 
    var PPP = cont['Q'] + cont['R'] + cont['N'] + cont['B'] + cont['P'];
    if(PPP > 15) { bad.pos = pos; bad.mess = 'prea multe piese Albe: pioni + piese-din-transformare trebuie să fie <= 8'; return bad; }
    var ppp = cont['q'] + cont['r'] + cont['n'] + cont['b'] + cont['p'];
    if(ppp > 15) { bad.pos = pos; bad.mess = 'prea multe piese Negre: pioni + piese-din-transformare trebuie să fie <= 8'; return bad; }
    var rows = fen.substring(0, pos).split('/');
    if(/[pP]/.test(rows[0] + rows[7])) { bad.pos = pos; bad.mess = 'nu pot exista Pioni pe prima/ultima linie'; return bad; }
    if(c != ' ') { bad.pos = pos; bad.mess = 'secţiunile FEN trebuie separate prin câte un spaţiu'; return bad; } 
    c = fen.charAt(++pos);
    if(!/[wb]/.test(c)) { bad.pos = pos; bad.mess = 'partea care trebuie să mute: w pentru Alb, b pentru Negru'; return bad; }
    var tom = c;
    c = fen.charAt(++pos);
    if(c != ' ') { bad.pos = pos; bad.mess = 'secţiunile FEN trebuie separate prin câte un spaţiu'; return bad; } 
    c = fen.charAt(++pos);
    if(c == '-') c = fen.charAt(++pos); // no castling rights
    else {
        if(c == 'K') {
            if(!/K..R$/.test(rows[7].replace(/[1-8]/g, function(s) { return SPACES[s]; }))) {
                bad.pos = pos; bad.mess = 'O-O albă este imposibilă (trebuie K pe e1 şi R pe h1)'; return bad; 
            }
            c = fen.charAt(++pos);
        }
        if(c == 'Q') {
            if(!/^R...K/.test(rows[7].replace(/[1-8]/g, function(s) { return SPACES[s]; }))) {
                bad.pos = pos; bad.mess = 'O-O-O albă este imposibilă (trebuie R pe a1 şi K pe e1)'; return bad; 
            }
            c = fen.charAt(++pos);
        }
        if(c == 'k') {
            if(!/k..r$/.test(rows[0].replace(/[1-8]/g, function(s) { return SPACES[s]; }))) {
                bad.pos = pos; bad.mess = 'O-O neagră este imposibilă (trebuie K pe e8 şi R pe h8)'; return bad; 
            }
            c = fen.charAt(++pos);
        }
        if(c == 'q') {
            if(!/^r...k/.test(rows[0].replace(/[1-8]/g, function(s) { return SPACES[s]; }))) {
                bad.pos = pos; bad.mess = 'O-O-O neagră este imposibilă (trebuie R pe a8 şi K pe e8)'; return bad; 
            }
            c = fen.charAt(++pos);
        }
    }
    if(c != ' ') { bad.pos = pos; bad.mess = 'secţiunile FEN trebuie separate prin câte un spaţiu'; return bad; } 
    c = fen.charAt(++pos);
    if(c == '-') c = fen.charAt(++pos); // no en-passant
    else {
        if(c < 'a' || c > 'h') { bad.pos = pos; bad.mess = 'coloană (a-h) notată greşit'; return bad; }
        var tgt = String(c).charCodeAt(0) - 97;
        c = fen.charAt(++pos);
        if(c != '3' && c != '6') { bad.pos = pos; bad.mess = 'linia en-passant este sau 3, sau 6'; return bad; }
        if(tom == 'w') {
            if(c != '6') { bad.pos = pos; bad.mess = 'albul fiind la mutare, linia en-passant trebuie să fie 6'; return bad; }
            var tgln = rows[2].replace(/[1-8]/g, function(s) { return SPACES[s]; });
            if(tgln.charAt(tgt) != ' ') { bad.pos = pos; bad.mess = 'destinaţia en-passant este ocupată'; return bad; }
            tgln = rows[3].replace(/[1-8]/g, function(s) { return SPACES[s]; }); alert(tgln.split('')+'\n'+tgt);
            if(tgln.charAt(tgt) != 'p') {
                bad.pos = pos; bad.mess = 'nu există pion negru de capturat en-passant'; return bad; 
            }
        } else {
            if(c != '3') { bad.pos = pos; bad.mess = 'negrul fiind la mutare, linia en-passant trebuie să fie 3'; return bad; }
            var tgln = rows[5].replace(/[1-8]/g, function(s) { return SPACES[s]; });
            if(tgln.charAt(tgt) != ' ') { bad.pos = pos; bad.mess = 'destinaţia en-passant este ocupată'; return bad; }
            tgln = rows[4].replace(/[1-8]/g, function(s) { return SPACES[s]; });
            if(tgln.charAt(tgt) != 'P') {
                bad.pos = pos; bad.mess = 'nu există pion alb de capturat en-passant'; return bad; 
            }
        } 
        c = fen.charAt(++pos);
    }
    return bad;
}

/*
   From the PGN Standard by Steven J. Edwards:

   16.1.3.1: Piece placement data
   The first field represents the placement of the pieces on the board.
   The board contents are specified starting with the eighth rank and
   ending with the first rank. For each rank, the squares are specified
   from file a to file h. White pieces are identified by uppercase SAN
   piece letters ("PNBRQK") and black pieces are identified by lowercase
   SAN piece letters ("pnbrqk"). Empty squares are represented by the
   digits one through eight; the digit used represents the count of
   contiguous empty squares along a rank. A solidus character "/" is
   used to separate data of adjacent ranks.

   16.1.3.2: Active color
   The second field represents the active color. A lower case "w" is
   used if White is to move; a lower case "b" is used if Black is the
   active player.

   16.1.3.3: Castling availability
   The third field represents castling availability. This indicates
   potential future castling that may of may not be possible at the
   moment due to blocking pieces or enemy attacks. If there is no
   castling availability for either side, the single character symbol
   "-" is used. Otherwise, a combination of from one to four characters
   are present. If White has kingside castling availability, the
   uppercase letter "K" appears. If White has queenside castling
   availability, the uppercase letter "Q" appears. If Black has
   kingside castling availability, the lowercase letter "k" appears.
   If Black has queenside castling availability, then the lowercase
   letter "q" appears. Those letters which appear will be ordered first
   uppercase before lowercase and second kingside before queenside.
   There is no white space between the letters.

   16.1.3.4: En passant target square
   The fourth field is the en passant target square.
   If there is no en passant target square then the single character
   symbol "-" appears. If there is an en passant target square then is
   represented by a lowercase file character immediately followed by a
   rank digit. Obviously, the rank digit will be "3" following a white
   pawn double advance (Black is the active color) or else be the digit
   "6" after a black pawn double advance (White being the active color).

   An en passant target square is given if and only if the last move was
   a pawn advance of two squares. Therefore, an en passant target square
   field may have a square name even if there is no pawn of the opposing
   side that may immediately execute the en passant capture.

   16.1.3.5: Halfmove clock
   The fifth field is a nonnegative integer representing the halfmove
   clock. This number is the count of halfmoves (or ply) since the
   last pawn advance or capturing move. This value is used for the
   fifty move draw rule.

   16.1.3.6: Fullmove number
   The sixth and last field is a positive integer that gives the fullmove
   number. This will have the value "1" for the first move of a game for
   both White and Black. It is incremented by one immediately after each
   move by Black.
 */

/*

function higFEN() {
    var fen1 = myeng.getFEN();
    var fen = fen1.split(/\s+/);
    // fen[0] = fen[0].replace(/[rnbqkp]/g, function(p) { return '<span class="blackp">' + p + '</span>'; })
    //     .replace(/[RNBQKP]/g, function(p) { return '<span class="whitep">' + p + '</span>'; })
    //     .replace(/[1-8]/g, function(p) {return '<span class="countp">' + p + '</span>'});
    // fen[1] = '<span class="tomove">' + fen[1] + '</span>';
    // fen[2] = fen[2].replace(/[kq]/g, function(p) { return '<span class="black00">' + p + '</span>'; })
    //     .replace(/[KQ]/g, function(p) { return '<span class="white00">' + p + '</span>'; });
    // fen[3] = '<span class="enpass">' + fen[3] + '</span>';
    // fen[4] = '<span class="fifty">' + fen[4] + '</span>';
    // fen[5] = '<span class="nextnr">' + fen[5] + '</span>';
    return '<a href="" class="TF-Link-r" onclick="initPos(\'' + fen1 + '\');return false;">' + fen.join(' ') + '</a>  ';
};

*/

/* *********************************************************************
function resetGame(fen) {
    if (fen == null) {
        fen = document.getElementById("fen").value;
    }
    var lpm = LAST_POS_MOVE;
    myeng.setBOARD(fen);
    initPos(fen);
    PHASE = 0;

    if (lpm != null) {
        var el = document.getElementById("moves").childNodes[lpm];
        delClass(el, "oneMoveSel");
        el = el.nextSibling;
        while (el) {
            var tmp = el.nextSibling;
            el.parentNode.removeChild(el);
            el = tmp;
        }
    }
};

function newGame() {
    resetGame(FEN_STD);
    document.getElementById("moves").innerHTML = '';
};
*/
