
function Board() {}
window.attachEvent("onload", function()
{
    Board.startX = findPosX( boardImage );
    Board.startY = findPosY( boardImage );

    Board.laidTilesX = [];
    for (var cnx = 1; cnx <= 15; cnx++)
    {
        Board.laidTilesX[cnx] = [];
        for (var rnx = 1; rnx <= 15; rnx++)
            Board.laidTilesX[cnx][rnx] = null;
    }
    Board.laidTilesY = [];
    for (var rnx = 1; rnx <= 15; rnx++)
    {
        Board.laidTilesY[rnx] = [];
        for (var cnx = 1; cnx <= 15; cnx++)
            Board.laidTilesY[rnx][cnx] = null;
    }
    Board.laidCount = 0;
    
    Board.SetCursor(8,8);
});

Board.CursorCol = function()
{
    return (getPixels(boardCursor.style.left) - Board.startX) / 40 + 1;
}
Board.CursorRow = function()
{
    return (getPixels(boardCursor.style.top) - Board.startY) / 40 + 1;
}
Board.SetCursor = function( col, row )
{
    boardCursor.style.left = Board.startX + (col-1)*40;
    boardCursor.style.top = Board.startY + (row-1)*40;
}


Board.NextLeft = function()
{
    var yArray = Board.laidTilesY[Board.CursorRow()];
    var col = Board.CursorCol();
    do { col--; } while (yArray[col] != null);
    return col;
}
Board.NextRight = function()
{
    var yArray = Board.laidTilesY[Board.CursorRow()];
    var col = Board.CursorCol();
    do { col++; } while (yArray[col] != null);
    return col;
}
Board.NextUp = function()
{
    var xArray = Board.laidTilesX[Board.CursorCol()];
    var row = Board.CursorRow();
    do { row--; } while (xArray[row] != null);
    return row;
}
Board.NextDown = function()
{
    var xArray = Board.laidTilesX[Board.CursorCol()];
    var row = Board.CursorRow();
    do { row++; } while (xArray[row] != null);
    return row;
}

Board.GetStretch = function( orient, tenativeTiles )
{
    if ( orient == Orientation.Vertical )
        return Board.RowStretch( tenativeTiles );
    else
        return Board.ColumnStretch( tenativeTiles );
}
Board.ColumnStretch = function( tenativeTiles )
{
    var stretch = { min:999, max:-1 };
    for (var inx = 0; inx < tenativeTiles.length; inx++)
    {
        var pt = tenativeTiles[inx].GetPosition();
        stretch.min = Math.min( stretch.min, pt.col );
        stretch.max = Math.max( stretch.max, pt.col );
    }
    return stretch;
}
Board.RowStretch = function( tenativeTiles )
{
    var stretch = { min:999, max:-1 };
    for (var inx = 0; inx < tenativeTiles.length; inx++)
    {
        var pt = tenativeTiles[inx].GetPosition();
        stretch.min = Math.min( stretch.min, pt.row );
        stretch.max = Math.max( stretch.max, pt.row );
    }
    return stretch;
}
Board.XDistance = function( row, col1, col2 )
{
    var countEmpty = 0;
    var yArray = Board.laidTilesY[row];
    for (var inx = col1; inx <= col2; inx++)
    {
        if (yArray[inx] == null)
            countEmpty++;
    }
    return countEmpty;
}
Board.YDistance = function( col, row1, row2 )
{
    var countEmpty = 0;
    var xArray = Board.laidTilesX[col];
    for (var inx = row1; inx <= row2; inx++)
    {
        if (xArray[inx] == null)
            countEmpty++;
    }
    return countEmpty;
}


