var Sudoku = {};
Sudoku.boxSize = 64;
Sudoku.boxes = [];
Sudoku.selectedBox = 0;
// Define some example puzzles.
Sudoku.examples = [
'000006018006700025007029000008000340020164080074000600000690200560007800740200000',
'902000004000000567000080000005008200070403080003100400000040000281000000600000901',
'010000960050017000400006000500008400703000506004500003000600001000850020047000080',
'000000016100009048030060000600430000005070200000098004000020070960800001750000000',
'005030000000084970004700005007000402500203001802000600700005100093120000000040200',
'000900400000076001050080023000007509067000130109400000370040080600730000002008000'];
Sudoku.nextExample = 0;
Sudoku.autoSolveTimeout = null;
Sudoku.autoSolveInterval = 500;

//**********************************************************************
// Initialization function
//**********************************************************************
$(function() {
    Sudoku.setupPage();
});

//**********************************************************************
// autoSolveStep: find one cell with only one possibility and fill it.
//**********************************************************************
Sudoku.autoSolveStep = function() {
    var index = 0;
    while(index < 81) {
        var box = Sudoku.boxes[index];
        var digit = box.data('digit');
        if(digit==0) {
            if(box.hasClass('single-poss') || box.hasClass('single-digit')) {
                box.click();
                Sudoku.changeMade();
                Sudoku.autoSolveTimeout =
                    setTimeout(Sudoku.autoSolveStep, Sudoku.autoSolveInterval);
                return;
            }
        }
        ++index;
    }
    $('#auto-solve').attr('value', 'Autosolve');
};

//**********************************************************************
// setupPage: Allow each main page section to be collapsed
//**********************************************************************
Sudoku.setupPage = function()
{
    $('#auto-solve').click(function() {
        if(Sudoku.autoSolveTimeout) {
            clearTimeout(Sudoku.autoSolveTimeout);
            Sudoku.autoSolveTimeout = null;
            $(this).attr('value', 'Autosolve');
        } else {
            $(this).attr('value', 'STOP autosolving');
            Sudoku.autoSolveTimeout = setTimeout(Sudoku.autoSolveStep, 0);
        }
    })
    $('#auto-solve-faster').click(function() {
        Sudoku.autoSolveInterval /= 2;
        $(this).attr('value', 'Autosolve faster ('
            +Math.round(Sudoku.autoSolveInterval)+'ms delay)');
    });
    $('#load-example').click(function() {
        Sudoku.fillFromSpec(Sudoku.examples[Sudoku.nextExample]);
        ++Sudoku.nextExample;
        if(Sudoku.nextExample >= Sudoku.examples.length)
            Sudoku.nextExample = 0;
    });
    $('#clear-board').click(function() {
        Sudoku.fillFromSpec(
'000000000000000000000000000000000000000000000000000000000000000000000000000000000'
        );
    });
    $('html').keypress(function(e) {
        var box = Sudoku.boxes[Sudoku.selectedBox];
        var cc = e.charCode;
        var moveToNextCell = true;
        var ch = '&nbsp;';
        var digit = 0;
        
        if((49<=cc) && (cc<=57)) { // '1' to '9'
            ch = String.fromCharCode(cc);
            digit = cc - 48;
        
            // Make sure this digit can legally go here.
            var poss = box.data('poss');
            if((digit>0) && poss[digit-1]) {
            } else {
                alert(ch+" cannot go in the cell");
                moveToNextCell = false;
                ch = '&nbsp;';
                digit = 0;
            }
        }
        
        $('.mid-digit', box).html(ch);
        box.data('digit', digit);
        Sudoku.changeMade();

        if(moveToNextCell) {
            var nextIndex = box.data('index')+1;
            if(nextIndex >= 81) nextIndex = 0;
            var nextBox = Sudoku.boxes[nextIndex];
            nextBox.click();
        }
        return false;
    });

    // Create a box once and (below) clone it 81 times.
    var boxToCopy = $('<div class="box1x1">'
        +'<div class="top-line">&nbsp;</div>'
        +'<div class="mid-digit">&nbsp;</div>'
        +'<div class="bottom-line">&nbsp;</div>'
        +'</div>')
        .height(Sudoku.boxSize)
        .width(Sudoku.boxSize);
    
    //var dim = 9*(Sudoku.boxSize+2);
    var body = $('body');
    var top = 40;
    var index = 0;
    var row = 0;
    var col = 0;
    var i, j, k, l, left, b;
    
    // Create the board from 81 cloned cells and place them (absolute positioning)
    // in the window.
    for(i=0; i<3; ++i) {
        for(j=0; j<3; ++j) {
            left = 15;
            var tbBorders = 2;
            for(k=0; k<3; ++k) {
                for(l=0; l<3; ++l) {
                    var lrBorders = 1;
                    b = boxToCopy.clone(false)
                        .css('top', top)
                        .css('left', left)
                        .data('index', index)
                        .data('digit', 0)
                        .appendTo(body)
                        .bind('click', Sudoku.clickOnCell);
                        
                    // Fix up the borders
                    if((row%3)==0) {
                        b.css('border-top-width', (row==0) ? 3 : 2);
                        //tbBorders += 2;
                    }
                    if(row==8) {
                        b.css('border-bottom-width', 3);
                    }
                    if((col%3)==0) {
                        b.css('border-left-width', (col==0) ? 3 : 2);
                        //lrBorders += 2;
                    }
                    if(col==8) {
                        b.css('border-right-width', 3);
                    }
                    
                    Sudoku.boxes[index] = b;
                    ++index;
                    left += Sudoku.boxSize + lrBorders;
                    ++col;
                }
            }
            top += Sudoku.boxSize + tbBorders;
            ++row;
            col = 0;
        }
    }
    
    // Reposition cells now that the dimensions are set.
    index = 0;
    top = 40;
    for(i=0; i<3; ++i) {
        for(j=0; j<3; ++j) {
            left = 15;
            for(k=0; k<3; ++k) {
                for(l=0; l<3; ++l) {
                    b = Sudoku.boxes[index];
                    b.css('top', top).css('left', left);
                    ++index;
                    left += b.outerWidth();
                }
            }
            top += b.outerHeight();
        }
    }
    
    Sudoku.changeMade();
};

