/*!
 * jPatchwork 
 *
 * Copyright (c) 2010 Héctor Paùl Cervera Garcia (jek_@hotmail.com)
 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
 *
 * Built on top of the jQuery library
 *   http://jquery.com
 *
 * Inspired by the "jpatchwork" by Jan Sorgalla
 *   http://sorgalla.com
 */

 function dump(obj) {
    var out = '';
    for (var i in obj) {
        out += i + ": " + obj[i] + "\n";
    }

    /* alert(out); */

    // or, if you wanted to avoid alerts...

    var pre = document.createElement('pre');
    pre.innerHTML = out;
    document.body.appendChild(pre)
}


(function($) {
    /**
     * Creates a patchwork aléatoire for all matched elements.
     *
     * @example $("#mypatchwork").jpatchwork();
     * @before <ul id="mypatchwork" class="jpatchwork-skin-name"><li>First item</li><li>Second item</li></ul>
     * @result
     *
     * <div class="jpatchwork-skin-name">
     *   <div class="jpatchwork-container">
     *     <div class="jpatchwork-clip">
     *       <ul class="jpatchwork-list">
     *         <li class="jpatchwork-item-1">First item</li>
     *         <li class="jpatchwork-item-2">Second item</li>
     *       </ul>
     *     </div>
     *     <div disabled="disabled" class="jpatchwork-prev jpatchwork-prev-disabled"></div>
     *     <div class="jpatchwork-next"></div>
     *   </div>
     * </div>
     *
     * @method jpatchwork
     * @return jQuery
     * @param o {Hash|String} A set of key/value pairs to set as configuration properties or a method name to call on a formerly created instance.
     */
    $.fn.jpatchwork = function(o) {
	    if (typeof o == 'string') {
            var instance = $(this).data('jpatchwork'), args = Array.prototype.slice.call(arguments, 1);
			
            return instance[o].apply(instance, args);
        } else
			return $(this).data('jpatchwork', new $jc(o));
    };

    // Default configuration properties.
    var defaults = {
		// Min time in seconds to change at the first time 
        minTime: 4,
		// Time in seconds between changes takes place
		timeChangeInterval: 4,
		// patchwork container
		p_container: null,
		// image container
		i_container: null,
		// loads random image at the beginning
		load_random: false,
		// truly random image inside each group (without repeating any other until having repeated everyone)
		random_image_group: false,
		// truly random group (without repeating any other until having repeated everyone)
		random_group: false,
		// animation duration (in seconds),
		animation_duration:2/* ,
		// sort of animation
        animation: 'normal' */
    };

    /**
     * The jpatchwork object.
     *
     * @constructor
     * @class jpatchwork
     * @param e {HTMLElement} The element to create the patchwork for.
     * @param o {Object} A set of key/value pairs to set as configuration properties.
     * @cat Plugins/jpatchwork
     */
    $.jpatchwork = function(o) {
		this.options    = $.extend({}, defaults, o || {});
		
		this.pc = typeof this.options.p_container=='object'?this.options.p_container:jQuery(this.options.p_container);
		this.ic = typeof this.options.i_container=='object'?this.options.i_container:jQuery(this.options.i_container);
		this.list 		= this.loadImages();
        
		var self = this;
	
        if ($.browser.safari) {
            $(window).bind('load.jpatchwork', function() { self.setup(); });
        } else
            this.setup();
    };

    // Create shortcut for internal use
    var $jc = $.jpatchwork;

    $jc.fn = $jc.prototype = {
        jpatchwork: '1.0'
    };

    $jc.fn.extend = $jc.extend = $.extend;

    $jc.fn.extend({
        /**
         * Setups the patchwork.
         *
         * @method setup
         * @return undefined
         */
        setup: function() {
            this.animating = false;
            this.timer     = null;
		    this.start();
        },

		
       
	   /**
         * Builds internal structure for "list" with every image
         *
         * @method get
         * @return {Number}
         */
        loadImages: function() {
			var list=new Array();
			if( this.options.imagesList )
			{
				
				var list=this.options.imagesList;
				for(i=0; i<list;++i){
					list[i].container=typeof list[i].container=='object'?list[i].container:jQuery(list[i].container);
				}
			}else{
				// load groups with new ID = "patch-[old group id]"
				this.ic.children('ul').each(function(i, e){
						list[i]={};
						a=list[i];
						a.change=0;
						e = jQuery(e);
						//a.container = 'patch-'+jQuery(e).attr('id');
						a.container = 'patch-'+jQuery(e).attr('id');
						// load every image in this group
						a.images=new Array();
						e.children('li').each(function(ii, ei){
							ima={};
							ei=jQuery(ei);
							ima.src=ei.find('img').first().attr('src')?ei.find('img').first().attr('src'):'';
							ima.alt=ei.find('img').first().attr('alt')?ei.find('img').first().attr('alt'):'';
							ima.text=ei.children('p').first()?ei.children('p').first():'';
							ima.href=ei.children('a').first()?ei.children('a').first().attr('href'):'';
							ima.change=0;
							a.images[ii]=ima;
						});
					});
			}
			return list;
        },
	   
	   /**
         * Gets a random images' group whose attribs "change" are 0
         *
         * @method get
         * @return {Number}
         */
        getRandomGroup: function() {
			var groupsValids = new Array();
			for(i=0; i<this.list.length;i++){
				/* dump(this.list[i]); */
				if(this.list[i].change==0) groupsValids.push(i);
			}
			if(groupsValids.length == 0)return false;
			return groupsValids[Math.round(Math.random() * (groupsValids.length-1) )];
        },
		
		
		/**
         * Sets all groups' change field to 0, that means next iteration they will be selectable for random animation
         *
         * @method set
         * @return undefined
         */
        setAllGroupsSelectables: function() {
			for(i=0; i<this.list.length;i++){
				this.list[i].change=0;
			}
        },
		
		/**
         * Evalues if every group has changed so far
         *
         * @method set
         * @return undefined
         */
        groupsFinished: function() {
			for(i=0; i<this.list.length;i++){
				if( this.list[i].change==0 )
					return false
			}
			return true;
        },
		
		/**
         * Sets un group not-selectable
         *
         * @method set
         * @return undefined
         */
        setGroupNotSelectable: function(i) {
			if(this.list.length > 1 && this.list[i])	this.list[i].change=1;
        },
		
		/**
         * Gets next group to animate
         *
         * @method get
         * @return {Number}
         */
        getNextGroup: function() {
			// Get next random group and reset others' values
			var g = this.getRandomGroup();
			this.setGroupNotSelectable(g);
			if(this.options.random_group || this.groupsFinished())	
				this.setAllGroupsSelectables();
			/* this.setGroupNotSelectable(g); */
			return g;
        },
		
		
		/**
         * Gets a random image from a group whose attrib "change" is 0
         *
         * @method get
		 * @param ig {Number} The index of the group.
         * @return {Number}
         */
        getRandomImage: function(ig) {
			if(ig===false)return false;
			var groupsValids = new Array();
			for(i=0; i<this.list[ig].images.length;i++){
				if(this.list[ig].images[i].change==0) groupsValids.push(i);
			}
			if(groupsValids.length == 0)return false;
			return groupsValids[Math.round(Math.random() * (groupsValids.length-1) )];
        },
		
		
		/**
         * Sets all images' change field to 0 from a group, that means next iteration they will be selectable for random animation
         *
         * @method set
		 * @param ig {Number} The index of the group.
         * @return undefined
         */
        setAllImagesSelectables: function(ig) {
			for(i=0; i<this.list[ig].images.length;i++){
				this.list[ig].images[i].change=0;
			}
        },
		
		/**
         * Evalues if a group has changed every image so far
         *
         * @method set
		 * @param g {Number} The index of the group.
         * @return undefined
         */
        imagesFinished: function(g) {
			for(i=0; i<this.list[g].images.length;i++){
				if( this.list[g].images[i].change==0 )
					return false
			}
			return true;
        },
		
		/**
         * Sets une image not-selectable
         *
         * @method set
		 * @param ig {Number} The index of the group.
		 * @param index {Number} The index of the image in its group.
         * @return undefined
         */
        setImageNotSelectable: function(ig, index) {
			if(this.list[ig].images.length > 1 && this.list[ig].images[index] )
					this.list[ig].images[index].change=1;
        },
		
		/**
         * Returns une image from given group index and image index 
         *
         * @method getImage
		 * @param ig {Number} The index of the group.
		 * @param index {Number} The index of the image in its group.
         * @return undefined
         */
        getImage: function(ig, index) {
			return this.list[ig].images[index];
        },
		
		/**
         * Returns un object jQuery with the group from given group index
         *
         * @method getGroupObj
		 * @param ig {Number} The index of the group.
		 * @return object jQuery
         */
        getGroupObj: function(ig) {
			return typeof this.list[ig].container == 'object'?this.list[ig].container:jQuery(this.list[ig].container);
        },
		
		/**
         * Gets next group to animate
         *
         * @method get
         * @return {Number}
         */
        getNextImage: function(g) {
			// Get next random image from the group selected and reset other images' values
			var i = this.getRandomImage(g);
			this.setImageNotSelectable(g, i);
			if(this.options.random_image_group || this.imagesFinished(g))	
				this.setAllImagesSelectables(g);
			/* this.setImageNotSelectable(g, i); */
			return i;
        },
        
		/**
         * Creates all html for a given image object jQuery
         *
         * @method buildPatchwork
         * @return undefined
         */
		buildImgHtml: function(ima) {
			return '<a class="panneau_border" href="'+ima.href+'"><img src="'+ima.src+'" alt="'+ima.alt+'"/></a>';
		},
		
		/**
         * Creates all html tags for the patchwork with internal data loaded in list
         *
         * @method buildPatchwork
         * @return undefined
         */
        buildPatchwork: function() {
			
			this.ic.css('display','none');
			var l = this.list;
			for(g=0;g<l.length;g++){
				// Get random image on load
				var id=typeof l[g].container=='object'?l[g].container.attr('id'):l[g].container;
				var index = (this.options.load_random)?this.getNextImage(g):0;
				var ima = l[g].images[index];
				if( ima != undefined ){
					this.pc.append('<div class="home_panneau" id="'+id+'">'+this.buildImgHtml(ima)+'</div>');
					this.setImageNotSelectable(g, index);
				}
				if(id.indexOf('#') !== 0)id = '#' + id;
				l[g].container=jQuery(id);
			}
			
			this.pc.css('display','block');
        },
		
		/**
         * Starts the patchwork with the loaded values.
         *
         * @method start
         * @return undefined
         */
        start: function() {
			var self = this;
			this.buildPatchwork();
			this.timer = setTimeout(function() { self.pass(); }, this.options.minTime * 1000);
        },
		
		
        /**
         * Animates the patchwork to next item.
         *
         * @method animate
		 * @param ig {Number} The index of the group.
		 * @param index {Number} The index of the image in its group.
         * @return undefined
         */
        animate: function(g, i) {
            this.animating = true;

            var self = this;
            var changed = function() {
			    self.animating = false;
				/* self.pass(); */
                self.notify('onAfterAnimation');
            };

            this.notify('onBeforeAnimation');
			
			
			
			// Animate stuff
			
			var ni=jQuery('<div>'+this.buildImgHtml(this.getImage(g,i))+'</div>'); 
			var gc=this.getGroupObj(g);
			
			if( gc !== undefined ){
				/* ni.css('display','none'); */
				gc.append(ni);
				
				ni.fadeIn(self.options.animation_duration*1000, function() {
					// Change tags elements
					// Move image
					ni.children('a').first().appendTo(gc);
					
					// Remove previous image
					gc.children('a').first().remove();
					// Remove empty div
					gc.children('div').first().remove();
					
					// Animation complete.
					changed();
				});
				 
			}
			
        },

		
		/* $('ul.hover_block li').hover(function(){
				$(this).find('img').animate({top:'182px'},{queue:false,duration:500});
			}, function(){
				$(this).find('img').animate({top:'0px'},{queue:false,duration:500});
			}); */

		
        /**
         * Animate (1 pass).
         *
         * @method auto
         * @return undefined
         * @param s {Number} Seconds to periodically autoscroll the content.
         */
        pass: function(s) {
			if (s != undefined)
                this.options.timeChangeInterval = s;
			/* alert(this.options.timeChangeInterval); */
			/* if (this.timer != null)
                return; */

			var g = this.getNextGroup();
			var i = this.getNextImage(g);
			
			var self = this;
			// Animer (opacity, etc)
			this.animate(g, i);
			
			this.timer = setTimeout(function() { self.pass(); }, this.options.timeChangeInterval * 1000);
        },

       

        /**
         * Notify callback of a specified event.
         *
         * @method notify
         * @return undefined
         * @param evt {String} The event name
         */
        notify: function(evt) {
            var state = this.prevFirst == null ? 'init' : (this.prevFirst < this.first ? 'next' : 'prev');

            // Load items
            this.callback('itemLoadCallback', evt, state);

            if (this.prevFirst !== this.first) {
                this.callback('itemFirstInCallback', evt, state, this.first);
                this.callback('itemFirstOutCallback', evt, state, this.prevFirst);
            }

            if (this.prevLast !== this.last) {
                this.callback('itemLastInCallback', evt, state, this.last);
                this.callback('itemLastOutCallback', evt, state, this.prevLast);
            }

            this.callback('itemVisibleInCallback', evt, state, this.first, this.last, this.prevFirst, this.prevLast);
            this.callback('itemVisibleOutCallback', evt, state, this.prevFirst, this.prevLast, this.first, this.last);
        },

        callback: function(cb, evt, state, i1, i2, i3, i4) {
            if (this.options[cb] == undefined || (typeof this.options[cb] != 'object' && evt != 'onAfterAnimation'))
                return;

            var callback = typeof this.options[cb] == 'object' ? this.options[cb][evt] : this.options[cb];

            if (!$.isFunction(callback))
                return;

            var self = this;

            if (i1 === 'undefined')
                callback(self, state, evt);
            else if (i2 === undefined)
                this.get(i1).each(function() { callback(self, this, i1, state, evt); });
            else {
                for (var i = i1; i <= i2; i++)
                    if (i !== null && !(i >= i3 && i <= i4))
                        this.get(i).each(function() { callback(self, this, i, state, evt); });
            }
        }

        
    });
    $jc.extend({
        /**
         * Gets/Sets the global default configuration properties.
         *
         * @method defaults
         * @return {Object}
         * @param d {Object} A set of key/value pairs to set as configuration properties.
         */
        defaults: function(d) {
            return $.extend(defaults, d || {});
        }
    });

})(jQuery);
