﻿// Here, we are going to define the jQuery puzzle
// plugin that will create the interface for each
// DIV that contains an image.
jQuery.fn.puzzle = function(intWidth, intHeight, intColumns, intRows, intShuffle, intSpeed) {

    jQuery.fn.extend({
        move: function(fn) {
            if (fn) {
                return jQuery.event.add(this[0], "move", fn, null);
            }
            else {
                var ret = jQuery.event.trigger("move", null, this[0], false, null);

                // if there was no return value then the even validated correctly  
                if (ret === undefined)
                    ret = true;

                return ret;
            }
        }
    });

    // Make sure that each of the parent elements
    // has a nested IMG tag. We don't want elements
    // that lack the image. Once we get those, then
    // loop over them to initialize functionality.
    //return this.filter(":has( li )").each(
    //function(intI) {
    // This is the functionality that will initialize 
    // the container and img for puzzle. This will only
    // be called ONCE the image has been loaded, and once
    // per each instance of the target puzzle.
    function InitPuzzle() {
        var jPiece = null;
        var intRowIndex, intColIndex, intI = 0;

        // Get the number of columns and rows.
        //intColumns = 5;
        //intRows = 3;

        // Get the puzzle width and height based on piece size
        intPuzzleWidth = intWidth * intColumns;
        intPuzzleHeight = intHeight * intRows;

        // Empty the container element. We don't actually
        // want the image inside of it (or any of the 
        // other elements that might be there).
        //jContainer.empty();

        // Set the container CSS and dimensions.
        jContainer
					.css(
						{
						    overflow: "hidden",
						    display: "block"
						}
						)
					.width(intPuzzleWidth)
					.height(intPuzzleHeight)
				;

        // Check to see how the container is positioned.
        // If is relative or absolute, we can keep it, 
        // but if it is not those, then we need to set 
        // is to relative explicitly.
        if (
					(jContainer.css("position") != "relative") &&
					(jContainer.css("position") != "absolute")
					) {

            // The container element is not explicitly 
            // positioned, so position it to be relative.
            jContainer.css("position", "relative");

        }

        var intMyRowIndex = 0;
        var intMyColumnIndex = 0;
        var intItemIndex = 0;
        //arr2DBoard[intMyRowIndex] = [];

        arr2DBoard[0] = [];

        $(jContainer).find("li").each(function() {

            $(this).css(
					{
					    display: "block",
					    float: "left",
					    cursor: "pointer",
					    position: "absolute",
					    top: ((intHeight * intMyRowIndex) + "px"),
					    left: ((intWidth * intMyColumnIndex) + "px")
					}
					)
				.width(intWidth)
				.height(intHeight)
			;

            $(this).find("a")
					.each(function(iAIndex, oA) {
					    $(oA).move(PieceMoveHandler);
					});

            arr2DBoard[intMyRowIndex][intMyColumnIndex] = this;

            if ((intItemIndex + 1) % intColumns == 0) {
                intMyRowIndex++;
                arr2DBoard[intMyRowIndex] = [];
                intMyColumnIndex = 0;
            }
            else {
                intMyColumnIndex++;
            }

            intItemIndex++;

        });

        // Make the last one opaque and give it a special "rel" 
        // value so that we can easily loacate this one later on.
        $(arr2DBoard[intRows - 1][intColumns - 1]).empty().attr("rel", "empty").css( { border: "none" } );

        // In order to shuffle the board, we are going to simulate 
        // a certain number of clicks. This is to ensure that any
        // state the board gets into, it is certain that the board
        // can get back into a "winning" state.

        for (intI = 0; intI < intShuffle; intI++) {

            // Select the piece that we want to "click".
            // We will do this by randomly selecting a row
            // and a column to click.
            jPiece = $(arr2DBoard[
						(Math.floor(Math.random() * intRows * intRows) % intRows)
						][
						(Math.floor(Math.random() * intColumns * intColumns) % intColumns)
						]).find("a:first");

            // Simulate the click.
            jPiece.move();
        }

        // Now that we have initialized, turn on the animation.
        blnShowAnimation = true;

        // Return out.
        return (true);
    }

    // This sets up the click handler for the pieces.
    function PieceMoveHandler(objEvent) {
        var didMove = false;

        // Get the jQuery objects for the piece clicked as
        // well as the empty square within the board.
        var jPiece = $(this).parent();
        var jEmpty = jContainer.find("li[ rel = 'empty' ]");

        // Get the CSS position for the current piece.
        var objPiecePos = {
            top: parseInt(jPiece.css("top")),
            left: parseInt(jPiece.css("left"))
        };

        // Get the CSS position for the empty piece
        var objEmptyPos = {
            top: parseInt(jEmpty.css("top")),
            left: parseInt(jEmpty.css("left"))
        };

        var intRowIndex, intColIndex = 0;


        // Check to see if we are in the middle of an animation.
        // If we are, then just return out since we don't want
        // to update values yet.
        if (blnInAnimation) {
            return (false);
        }


        // Blur the current piece to get rid of the dotted box.
        jPiece.find("a").blur();

        // Base on the CSS of the current piece and the size of
        // each of the pieces, we can calculate the row and column
        // of the given piece.
        objPiecePos.row = (objPiecePos.top / intHeight);
        objPiecePos.col = (objPiecePos.left / intWidth);

        // Base on the CSS of the empty piece and the size of
        // each of the pieces, we can calculate the row and column
        // of the given piece.
        objEmptyPos.row = (objEmptyPos.top / intHeight);
        objEmptyPos.col = (objEmptyPos.left / intWidth);


        // Now that we have the row and column of the target piece
        // as well as the empty piece, we can check to see if anything
        // needs to be moved. Remember, we ONLY need to move pieces
        // if the target piece and the empty piece share a row
        // or a column.

        // Check to see if they share the same row.
        if (objPiecePos.row == objEmptyPos.row) {

            didMove = true;

            // Check to see which direction we are moving in.
            if (objPiecePos.col > objEmptyPos.col) {

                // Move left.
                for (intColIndex = objEmptyPos.col; intColIndex < objPiecePos.col; intColIndex++) {
                    arr2DBoard[objPiecePos.row][intColIndex] = arr2DBoard[objPiecePos.row][intColIndex + 1];
                }

                // Put empty in place.
                arr2DBoard[objPiecePos.row][intColIndex] = jEmpty;

            } else {

                // Move right.
                for (intColIndex = objEmptyPos.col; intColIndex > objPiecePos.col; intColIndex--) {
                    arr2DBoard[objPiecePos.row][intColIndex] = arr2DBoard[objPiecePos.row][intColIndex - 1];
                }

                // Put empty in place.
                arr2DBoard[objPiecePos.row][intColIndex] = jEmpty;

            }


            // Update the CSS of the entire row (to make it easy).
            for (intColIndex = 0; intColIndex < intColumns; intColIndex++) {
                if (blnShowAnimation) {

                    // Flag that an animation is about to being.
                    blnInAnimation = true;

                    // Animate the CSS move.
                    $(arr2DBoard[objPiecePos.row][intColIndex]).animate(
								{
								    left: ((intWidth * intColIndex) + "px")
								},
								intSpeed,
								function() {
								    blnInAnimation = false;
								}
								);

                } else {

                    // Update the CSS for the given piece.
                    $(arr2DBoard[objPiecePos.row][intColIndex]).css(
								"left",
								((intWidth * intColIndex) + "px")
								);
                }

            }


            // Check to see if we should move vertically.
        } else if (objPiecePos.col == objEmptyPos.col) {

            didMove = true;

            // Check to see which direction we are moving in.
            if (objPiecePos.row > objEmptyPos.row) {

                // Move up.
                for (intRowIndex = objEmptyPos.row; intRowIndex < objPiecePos.row; intRowIndex++) {
                    arr2DBoard[intRowIndex][objPiecePos.col] = arr2DBoard[intRowIndex + 1][objPiecePos.col];
                }

                // Put empty in place.
                arr2DBoard[intRowIndex][objPiecePos.col] = jEmpty;

            } else {

                // Move down.
                for (intRowIndex = objEmptyPos.row; intRowIndex > objPiecePos.row; intRowIndex--) {
                    arr2DBoard[intRowIndex][objPiecePos.col] = arr2DBoard[intRowIndex - 1][objPiecePos.col];
                }

                // Put empty in place.
                arr2DBoard[intRowIndex][objPiecePos.col] = jEmpty;

            }


            // Update the CSS of the entire column (to make it easy).
            for (intRowIndex = 0; intRowIndex < intRows; intRowIndex++) {

                if (blnShowAnimation) {

                    // Flag that an animation is about to being.
                    blnInAnimation = true;

                    // Animate the CSS move.
                    $(arr2DBoard[intRowIndex][objPiecePos.col]).animate(
								{
								    top: ((intHeight * intRowIndex) + "px")
								},
								intSpeed,
								function() {
								    blnInAnimation = false;
								}
								);

                } else {

                    // Update the CSS for the given piece.
                    $(arr2DBoard[intRowIndex][objPiecePos.col]).css(
								"top",
								((intHeight * intRowIndex) + "px")
								);

                }

            }

        }

        // Return false so nothing happens.
        return (didMove);
    }

    // ASSERT: At this point, we have defined all the class
    // methods for this plugin instance. Now, we can act on
    // the instance properties and call methods.				

    // Get a jQUery reference to the container.
    var jContainer = $(this);

    // Get a jQuery reference to the first image 
    // - this is the one that we will use to make 
    // the image puzzle.
    //var jUl = jContainer.find("ul:first");

    // This is the array that will hold the 2-dimentional 
    // representation of the board.
    var arr2DBoard = [];

    // The height and width of the puzzle.
    var intPuzzleWidth = intColumns * intWidth;
    var intPuzzleHeight = intRows * intHeight;

    // Flag for wether or not to show animation.
    var blnShowAnimation = false;

    // Flag for wether or not an animation is in the midst. We
    // are going to need this to prevent further clicking during
    // and anmiation sequence.
    var blnInAnimation = false;


    // The image has loaded so call Init.
    InitPuzzle();
    //}
    //);
}