Board.MovementOkay = function( targetPt, tenativeTiles )
{
    if ( targetPt.col < 1 || targetPt.col > 15 )
        return false;
    if ( targetPt.row < 1 || targetPt.row > 15 )
        return false;

    if ( tenativeTiles.length == 1 )
    {
        var pt = tenativeTiles[0].GetPosition();
        if ( pt.col == targetPt.col )
        {
            var stretch = Board.RowStretch( tileLaidDown );
            stretch.min = Math.min( stretch.min, targetPt.row );
            stretch.max = Math.max( stretch.max, targetPt.row );
            if ( Board.YDistance( pt.col, stretch.min, stretch.max ) > 7 )
                return false;
        }
        else if ( pt.row == targetPt.row )
        {
            var stretch = Board.ColumnStretch( tileLaidDown );
            stretch.min = Math.min( stretch.min, targetPt.col );
            stretch.max = Math.max( stretch.max, targetPt.col );
            if ( Board.XDistance( pt.row, stretch.min, stretch.max ) > 7 )
                return false;
        }
        else // you can vary in one-axis, but not both
            return false;
    }
    else if ( tenativeTiles.length >= 2 )
    {
        var pt = tenativeTiles[0].GetPosition();
        if ( Orientation.Get( tenativeTiles ) == Orientation.Vertical )
        {
            if ( pt.col != targetPt.col )
                return false;

            var stretch = Board.RowStretch( tileLaidDown );
            stretch.min = Math.min( stretch.min, targetPt.row );
            stretch.max = Math.max( stretch.max, targetPt.row );
            if ( Board.YDistance( pt.col, stretch.min, stretch.max ) > 7 )
                return false;
        }
        else // row == row2
        {
            if ( pt.row != targetPt.row )
                return false;

            var stretch = Board.ColumnStretch( tileLaidDown );
            stretch.min = Math.min( stretch.min, targetPt.col );
            stretch.max = Math.max( stretch.max, targetPt.col );
            if ( Board.XDistance( pt.row, stretch.min, stretch.max ) > 7 )
                return false;
        }
    }
    return true;
}



Board.Holes = function( tiles )
{
    if ( tiles.length == 1 )
        return false;

    var orient = Orientation.Sort( tiles );
    var fixedDim = tiles[0].GetFixedDim(orient);
    var stretch = Board.GetStretch(orient, tiles);
    var gridLine = Orientation.GetArray( orient, fixedDim );

    var currentTileInx = 0;
    for (var varyingDim = stretch.min; varyingDim <= stretch.max; varyingDim++)
    {
        if ( tiles[currentTileInx].GetVaryingDim(orient) == varyingDim )
            currentTileInx++;
        else if (gridLine[varyingDim] == null)
            return true;
    }

    return false;
}


var Direction = { Left:1, Right:2, Up:3, Down:4 };
var DirectionOneDim = { Back:true,Forward:false };
var Orientation = 
{ 
    Horizontal:true, 
    Vertical:false,
    
    Get: function (tiles)
    {
        var pt  = tiles[0].GetPosition();
        var pt2 = tiles[1].GetPosition();
        if ( pt.col == pt2.col )    // Vertical
            return Orientation.Vertical;
        else                        // Horizontal
            return Orientation.Horizontal;
    },

    GetArray: function (orient, fixedDim)
    {
        return ( orient == Orientation.Vertical ) ? Board.laidTilesX[fixedDim] : Board.laidTilesY[fixedDim];
    },
    
    Sort: function (tiles)
    {
        var orient = this.Get(tiles);
        if (orient == Orientation.Vertical)
            tiles.sort( function(a, b)
            {
                var aRow = a.GetPosition().row;
                var bRow = b.GetPosition().row;
                if (aRow < bRow)
                    return -1;
                else if (aRow == bRow)
                    return 0;
                else
                    return 1;
            });
        else
            tiles.sort( function(a, b)
            {
                var aCol = a.GetPosition().col;
                var bCol = b.GetPosition().col;
                if (aCol < bCol)
                    return -1;
                else if (aCol == bCol)
                    return 0;
                else
                    return 1;
            });
        return orient;
    }
};