//**********************************************************************
// fillFromSpec: fill in the board according to a string specification
//**********************************************************************
Sudoku.fillFromSpec = function(spec) {
    for(var i=0; i<81; ++i) {
        var digit = spec.charCodeAt(i) - 48;
        if((digit < 0) || (digit > 9)) {
            digit = 0;
            console.log('ERROR: board specification error, character not 0-9: "'
                +spec.charAt(i)+'" at position i');
        }
        var s = '&nbsp;';
        if(digit > 0) s = digit.toString();
        var box = Sudoku.boxes[i];
        $('.mid-digit', box).html(s);
        box.data('digit', digit);
        //console.log('i='+i+' digit='+digit+' s='+s);
    }
    Sudoku.changeMade();
};

//**********************************************************************
// getRowBoxes: 
//**********************************************************************
Sudoku.getRowBoxes = function(box) {
    var j;
    
    // Check cache first
    var boxes = box.data('rowBoxes');
    if(boxes) return boxes;
    
    // Not in cache, so compute it
    boxes = [];
    
    // check the row it is in.
    var row = Math.floor(box.data('index') / 9);
    var start = 9*row;
    
    for(j=start; j<start+9; ++j) {
        boxes.push(Sudoku.boxes[j]);
    }
    
    // save the result in the cache.
    box.data('rowBoxes', boxes);

    return boxes;
};

//**********************************************************************
// getColBoxes: 
//**********************************************************************
Sudoku.getColBoxes = function(box) {
    var j;
    
    // Check cache first
    var boxes = box.data('colBoxes');
    if(boxes) return boxes;
    
    // Not in cache, so compute it
    boxes = [];
    
    var boxIndex = box.data('index');
    var col = boxIndex % 9;
    for(j=col; j<81; j += 9) {
        boxes.push(Sudoku.boxes[j]);
    }

    // save the result in the cache.
    box.data('colBoxes', boxes);

    return boxes;
};

//**********************************************************************
// getCellBoxes: 
//**********************************************************************
Sudoku.getCellBoxes = function(box) {
    // Check cache first
    var boxes = box.data('cellBoxes');
    if(boxes) return boxes;
    
    // Not in cache, so compute it
    boxes = [];
    var boxIndex = box.data('index');
    var row = Math.floor(boxIndex/9);
    var col = boxIndex % 9;
    var r = row - (row % 3);
    var c = col - (col % 3);
    var index = 9*r + c;
    for(j = 0; j<3; ++j) {
        for(k = 0; k<3; ++k) {
            boxes.push(Sudoku.boxes[index]);
            ++index;
        }
        index += 6;
    }
        
    // save the result in the cache.
    box.data('cellBoxes', boxes);

    return boxes;
};



