(function($) {
    /*
    *@summary  Arranges items on a page using dynamic animations.
    *          Allows hiding internal elements during animation and
    *          timed delay between items.
    *
    *@requires Each item must have a number in the class name immediately following the itemClassPrefix.
    *          For example: <div class="box-2">
    *          This allows easy re-ordering of the items in the markup against the grid order
    *          
    *@this {object} the container of the items          
    *
    *@param {array} positionGrid an array of start and end points of form [{start:{x:0,y:0},end:{x:10,y:10}]
    *@param {string} itemClassPrefix a string defining the prefix to use in the class query of each item (ex 'box-')
    *@param {string} internalQuery a string used as a query for the internal contents of each item
    *@param {object} [options] optional settings
    *@param {function} [callback] optional callback for the completion of each item's animation
    *
    *@return {object} the container object
    */

    $.fn.dynamicArrange = function(positionGrid, itemClassPrefix, internalQuery, minScreenSize, options, callback) {
        var opts = $.extend({}, $.fn.dynamicArrange.defaults, options);

        var defaultGrid = $.extend(true, {}, positionGrid);
        var changingGrid = positionGrid;

        //******* Helping functions **************

        //Delegate for setting timed action on item
        var setItemTimer = function(itemNumber, item, toX, toY) {
            setTimeout(function() {
                animateItem(item, toX, toY);
            }, (itemNumber * opts.slideInterval));
        }

        //Delegate for animation
        var animateItem = function(item, toX, toY) {
            item.animate(
                { "left": toX, "top": toY },
                opts.animateTime,
                opts.easing,
                function() { showInternals(item); }
            );
        }

        //Shows the inside of itemes
        var showInternals = function(item) {
            if (jQuery.support.opacity)
                item.find(internalQuery).fadeTo(opts.internalFadeTime, 1);
            else {
                item.find(internalQuery).css("visibility", "visible");
            }
        }

        //Adjusts the grid end positions to fit to screen
        var fitToScreen = function() {

            var windowDimensions = { "width": $(window).width(), "height": $(window).height() };
            var dimensionDiff = { "x": windowDimensions.width - minScreenSize.width, "y": windowDimensions.height - minScreenSize.height };

            //if screen > minScreenSize
            if (dimensionDiff.x > 0 && dimensionDiff.y > 0) {

                var itemCount = changingGrid.length;
                var delta = dimensionDiff.y * .3;

                //get direction of each object and expand
                for (var i = 0; i < itemCount; i++) {
                    //moves on x axis
                    switch (changingGrid[i].direction) {

                        case cardinals.north:
                            changingGrid[i].end.y = changingGrid[i].end.y - delta;
                            break;

                        case cardinals.south:
                            changingGrid[i].end.y = changingGrid[i].end.y + delta;
                            break;

                        case cardinals.east:
                            changingGrid[i].end.x = changingGrid[i].end.x + delta;
                            break;

                        case cardinals.west:
                            changingGrid[i].end.x = changingGrid[i].end.x - delta;
                            break;
                    }
                }
            }
        }

        //******* Process Main **************

        //Hide item internals
        if (jQuery.support.opacity) {
            $(internalQuery).fadeTo(0, 0);
        }
        else {
            // WARNING!!!!
            // HIDES HEADER IN IE 6/7
            //$(internalQuery).css("visibility", "hidden");
        }

        if (opts.expandWithScreen) { fitToScreen(); }

        //Adds resize handler to re-adjust to positions
        //TODO!!!! Not moving!
        $(window).resize(function() {
            if (opts.expandWithScreen) {
                //Foreach item, set current pos as start, and default end as end
                var itemCount = changingGrid.length;
                for (var i = 0; i < itemCount; i++) {
                    changingGrid[i].end.x = defaultGrid[i].end.x;
                    changingGrid[i].end.y = defaultGrid[i].end.y;
                }

                //fitToScreen();

                //Foreach item, animate into screen and fade show internal content
                for (var i = 0; i < itemCount; i++) {
                    var thisItem = $(this).find('.' + itemClassPrefix + (i + 1));
                    setItemTimer(i, thisItem, changingGrid[i].end.x, changingGrid[i].end.y);
                }
            }
        });

        //Set the callback to run after all animations
        //Note: Moved here due to possible performance hit if executing callback on each item
        //      Added 1.5 modifier to make up for any processing overhead time (estimate)
        if (callback) {
            setTimeout(function() {
                callback.call(this);
            }, (itemCount * 1.5 * opts.slideInterval));
        }

        var itemCount = changingGrid.length;

        //Foreach item, set inital position off screen
        for (var i = 0; i < itemCount; i++) {
            $(this).find('.' + itemClassPrefix + (i + 1)).css({ "left": changingGrid[i].start.x, "top": changingGrid[i].start.y });
        }

        //Foreach item, animate into screen and fade show internal content
        for (var i = 0; i < itemCount; i++) {
            var thisItem = $(this).find('.' + itemClassPrefix + (i + 1));
            setItemTimer(i, thisItem, changingGrid[i].end.x, changingGrid[i].end.y);
        }
        //___IMPORTANT___
        //
        //Don't put any code here, will run during animations and slow things down
        //

        return this;
    };

    // plugin defaults
    $.fn.dynamicArrange.defaults = {
        animateTime: 1000,
        easing: "easeOutExpo",
        slideInterval: 200,
        internalFadeTime: 600,
        expandWithScreen: false
    };

})(jQuery);
