/**
 * blockfade jQuery plugin
 * 
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * 
 * @author     Codefocus (http://www.codefocus.ca/)
 * @copyright  2011 Codefocus
 * @license    http://www.gnu.org/licenses/gpl-3.0.html
 * @version    0.2
 * @link       http://www.codefocus.ca/
 */
(function($) {
    
    var TIME_ESCAPE_STACK   = 50;
    var TIME_FADE           = 500;
    var TIME_NEXT_BLOCK     = 10;
    var SIMULTANEOUS_BLOCKS = 4;


/**
 *  blockfade class
 *
 */
    $.blockfade_slide = function(slide_options, container, callback) {
    //  Initialize.
    //  Keep myself in scope.
        var self            = this;
    //  Variables used in this class
        self.options        = slide_options;
        self.blocks         = [];
        self.block_parents  = [];
        self.callback       = null;
    //  Create container
        if (null === slide_options.href) {
            self.container = $('<div class="blockfader-container" />').hide();
        }
        else {
            self.container = $('<a class="blockfader-container" href="' + slide_options.href + '" />').hide();
        }
        
        container.append(self.container);
    //  Preload image
        self.image          = new Image();
        self.image.src      = self.options.image;
        self.image.onload   = function() {
        //  Image loaded.
        //  Calculate block size
            var block_w = (this.width / self.options.blocks_x);
            var block_h = (this.height / self.options.blocks_y);
        //  Create blocks
            var x;
            var y;
            var bg_x;
            var bg_y;
            for (y=0; y<self.options.blocks_y; ++y) {
                bg_y = y * block_h;
                for (x=0; x<self.options.blocks_x; ++x) {
                    bg_x = x * block_w;
                //  Create block with background
                    var block_parent = $('<div class="blockfader-block-parent" />')
                        .css('float', 'left')
                        .width(block_w)
                        .height(block_h);
                    var block = $('<div class="blockfader-block" />')
                        .width(block_w)
                        .height(block_h)
                        .css('background-image', 'url(' + self.image.src + ')')
                        .css('background-position', '-' + bg_x + 'px -' + bg_y + 'px')
                        .css('background-repeat', 'no-repeat')
                        .hide();
                //  Push to DOM
                    block_parent.append(block);
                    self.container.append(block_parent);
                //  Push to my blocks
                    self.blocks.push(block);
                    self.block_parents.push(block_parent);
                }   //  each x
            }   //  each y
        //  Execute the post-initialization callback
        //  But clear the stack by using a timeout
            setTimeout(callback, TIME_ESCAPE_STACK);
        };  //  image loaded
        
    //  ---------------
    //  Shuffle: Random
    //  ---------------
        self.shuffle_random = function(input) {
            var output = [];
            var num_input = input.length;
            var idx_input;
            var tmp_element;
            var rnd_element;
        //  Copy
            for (idx_input = 0; idx_input<num_input; ++idx_input) {
                output.push(input[idx_input]);
            }
        //  Shuffle
            for (idx_input = 0; idx_input<num_input; ++idx_input) {
            //  Swap this element with another random one
                rnd_element = Math.floor(Math.random() * num_input);
                if (rnd_element === idx_input) {
                //  No swap
                    continue;
                }
                tmp_element = output[rnd_element];
                output[rnd_element] = output[idx_input];
                output[idx_input] = tmp_element;
            }
            return output;
        };  //  function shuffle_random
        
    //  ------------------
    //  Show block parents
    //  ------------------
        self.show = function() {
        //  Resize the container to hold all the blocks
        //  TODO: +2 is SO ugly; properly round resize values
            self.container
                .css('width', self.image.width+2)
                .show();
        //  Show blocks
            /*
            $(self.block_parents).each(function(){
                $(this).show();
            });
            */
        };  //  function show
        
    //  ------------------
    //  Hide block parents
    //  ------------------
        self.hide = function() {
            self.container.hide();
            /*
        //  Hide blocks
            $(self.block_parents).each(function(){
                $(this).hide();
            });
            */
        };  //  function hide
        
    //  -------------
    //  Transition in
    //  -------------
        self.transition_in = function(callback) {
        //  Transition in
            self.callback       = callback;
        //  Shuffle blocks
            switch(self.options.transition_in.toLowerCase()) {
            case 'random':
            //  Block
                var blocks_shuffled = self.shuffle_random(self.blocks);
                break;
            }
        //  Effect
            var effect_interval = setInterval(function(){
                var block = blocks_shuffled.pop();
                if (typeof block == 'undefined') {
                //  No more blocks to fade in
                    clearInterval(effect_interval);
                //  Clean up
                    setTimeout(callback, TIME_FADE);
                    return;
                }
                block.fadeTo(TIME_FADE, 1);
            }, TIME_NEXT_BLOCK);
        };  //  function transition_in
        
    //  --------------
    //  Transition out
    //  --------------
        self.transition_out = function(callback) {
        //  Transition in
            self.callback       = callback;
        //  Shuffle blocks
            switch(self.options.transition_out.toLowerCase()) {
            case 'random':
            //  Block
                var blocks_shuffled = self.shuffle_random(self.blocks);
                break;
            }
        //  Effect
            var effect_interval = setInterval(function(){
                var block = blocks_shuffled.pop();
                if (typeof block == 'undefined') {
                //  No more blocks to fade in
                    clearInterval(effect_interval);
                //  Clean up
                    setTimeout(callback, TIME_FADE);
                    return;
                }
                block.fadeTo(TIME_FADE, 0);
            }, TIME_NEXT_BLOCK);
        };  //  function transition_out
        
        
        
    //  ---------------
    //  Run this slide!
    //  ---------------
        self.run = function(callback) {
        //  Show block parents.
            self.show();
        //  Transition in
            self.transition_in(function(){
            //  Transition done.
                setTimeout(function(){
                //  Displayed for [delay] milliseconds
                    self.transition_out(function(){
                    //  Transition done.
                    //  Hide block parents.
                        self.hide();
                    //  Run the callback.
                        callback();
                    });
                }, self.options.delay);
            });
        };  //  function run
        
        
        
        
    };  //  class blockfade




/**
 *  Function executed by jQuery
 *
 */
    $.fn.blockfade = function(slides) {
    //  Defaults
        var defaults = {
            image:          null,
            href:           null,
            transition_in:  'random',
            transition_out: 'random',
            blocks_x:       4,
            blocks_y:       4,
            delay:          5000
        };
    //  The containing element
        var container = $(this);
        container.addClass('blockfader-loading');
    //  Variables
        var blockfade_slides = [];
        var slides_to_load = slides.length;
        var active_slide = 0;
        
    //  Main show loop
        var run_slide = function() {
        //  Run the active slide
            blockfade_slides[active_slide].run(function(){
                if (++active_slide >= blockfade_slides.length) {
                    active_slide = 0;
                }
                run_slide();
            });            
        };
        
    //  Create a blockfade slide for each image
        var idx_slide = 0;
        for (idx_slide=0; idx_slide<slides.length; ++idx_slide) {
            var slide = $.extend(defaults, slides[idx_slide]);
        //  Create a blockfade slide
            blockfade_slides.push(
                new $.blockfade_slide(slide, container, function(){
                //  Slide for this image initialized
                    if (--slides_to_load === 0) {
                    //  All slides initialized.
                    //  Show
                        container.removeClass('blockfader-loading');
                        setTimeout(run_slide, TIME_ESCAPE_STACK);
                    }
                })
            );
        }
        
    };  //  function blockfade
    
})(jQuery);