Board.GetAdjacent = function( fixedDim, varyingDim, orient, direction1Dim )
{
    var adjacent = [];
    var gridLine = Orientation.GetArray( orient, fixedDim );
    if ( direction1Dim == DirectionOneDim.Back )
        while ( varyingDim > 1 && gridLine[--varyingDim] )
            adjacent.push( gridLine[varyingDim] );
    else
        while ( varyingDim < 15 && gridLine[++varyingDim] )
            adjacent.push( gridLine[varyingDim] );

    return adjacent;
}

// Returns an array of { WordText:, WordHTML:, Score: }
Board.GetWords = function( tiles )
{
    var words = [];

    if ( tiles.length == 1 )
    {
        var t = tiles[0];

        // Check its 4 sides.
        // A maximum of two words can be obtained by laying down 1 letter.        
        var w = _CheckSingleLetterSides( t, Orientation.Horizontal );
        if ( w )
            words.push(w);
        var w = _CheckSingleLetterSides( t, Orientation.Vertical );
        if ( w )
            words.push(w);

        if ( words.length == 0 ) // if it was adjacent to nothing...
            return null;
    }
    else
    {
        var orient = Orientation.Sort( tiles );
        var fixedDim = tiles[0].GetFixedDim(orient);
        var stretch = Board.GetStretch(orient, tiles);

        // First lay has to include H8 (center square).
        if (Board.laidCount == 0 && !(fixedDim == 8 && stretch.min <= 8 && 8 <= stretch.max))
            return null;

        var begin  = Board.GetAdjacent( fixedDim, stretch.min, orient, DirectionOneDim.Back );
        var end    = Board.GetAdjacent( fixedDim, stretch.max, orient, DirectionOneDim.Forward );

        var w = { WordText:"", WordHTML:"", WordScore:0, WordMultipler:1 };

        for (var inx = begin.length-1; inx >= 0; inx--)
        {
            var tile = begin[inx];
            w.WordText += tile.letter;
            w.WordHTML += tile.letter.toUpperCase();
            w.WordScore += tile.Score();
        }

        var gridLine = Orientation.GetArray( orient, fixedDim );
        var tileInx = 0;
        var usedExistingCount = 0;
        for (var varyingDim = stretch.min; varyingDim <= stretch.max; varyingDim++)
        {            
            var t = gridLine[varyingDim];

            if (t)                  // Could be existing tile
            {
                usedExistingCount++;
                w.WordText += t.letter;
                w.WordHTML += t.letter.toUpperCase();
                w.WordScore += t.Score();
            }
            else                    // Or a new tile we just laid down.
            {
                t = tiles[ tileInx++ ];
                var bonus = Board.GetBonus( t.GetPosition() );
                if ( bonus != null && bonus.Type == BonusType.Word )
                    w.WordMultipler *= bonus.Multipler;

                // For new tiles, check their sides to see if they rub up against any other letters (which means they are forming more new words).
                var otherWord = _CheckSingleLetterSides( t, !orient );
                if (otherWord)
                    words.push(otherWord);

                w.WordText += t.letter;
                w.WordHTML += (( bonus != null ) ? "<b style='color:"+bonus.Color+"'>" : "<b>") + t.letter.toUpperCase() + "</b>";
                w.WordScore += t.Score();
                if ( bonus && bonus.Type == BonusType.Letter )
                    w.WordScore += (t.Score() * (bonus.Multipler-1));
            }
        }

        for (var inx = end.length-1; inx >= 0; inx--)
        {
            var tile = end[inx];
            w.WordText += tile.letter;
            w.WordHTML += tile.letter.toUpperCase();
            w.WordScore += tile.Score();
        }
        
        w.WordScore *= w.WordMultipler;


        // If no hooks at the begin or end, and no extra words in the middle, then *pewt*
        if ( Board.laidCount > 0 && usedExistingCount == 0 && begin.length == 0 && end.length == 0 && words.length == 0 )
            return null;

        
        words.push( w );
    }

    return words;
}


    function _CheckSingleLetterSides( t, orient )
    {
        var fixedDim = t.GetFixedDim(orient);
        var varyingDim = t.GetVaryingDim(orient);
        var begin  = Board.GetAdjacent( fixedDim, varyingDim, orient, DirectionOneDim.Back );
        var end    = Board.GetAdjacent( fixedDim, varyingDim, orient, DirectionOneDim.Forward );

        if ( begin.length == 0 && end.length == 0 )
            return null;

        var w = { WordText:"", WordHTML:"", WordScore:0, LetterScore:0, WordMultipler:1 };

        for (var inx = begin.length-1; inx >= 0; inx--)
        {
            var tile = begin[inx];
            w.WordText += tile.letter;
            w.WordHTML += tile.letter.toUpperCase();
            w.WordScore += tile.Score();
        }

        var bonus = Board.GetBonus( t.GetPosition() );
        if ( bonus != null && bonus.Type == BonusType.Word )
            w.WordMultipler *= bonus.Multipler;
        w.WordText += t.letter;
        w.WordHTML += (( bonus != null ) ? "<b style='color:"+bonus.Color+"'>" : "<b>") + t.letter.toUpperCase() + "</b>";
        w.WordScore += t.Score();
        if ( bonus && bonus.Type == BonusType.Letter )
            w.WordScore += (t.Score() * (bonus.Multipler-1));

        for (var inx = end.length-1; inx >= 0; inx--)
        {
            var tile = end[inx];
            w.WordText += tile.letter;
            w.WordHTML += tile.letter.toUpperCase();
            w.WordScore += tile.Score();
        }
        
        w.WordScore *= w.WordMultipler;
        return w;
    }


