/*

Quicktetris published by Thomas Kjeldahl Nilsson under 
Creative Commons Attribution 3.0 license.
License terms: http://creativecommons.org/licenses/by/3.0/

*/




/* 
Field represented as x*y matrix 'gridState'. Tiles with blocks on them are set to 1, 
empty ones are 0.

00000000000000000
00000000000000000
00000000000000000
00000000000000000
00000000000000000
00000000000000000
00000000000010000
00000000000010000
00000000000010110
00000000000010110
00000000000111111
00000000000111111

Also have a corresponding array 'gridTiles' of visible or non visible div tiles, 
tiles turned on or off based on gridState.
*/

var Field = {

    WIDTH: 12,
    // no of tiles
    HEIGHT: 20,
    // no of tiles
    State: {
        gridState: null,
        gridTiles: null,
        gridBackground: null,
        posX: 0,
        // Absolute position of field left border
        posY: 0 // Absolute position of field top border
    },

    init: function(bgcolor, tilecolor, posX, posY) {
        this.State.posX = posX;
        this.State.posY = posY;

        this.State.gridBackground = Graphics.createFieldBackground(bgcolor, this.State.posX, this.State.posY, (Piece.State.tileWidth * this.WIDTH), (Piece.State.tileHeight * this.HEIGHT));
        this.State.gridState = get2dArray(this.WIDTH, this.HEIGHT, 0);
        this.State.gridTiles = this.createHiddenTileArray(this.State.posX, this.State.posY, this.WIDTH, this.HEIGHT, tilecolor, Piece.State.tileWidth, Piece.State.tileHeight);
    },

    createHiddenTileArray: function(posX, posY, matrixWidth, matrixHeight, color, tileWidth, tileHeight) {
        var matrix = get2dArray(matrixWidth, matrixHeight, 0);
        matrix = matrix.map(function(element, x, y) {
            element = Graphics.createRectangleDiv(color, posX + (tileWidth * x), posY + (tileHeight * y), tileWidth, tileHeight);
            element.style.visibility = "hidden";
            return element;
        });

        return matrix;
    },

    tileOn: function(x, y) {
        if ((x >= 0) && (y >= 0)) {
            this.State.gridState[x][y] = 1;
            this.State.gridTiles[x][y].style.visibility = "visible";
        }
    },

    tileOff: function(x, y) {
        if ((x >= 0) && (y >= 0)) {
            this.State.gridState[x][y] = 0;
            this.State.gridTiles[x][y].style.visibility = "hidden";
        }
    },

    isTileOn: function(x, y) {
        if ((x >= 0) && (y >= 0)) {
            return (this.State.gridState[x][y]);
        }
    },

    CollisionData: {
        collides: false,
        sticks: false
    },

    checkCollisions: function(xPos, yPos, shapeArray, directionIsDown, dx, dy) {
        if (this.State.gridState) {
            var fieldCollision = this.pieceCollidesWithField(xPos, yPos, shapeArray, directionIsDown, dx, dy);

            var boundaryCollision = this.pieceCollidesWithFloorOrWall(xPos, yPos, shapeArray, directionIsDown, dx, dy);

            var collisionResult = Object.create(this.CollisionData);
            collisionResult.collides = fieldCollision.collides || boundaryCollision.collides;
            collisionResult.sticks = fieldCollision.sticks || boundaryCollision.sticks;

            return collisionResult;
        }
    },

    pieceCollidesWithField: function(xPos, yPos, shapeArray, directionIsDown, dx, dy) {
        var collision = Object.create(this.CollisionData);

        shapeArray.eachWithIndexes(function(element, x, y) {
            if (!element || (collision && collision.collides)) {
                return;
            }

            var tileXPos = xPos + (Piece.State.tileWidth * x) + dx;
            var tileYPos = yPos + (Piece.State.tileHeight * y) + 1;

            var movingRect = makeRect(tileXPos, tileYPos, Piece.State.tileWidth, Piece.State.tileHeight);

            Field.State.gridTiles.eachWithIndexes(function(tile, arrX, arrY) {
                if (collision.collides) {
                    return;
                }

                if (Field.isTileOn(arrX, arrY)) { // Only  collision if tile is actually switched on
                    var tileX = Field.State.posX + (Piece.State.tileWidth * arrX);
                    var tileY = Field.State.posY + (Piece.State.tileHeight * arrY);
                    var fieldRect = makeRect(tileX, tileY, Piece.State.tileWidth, Piece.State.tileHeight);
                    collision.collides = intersectRect(movingRect, fieldRect);

                    if (collision.collides && directionIsDown) {
                        collision.sticks = true;
                    }
                }
            });
        });

        return collision;
    },

    pieceCollidesWithFloorOrWall: function(xPos, yPos, shapeArray, directionIsDown, dx, dy) {
        var collision;

        shapeArray.eachWithIndexes(function(element, x, y) {
            if (!element || (collision && collision.collides)) {
                return;
            }

            collision = Object.create(Field.CollisionData);

            // Check for floor collision
            var tileYPos = yPos + (Piece.State.tileHeight * y) + 1;
            var tileBottom = tileYPos + Piece.State.tileHeight;
            var fieldBottom = Field.State.posY + (Piece.State.tileHeight * Field.HEIGHT);

            collision.collides = (tileBottom > fieldBottom);
            collision.sticks = directionIsDown;

            // Check for wall collision (if no floor collision)
            if (!collision.collides) {
                var tileXPos = xPos + (Piece.State.tileWidth * x) + dx;
                var tileLeft = tileXPos;
                var tileRight = tileXPos + Piece.State.tileWidth;

                var fieldLeft = Field.State.posX;
                var fieldRight = Field.State.posX + (Piece.State.tileWidth * Field.WIDTH);

                collision.collides = (tileLeft < fieldLeft || tileRight > fieldRight);
            }
        });

        return collision;
    },

    mergeShapeIntoField: function(xPos, yPos, shapeArray) {
        shapeArray.eachWithIndexes(function(element, x, y) {
            if (element) {
                var tileAbsoluteXPos = xPos + (Piece.State.tileWidth * x);
                var tileAbsoluteYPos = yPos + (Piece.State.tileHeight * y);

                var tileXPosInField = tileAbsoluteXPos - Field.State.posX;
                var tileYPosInField = tileAbsoluteYPos - Field.State.posY;

                var tileXLocationInField = tileXPosInField / Piece.State.tileWidth;
                var tileYLocationInField = Math.round(tileYPosInField / Piece.State.tileHeight);

                Field.tileOn(tileXLocationInField, tileYLocationInField);
            }
        });

        Field.doRowClears();
    },

    // If any rows in field are continous tiles, then clear them
    doRowClears: function() {
        var rowsToClear = [];

        this.State.gridState.eachRowWithIndex(function(row, rowNumber) {
            var entireRowFilled = true;
	
            // Is row filled?
            for (var tile in row) {
                if (row[tile] === 0) {
                    entireRowFilled = false;
                    break;
                }
            }

            // Set row to be cleared
            if (entireRowFilled) {
                rowsToClear.push(rowNumber);
            }
        });

        if (rowsToClear.length > 0) {
            this.explodeAndClearRows(rowsToClear);
        }
    },

    explodeAndClearRows: function(rowsToExplode) {
	Sound.playClearedSound(rowsToExplode.length);

        // Set up big animation rectangle to cover the disappearing rows
        var topRow = 1000;
        var bottomRow = 0;
        for (var row in rowsToExplode) {
            if (rowsToExplode.hasOwnProperty(row)) {
                if (topRow > rowsToExplode[row]) {
                    topRow = rowsToExplode[row];
                }
                if (bottomRow < rowsToExplode[row]) {
                    bottomRow = rowsToExplode[row];
                }
            }
        }

        var rowsTotalTopY = this.State.posY + (topRow * Piece.State.tileHeight);
        var rowsTotalBottomY = this.State.posY + (bottomRow * Piece.State.tileHeight) + Piece.State.tileHeight;
        var rowsTotalHeight = rowsTotalBottomY - rowsTotalTopY;

        var explodingRect = Graphics.createRectangleDiv("#C0ADFF", this.State.posX, rowsTotalTopY, Piece.State.tileWidth * this.WIDTH, rowsTotalHeight, 10);

        // Clear the actual tiles in grid before animating the large rectangle
        for (row in rowsToExplode) {
            if (rowsToExplode.hasOwnProperty(row)) {
                this.clearRow(rowsToExplode[row]);
            }
        }

        // Use a spiffy JQuery UI effect to "explode" the big rectangle
        $(explodingRect).hide("explode", {},
        1500);
       


        // Clean up
        Graphics.removeNodeFromGameContainer(explodingRect);

        // Shuffle remaining higher tiles downwards in field
        for (row in rowsToExplode) {
            if (rowsToExplode.hasOwnProperty(row)) {
                this.shiftTilesDownToRowX(rowsToExplode[row]);
            }
        }
    },

    clearRow: function(rowNo) {
        for (x = 0; x < this.State.gridState.length; x++) {
            this.tileOff(x, rowNo);
        }
    },

    shiftTilesDownToRowX: function(clearedRowY) {
        for (var y = (clearedRowY - 1); y >= 0; y--) { // Start at bottom to cascade tiles
            for (var x = 0; x < this.State.gridState.length; x++) {
                // Shuffle tile state down
                if (this.isTileOn(x, y)) {
                    this.tileOff(x, y);
                    this.tileOn(x, y + 1);
                }
                else {
                    this.tileOff(x, y + 1);
                }
            }
        }
    }

};