Board.Lay = function( tiles )
{
    // Commit New Tiles to Board Layout
    for (var inx = 0; inx < tiles.length; inx++)
    {
        var t = tiles[inx];
        Board.AddTile( t );
    }
}

Board.AddTile = function( tile )
{
    var pt = tile.GetPosition();
    Board.laidTilesX[pt.col][pt.row] = tile;
    Board.laidTilesY[pt.row][pt.col] = tile;
    Board.laidCount++;
}






var BonusType = { Word:1, Letter:2 };

Board.GetBonus = function( pt )
{
    var ptID = pt.col+":"+pt.row;
    if ( _bonusSpotsAlreadyClaimed[ptID] )
        return null;
    _bonusSpotsAlreadyClaimed[ptID] = true;

    var col = ( pt.col > 8 ) ? pt.col = 16 - pt.col : pt.col;
    var row = ( pt.row > 8 )? pt.row = 16 - pt.row : pt.row;

    if ( col == row && 
        ((2 <= col && col <= 5 ) || col == 8)
       )
        return { Type: BonusType.Word, Multipler: 2, Color:"pink" };

    // Since its a diagonal mirrow, we can express some of these with min & max.
    var min = Math.min( col, row );
    var max = Math.max( col, row );

    if ( min == 1 && 
         (max == 1 || max == 8)
       )
        return { Type: BonusType.Word, Multipler: 3, Color:"red" };

    if ( (min == 1 && max == 3) ||
         (min == 3 && max == 7) ||
         (min == 4 && max == 8) ||
         (min == 7 && max == 7) 
       )
        return { Type: BonusType.Letter, Multipler: 2, Color:"#31B1F5" };

    if ( (min == 2 && max == 6) ||
         (min == 6 && max == 6) 
       )
        return { Type: BonusType.Letter, Multipler: 3, Color:"blue" };

    return null;
}
var _bonusSpotsAlreadyClaimed = {};



function Point( x, y )
{
    this.col = x;
    this.row = y;
}
Point.prototype.Equals = function( otherPoint )
{
    return this.col == otherPoint.col && this.row == otherPoint.row;
}



