/*


##########################################################################
 ###
 ####	
 ####	j3dcarousel plugin
 ####
 ####	v 0.0.1 (19-12-2009)
 ####
 ####	Federico Ghedina fedeghe@gmail.com, federico.ghedina@sitebysite.it
 ####	
 ###
##########################################################################


*/
(
	function($){  

		$.fn.jQuery_3d_carousel = function(options, polys, func){
			
			var CAROBJ__ = this;
			
			////////////////////////////
			// UTILITY
			if(typeof Object.create !== 'function'){
				Object.create = function(obj){
					var F = function(){};
					F.prototype =obj;
					return new F();
				};
			}
			
			
			/*	OBJECTS */
			
			/*	
			#################################################
			## ANIMATOR object
			## son of wheel
			*/
			var animator = function(obj){
				this.grandparent = obj.parent;
				this.parent = obj;
				
				this.phase = 0;

				this.count = 0;
				
				
					
				this.move = function(versus){
					var c = this.grandparent;
					var w = c.wheel;

					for(var i = 0, j = w.current; i < w.size; i++, this.count++){
						
						
						if(versus === 'cw'){
							j = (j+w.size -1) % w.size;
						//	this.phase =  j * ( Math.PI * 2 ) / w.size;
							w.containers[j].angle += opts.speed ;
							
						}else{
							j = (j+w.size+1)%w.size;
						//	this.phase =  (w.size-j) * ( Math.PI * 2 ) / w.size;
							w.containers[j].angle -= opts.speed ;
						}
						
						
						var xfact = Math.cos( w.containers[j].angle ),
							yfact = Math.sin( w.containers[j].angle );

						var x = parseInt(xfact * opts.radiusX + opts.centerX, 10),
							y = parseInt(yfact * opts.radiusY + opts.centerY, 10);
							
						w.containers[j].position.set(x, y);
						w.containers[j].opac = Math.abs( Math.cos((i+1)*(Math.PI/w.size))).toFixed(2);
						
						var factor	= (1+ Math.sin(w.containers[j].angle)) / 2;
						
						var width 	= opts.max_width/opts.min_max_ratio + factor * opts.max_width,
							height 	= opts.max_height/opts.min_max_ratio + factor * opts.max_height;
						
						w.containers[j].zindex = parseInt(height,10);						
						w.containers[j].dimension.set(width,height);						
						w.containers[j].position.set(x-width/2, y-height/2);
					}
					w.update();					
				};

				this.turn = function(index, versus){
					var that = this;
					var c = this.grandparent;
					var w = c.wheel;
					
					/* uncolor current */
					w.evidence(false);					
					
					var width = parseInt(jQuery('#'+opts.image_base_id+index).parent().css('width'), 10);
					
					var xxx = window.setInterval(
						function(){
							var compare = {
								'cw' : function(first, second){
									return (first > second);
								},
								'ccw' : function(first, second){
									return (first < second);
								}					
							};
							
							var left = w.containers[index].position.left + w.containers[index].dimension.width / 2;
							var v = compare[versus].call(this, left, opts.centerX );
							/* debug(v); */
							if(v){
								/* not enough */
								that.move(versus);
							}else{			
								/* ok, stop it */
								window.clearInterval(xxx);
								/* and get real polygons */
								w.get_real();
							}
						},
						25
					);
					w.current = index;
					
					/* color new current */
					w.evidence(true);
					
					/* set true opposite */
					this.opposite = (index + w.size/2) % w.size;					
				};
			};
			
			
			/*				
			###########################################################		
			## AUTOSTARTER object
			*/
			var autostarter = function(obj){
				var c = obj;
				var w = c.wheel;
			
				/* automatic rightclick event */
				var auto = false;
				/* actity event flag */
				var stop = false;
				/* event calling counter */
				var chiamate = 0;
				
				// for external use from click handling on images(just to help stopping autorotating)
				this.auto = false;
			
				/* book rightclick every auto_rotate_timeout sec */
				this.start_auto = function(after){	
					var that = this;			
					that.unbind_auto();				
					after = after || 0;
					/* reset default */
					c.versus = opts.versus;
					
					window.setTimeout(
						function(){
							/* equivalente a chiamare stop_auto(); */
							stop = true;
					
							window.clearInterval(auto);

							auto = window.setInterval(
								function(){

									switch(c.versus){
										case 'cw': c.panel.click_left(); break;
										case 'ccw': c.panel.click_right(); break;
									}
									stop = false;
								},
								opts.auto_rotate_timeout * 1000
							);
							
							
							
							// only if panel is loaded hover event stop rotation
							// control needed even in init
							if(opts.load_panel){
								that.bind_auto();
							}
							
							// to set it for external use from click handling on images(just to help stopping autorotating)
							that.auto = auto;
							
						}
						, after * 1000
					);
					
				};
					
				this.stop_auto = function(){

					stop = true;
					window.clearInterval(auto);
				};

				this.unbind_auto = function(){
					jQuery('#'+conf.car_id+'').unbind('hover');
				/*	jQuery('#'+conf.car_id+'').hover(
						function(){
							// debug('nada in '); 
						},
						function(){
							// debug('nada out '); 
						}
					);*/
				};


				this.bind_auto = function(){
					var that = this;
					jQuery('#'+conf.car_id+'').hover(
						function(){
							if(stop === false){
								that.stop_auto();
								stop = true;
							}
						},
						function(){
							if(stop === true){
								that.start_auto(opts.restart_after);
								stop = false;
							}	
						}
					);
				};
				
				this.init = function(){
					var that = this;
					if(opts.autostart){
						that.start_auto();
						// only if panel is loaded hover event stop rotation
						// control needed even in start_auto
						if(opts.load_panel){
							that.bind_auto();
						}
					}
				};
				this.init();
			};
			
			
					
			/*				
			###########################################################		
			## MAIN OBJECT element of carousel OBJ
			*/
			var carousel_obj = function(opt){
				
				
				//IEja browser
				this.is_IE = (document.all && !window.opera);
				
				this.autostarter = false;
				
				/* position of the div containig all the carousel; set in init() calling get_position()  */
				this.position = false;			
				
				/* dimensions */
				this.dimension = new dimension(opts.carousel_width, opts.carousel_height);				

				/* versus of the movement */
				this.versus = opts.versus;
			
				/* action panel */
				this.panel = false;

				/* preloader */
				this.imager = false;

				/* event handler */
				this.event_handler = false;

				/* one wheel */
				this.wheel = false ;
				
				
				
				/* initialize */
				this.init = function(){				
				
					/* 	define main position */
					this.get_position(CAROBJ__);
					/* create wheel passing carousel_obj(autoinit) */
					this.wheel = new wheel(this);
					/*  if not specified differently, load the panel and render */
					this.panel = new panel(this);
					if(opts.load_panel){ this.panel.render();}
					this.event_handler = new eh(this);
					
					// if arrow key event is enabled 
					// if(opts.enable_keyboard)this.event_handler.bind_hard_arrows();	
					/* start */
					this.autostarter = new autostarter(this);
				};
				
				this.init_loader = function(){
					/* image preloader (no autoinit) */
					var that = this;
					this.imager = new imager(
						function(){
						
							/* get the object position using general localizator UTILITY__.get_location() */
							that.get_position = function(element){
								var ret = UTILITY__.get_position(element);
								conf.carouselX = parseInt(ret.left, 10);
								conf.carouselY = parseInt(ret.top, 10);
								that.position = ret;
							};	
						
							that.init();
						}
					);	
					
					
					this.imager.show_loader();
					this.imager.go();			
					//this.imager.remove_loader();	
					
				};
				
				/*#############MAIN*/
				this.init_loader();			
			};
			
			
			/*
			################################################
			##	CONTAINER :: elements in the wheel
			*/
			var container = function(htmlObj, index){
				this.html = htmlObj;			
				/* actual position */
				this.position = new position(0, 0);				
				/* next position */
				this.next_position = new position(0, 0);				
				this.zindex = false;				
				this.opac = false;				
				this.dimension = new dimension(0, 0);				
				this.polygon = false;				
				this.image = false;				
				this.id = opts.image_base_id+''+index;				
				this.index = index;				
				this.init = function(){};				
				/* phase used in animation */
				this.angle = 0;								
				this.douwantme = function(point){
					return this.polygon.is_in(point);
				};
				this.set_dimension = function(w,h){
					this.dimension.set(w,h);
				};				
				this.init();
				
			};	

			/*
			####################################################		
			## DIMENSION :: OBJ
			*/
			var dimension = function(w, h){
				this.width = w;
				this.height = h;
				/* */
				this.set = function(w, h){
					this.width = w;
					this.height = h;
				};
			};

			/*
			################
			##	EVENT HANDLER OBJ
			##	sets click callback functions on arrow and images
			*/
			var eh = function(obj){	
				//hover
				this.hover = false;
			
				this.parent = obj;		
				/* obj is an carousel_obj instance */
				this.init = function(){
					/* bind_panel ?*/
					this.may_bind_arrows()
						/* bind containers */
						.may_bind_containers();
						// for keyboard arrows event
						//.bind_hard_arrows();
					
					// if wheel is enabled
					if(opts.enable_wheel){this.wheelme();}

				};
				
				this.wheelme = function(){
				
					/*SPERIMENTALE mouse wheel*/
					/** This is high-level function.
					* It must react to delta being more/less than zero.
					*/
					var that = this;
					function handle(delta) {
							//var p_p = this.parent.panel;
							if (delta < 0){
								that.parent.panel.click_right();
								//console.debug(that.parent.panel);
								//p_p.click_left();//console.debug('up');
							}else{
								that.parent.panel.click_left();
								//console.debug(that.parent.panel);
								//p_p.click_right();
							}
							//console.debug('down');
					}

					/** Event handler for mouse wheel event.
					 */
					function wheel(event){
						
							//that.parent.autostarter.unbind_auto();
							
							var delta = 0;
							if (!event){ /* For IE. */
								event = window.event;
							}
							if (event.wheelDelta) { /* IE/Opera. */
								delta = event.wheelDelta/120;
								/** In Opera 9, delta differs in sign as compared to IE.
								 */
								if (window.opera){
									delta = -delta;
								}
							} else if (event.detail) { /** Mozilla case. */
								/** In Mozilla, sign of delta is different than in IE.
								 * Also, delta is multiple of 3.
								 */
								delta = -event.detail/3;
							}
							/** If delta is nonzero, handle it.
							 * Basically, delta is now positive if wheel was scrolled up,
							 * and negative, if wheel was scrolled down.
							 */
							if (delta){
								handle(delta);
							}
							/** Prevent default actions caused by mouse wheel.
							 * That might be ugly, but we handle scrolls somehow
							 * anyway, so don't bother here..
							 */
							if (event.preventDefault){
								event.preventDefault();
							}
									
							//that.parent.autostarter.bind_auto();
									
						event.returnValue = false;
					}

					/** Initialization code. 
					 * If you use your own event management code, change it as required.
					 */
					if (window.addEventListener){
						/** DOMMouseScroll is for mozilla. */
						document.getElementById(CAROBJ__.attr('id')).addEventListener('DOMMouseScroll', wheel, false);
					}
					/** IE/Opera. */
					document.getElementById(CAROBJ__.attr('id')).onmousewheel = wheel;
				
				}
				
				
				
				
				this.bind_hard_arrows = function(){
					var p_p = this.parent.panel;
					jQuery(document).keyup(
						function(event){ 
							var dx_label = (opts.invert)?37:39;
							var sx_label = (opts.invert)?39:37;
							switch(event.keyCode){
								case dx_label:
									p_p.click_left();
								break;
								case sx_label:
									p_p.click_right();
								break;
							}
						}
					);
					return this;
				};
				this.may_bind_arrows = function(){
					if(opts.load_panel){				
						/* in case invert is opts passed as true */
						if(opts.invert){
							this.parent.panel.arrow_DX.bind('click', this.parent.panel.click_left);
							this.parent.panel.arrow_SX.bind('click', this.parent.panel.click_right);
						}else{
							this.parent.panel.arrow_DX.bind('click', this.parent.panel.click_right);
							this.parent.panel.arrow_SX.bind('click', this.parent.panel.click_left);
						}
					}
					return this;
				};
				
				this.may_bind_containers = function(){
					if(opts.load_panel){
						var p = this.parent;
						jQuery('#'+conf.car_id+' div.'+conf.car_class).bind(
							'click',
							function(e){
								p.versus = p.panel.get_versus(e);							
								/* clicked */
								var tmp_id = jQuery(this).find('img').attr('id');
								var ret = UTILITY__.get_relative_parent(e, tmp_id);							
								/* get real wanted
								indexing this way is a "speedy fuffa" */
								tmp_id = p.wheel.get_wanted(UTILITY__.myid(tmp_id), ret);

								if(tmp_id !== -1){
									/* update current */
									p.wheel.refresh(tmp_id);
									p.wheel.go(tmp_id, ret);
								}else{
									;/* debug('no target'); */
								}
								//help stopping autostart
								window.clearInterval(p.autostarter.auto);
							}
						)
						/*
						.bind('mousemove',
							function(e){
								p.versus = p.panel.get_versus(e);							
							
								var tmp_id = jQuery(this).find('img').attr('id');
								var ret = UTILITY__.get_relative_parent(e, tmp_id);							
							
								tmp_id = p.wheel.get_wanted(UTILITY__.myid(tmp_id), ret);

								if(tmp_id !== -1){
									console.debug(tmp_id);
								}
							}
						);
						*/
					}
					return this;
				};
				/* MAIN */
				this.init();	
			};
			
			/*
			#######################################		
			##	IMAGE LOADER of carousel OBJ
			*/
			var imager = function(callback){
			
				if(callback==undefined){
					this.callback = function(){}
				}
				else{
					this.callback = callback;
				}
				
				this.num = 0;
				this.images = new Array();
				this.spinner = false;
				/*	damn no literal true length !!!	*/
				this.count = function(){
					for(var im in polys) this.num++;
				};
				/*	adding	*/
				this.show_loader = function(){		
					var gauge = jQuery('<div>').attr('id',opts.car_class+'preloader_gauge').css({
						position : 'absolute',
						left : '0px', top : '0px',
						backgroundColor : '#F3C009',
						margin : '1px',
						width:'10%', height:'10px',
						zIndex : 0
					});
					var loader = jQuery('<div><span style="position:absolute;z-index:3;color:#ffffff;">LOADING...</span></div>').attr('id',opts.car_class+'preloader_container').css({
						position : 'absolute',
						left : opts.centerX-50,	top : opts.centerY-8,
						height:'10px', width : '100px',
						border : '1px solid #ffffff',
						padding : '1px',
						fontSize : '8px',
						fontFamily : 'arial'
					}).append(
						gauge
					);
					jQuery(CAROBJ__).append( loader );
				};
				
				// queste due funzioni le sposto prima del go perchè altrimenti si incazza e non le trova
				
				this.update_loader = function(val){
					jQuery('#'+opts.car_class+'preloader_gauge').css('width',val+'%');
				};
				
				this.remove_loader = function(){
					jQuery('#'+opts.car_class+'preloader_container').hide();
				};
				
				this.go = function(){

					var count  = 0;
					this.count();
					var step = Math.floor(100/this.num);
					
					var that = this;
					
					/*
					var t = window.setInterval(
						function(){
							var that = this;
							var im = polys[count];
							var tmp_im = new Image();
							var tmp_im_bw = new Image();
							tmp_im.src = conf.base_path +'/img/gallery/'+ im;
							tmp_im_bw.src = conf.base_path +'/img/gallery/bw/'+ im;

							++count;
							(that.images).push(tmp_im);
							that.update_loader(count * step);	
							//console.debug('helo');
							if(count === that.num)window.clearInterval(t);
						},
						10
					);
					*/	
					
					/*
					conto gli elementi inquanto literal
					*/
					
					var polys_length = 0;
					
					for(var im in polys){
						++polys_length;
					}
					
					for(var im in polys){

						var tmp_im = new Image();
						tmp_im.src = opts.base_path + opts.img_path + im;


						/*
						//tolgo l'if perchè voglio caricare le immagini a colori e quelli in bw indistintamente
						
						if(opts.evidence_first){
							var tmp_im_bw = new Image();
							tmp_im_bw.src = opts.base_path + opts.img_path + 'bw/' + im;
						}
						*/
						
						var tmp_im_bw = new Image();
						tmp_im_bw.src = opts.base_path + opts.img_path + 'bw/' + im;
						
						
						
						(this.images).push(tmp_im);
						
						jQuery(tmp_im,tmp_im_bw).load(
							function(){
								++count;
								that.update_loader(count * step);
								if(count == polys_length){
									
									//sono arrivato all'ultimo elemento
									that.update_loader(99); //messo solo per coerenza inquanto l'aggiornamento a 99 e la seguente rimozione del loader risultano instantanee
									that.remove_loader();
									that.callback();
									
								}
							}
						);
					}
					
					//this.update_loader(99);
				};

			};
			
			
			/*
			##############################
			## PANEL object
			*/
			var panel = function(obj){
				this.init = function(){
					this.left_element_id = 'jc3d_arrow_left';
					this.right_element_id = 'jc3d_arrow_right';
					this.arrow_SX = jQuery('<div>').attr('id',this.left_element_id).append(
						jQuery('<img>').attr({
							src:opts.base_path+'/img/utils/sx.png',
							title:opts.titles.prev
						})
					).css({left:'10px', position:'absolute', bottom:'10px', cursor:'pointer', zIndex:1000});
					this.arrow_DX = jQuery('<div>')	.attr('id',this.right_element_id).append(
						jQuery('<img>').attr({
							src:opts.base_path+'/img/utils/dx.png',
							title:opts.titles.next
						})
					).css({right:'10px', position:'absolute', bottom:'10px', cursor:'pointer', zIndex:1000});
				};
				this.click_left = function(e){			
					var i = (obj.wheel.current + obj.wheel.size - 1) % obj.wheel.size;
					obj.wheel.animator.turn( i , 'cw');
					//window.clearInterval(obj.autostarter.auto);
					opts.personalizzata(jQuery('#'+opts.image_base_id+i).parent());
				};
				this.click_right = function(e){
					var i =(obj.wheel.current + obj.wheel.size + 1) % obj.wheel.size;
					obj.wheel.animator.turn( i , 'ccw' );
					//window.clearInterval(obj.autostarter.auto);
					opts.personalizzata(jQuery('#'+opts.image_base_id+i).parent());
				};
				this.render = function(){	
					/* create two div and position abs */
					jQuery(CAROBJ__).append(this.arrow_SX, this.arrow_DX);
				};
				this.get_versus = function(ev){
					return (typeof ev === 'undefined') ?  opts.start_versus : (ev.pageX-conf.carouselX > opts.centerX)?'cw':'ccw';
				};
				this.init();
			}
			
			
			/*
			#############################
			## POINT object
			*/
			var point = function(x, y){
				this.x = x || 0;
				this.y = y || 0;
				/* draw point */
				this.draw = function(mis, cl){
					mis = mis || 0;
					cl = cl || false;
					var punto = jQuery('<div>').css({
						/*width			:	(mis + 1) + 'px',
						height			:	(mis + 1) + 'px',*/
						backgroundColor	:	'red',
						position		:	'absolute',
						left			:	(this.x - mis) + 'px',
						top				:	(this.y - mis) + 'px',
						width 			:	(1 + 2 * mis) + 'px',					
						height 			:	(1 + 2 * mis) + 'px'
					});
					if(cl){
						jQuery(punto).attr('class',cl);
					}
					jQuery('#'+conf.car_id).append(punto);
				};
				this.set = function(x, y){
					this.x = x;
					this.y = y;
				};
				this.show = function(){
					return 'x: '+this.x+' ;  y: '+this.y;
				};
			};
			
			/*
			###########################################
			## POLYGON object
			*/
			var polygon = function(parent, points){
				/*parent container object*/
				this.parent = parent;			
				this.perc_points = points;				
				this.real_points = new Array(); 
				
				/*
				#	INTERNAL
				##	USED BY is_in
				##	calculate real_points (array of 'point') for this object relatively to 'dimension' dim
				*/
				this.get_real = function(){
					for(var i = 0, len = this.perc_points.length; i < len; i++ ){
						var x = Math.floor(this.parent.dimension.width * this.perc_points[i][0] / 100),
							y = Math.floor(this.parent.dimension.height * this.perc_points[i][1] / 100);
						/* set or reset */
						if(!this.real_points[i]){
							this.real_points[i] = new point(x, y);
						}else{
							this.real_points[i].set(x, y);
						}
					}
				};
				
				this.render = function(){
					var temp = this.perc_points;
					for(var i1 = 0 , i2 = 1,  len = temp.length; i2 < len; i1++, i2++ ){
						UTILITY__.drawLine(this.real_points[i1], this.real_points[i2], jQuery('#'+image_base_id+parent.id).parent() );
					}
					
				};
				
				/* function discover if a point is in or out the polygon */
				this.is_in = function(point){
					/*every time maybe we have to get it real*/
					if(this.real_points.length === 0){this.get_real();}
					var poly = this.real_points;
					var c = false;
					for(var i = -1, l = poly.length, j = l - 1; ++i < l; j = i){
						((poly[i].y <= point.y && point.y < poly[j].y) || (poly[j].y <= point.y && point.y < poly[i].y)) &&
						(point.x < (poly[j].x - poly[i].x) * (point.y - poly[i].y) / (poly[j].y - poly[i].y) + poly[i].x) &&
						(c = !c);
					}
					return c;					
				};	
			};
			
			
			/*
			#########################################
			## POSITION OBJ
			*/
			var position = function(l, t){
				this.left = l;
				this.top = t;
				this.set = function(l, t){
					this.left = l;
					this.top = t;
				};
				this.show = function(){
					return 'left: '+this.left+' ;  top: '+this.top;
				};
				this.get = function(){
					return {left: this.left, top:this.top};
				}
			};
			
			
			
			/*
			################################
			## WHEEL OBJ ::
			*/
			var wheel = function(obj){
				
				/* carousel parent */
				this.parent = obj;
				
				/*  */
				this.animator = false;
				
				/* number of elements in the wheel */
				this.size = 0;
				
				/* the current element in evidence */
				this.current = 0;
				
				/* the next position it is going to move*/
				this.next = 0;
				
				/* ellipse center */
				this.center = new position(opts.centerX || 0, opts.centerY || 0);
				
				/* rotation ellipse */
				this.radiusX = opts.radiusX || 0;
				this.radiusY = opts.radiusY || 0;
				
				/* divs which contains carousel images */
				this.containers = new Array();
				
				/* relative opposite to the current (float if odd number of elements) */
				this.opposite = 0;
				
				//  everytime this.current is updated, this.opposite must be updated to (this.current + this.size/2) % this.size
				/*
				#	
				#	initialize
				#
				*/
				this.init = function(){
					
					//in case, shake array
					if(opts.rand_order){
						polys = UTILITY__.mix_literal( polys );
					}
					
				
					this.animator = new animator(this);
									
					/* create containerts and set their position */
					this.init_containers()
						/* get right initial position */
						.init_containers_position()
						/*  */
						.position_all()
						/* now i have dimensions and can get real polygon dimnsions */
						.get_real()
						/* and render */
						.render();
					
					/* NOW MUST ::::::::: notify eh to activate events on containers (cannot be done before appending) */
				};
				
				/*
				# recalculate realpolys and opposite
				*/
				this.refresh = function(){
				
					//opposite
					/*
					switch(obj.versus){
						case 'cw': this.opposite = (this.current + Math.ceil(this.size/2) ) % this.size; break;
						case 'ccw': this.opposite = (this.current + Math.floor(this.size/2) ) % this.size; break;
					}
					*/
					this.opposite = (this.current + Math[((obj.versus === 'cw')?'ceil':'floor')](this.size/2) ) % this.size;
				}
				
				
				/*
				#
				# create containers without positioning nor dimensioning
				#
				*/
				this.init_containers = function(){					
					/* create all internal divs	 */
					
					for(var p in polys){
						var path = (this.size===0)?'':((opts.evidence_first)?'bw/':'');
						this.containers[this.size] = new container(
							jQuery('<div>').attr({
								'class':opts.car_class,
								'alt':polys[p].alt
							}).append(
								jQuery('<img>').attr({
									'src':opts.base_path+opts.img_path+'/'+path+p,
									'id':opts.image_base_id+''+this.size,
									'cursor':'pointer'
								})
								.css({'width':'100%','height':''})
							),
							//id for autoindexing container
							this.size							
						);
						/* set perc polygon */
						this.containers[this.size].polygon = new polygon(this.containers[this.size], polys[p].poly);
						this.size++;
					}
					return this;										
				};
				
				/*
				#	INTERNAL
				#	set initial container position
				#
				*/
				this.init_containers_position = function(index){
					for(var i = this.current, j = 0, len = this.size; j< len; j++, i=(++i)%(this.size)){
						var phase =  i  * ( Math.PI * 2 ) / len;
						var angle = Math.PI/2 + phase;
						var xfact = Math.cos( Math.PI/2 + phase ),
							yfact = Math.sin( Math.PI/2 + phase ),
							cx = parseInt(xfact * opts.radiusX + opts.centerX, 10),
							cy = parseInt(yfact * opts.radiusY + opts.centerY, 10);
						
						/* setting ~truly position element in each container */
						this.containers[i].position.set(cx, cy);
						this.containers[i].angle = angle;
						
						if(opts.dbg){
							var temp = new point(cx,cy);
							temp.draw(1);
						}
					}
					/* chain */
					return this;
				};
				
				this.position_all = function(){
					/* position and opacity */
					for(var i = this.current, j = 0, len = this.size; j< len; j++, i=(++i)%(this.size)){
						/* opacity */
						
						/* for dimensioning containers */
						var factor	= (1+ Math.sin(this.containers[j].angle)) / 2;
						
						var opac 	= Math.abs(Math.cos(i*(Math.PI/this.size))).toFixed(2),
							min_w	= Math.ceil(opts.max_width/opts.min_max_ratio),
							min_h 	= Math.ceil(opts.max_height/opts.min_max_ratio),
							width 	= parseInt(opts.max_width  / opts.min_max_ratio + factor * opts.max_width, 10),
							height 	= parseInt(opts.max_height / opts.min_max_ratio + factor * opts.max_height, 10),
							pos 	= this.containers[i].position,
							zindex	= pos.top;
							
						this.containers[i].opac = opac;
						this.containers[i].zindex = zindex;
						this.containers[i].dimension.set(width, height);
						
						(this.containers[i].html).css({
							'position':'absolute',
							'left':pos.left,
							'top':pos.top,
							'zIndex':zindex
							//,'opacity':opac
						});

						//if is NOT IEJA
						if(!this.parent.is_IE){
							jQuery(this.containers[i].html).css({'opacity':opac});
						}
						
						
						//adjust position property
						this.containers[i].position.set(
							this.containers[i].position.left - this.containers[i].dimension.width / 2,
							this.containers[i].position.top - this.containers[i].dimension.height / 2
						);
						
						//displaying all ? 
						if(!opts.show_all){
							this.decide_bw(j);
						}
						
					}
					/* chain */
					return this;
				};
				
				/* Wrapper for getting real polygons */
				this.get_real = function(){
					for(var i = 0, len = this.size; i< len; i++){
						this.containers[i].polygon.get_real();
					}
					/* chain */
					return this;
				};
				
				
				/*  */
				this.render = function(){

					for(var i = 0; i < this.size; i++ ){
						
						jQuery(this.containers[i].html).css({
							'width': this.containers[i].dimension.width,
							'height': this.containers[i].dimension.height,
							'left' : this.containers[i].position.left,
							'top'  : this.containers[i].position.top
						});
						if(opts.dbg){
							jQuery(this.containers[i].html).css({
								'border': '1px solid red'
							});
						}
						jQuery(CAROBJ__).append(this.containers[i].html);
					}
				};
				
				this.update = function(){
					for(var i = 0; i < this.size; i++ ){
						jQuery(this.containers[i].html).css({
							'width'		:this.containers[i].dimension.width,
							'height'	:this.containers[i].dimension.height,
							'left' 		:this.containers[i].position.left,
							'top'  		:this.containers[i].position.top,
							'zIndex'	:this.containers[i].zindex
							//,'opacity'	:this.containers[i].opac
						});
						
						//if is NOT IEJA						
						if(!this.parent.is_IE){
							jQuery(this.containers[i].html).css({'opacity':this.containers[i].opac});
						}
						
						jQuery('img', this.containers[i].html).css({
							'width'		:this.containers[i].dimension.width,
							'height'	:this.containers[i].dimension.height
						});

						if(!opts.show_all){
							this.decide_bw(i);
						}
					}
				};

				/* show bw or color image ? */
				this.decide_bw = function(i){
					var inrange = UTILITY__.get_vis_range(i, this.current, opts.show_num, this.size);
					jQuery(this.containers[i].html).css({
						'display' : inrange ? 'block' : 'none'
					});
				};

				/* uses animator */
				this.turn = function(index,ev){		
					if(this.current !== index){
						this.animator.turn(index, this.parent.versus);
					}
				};
				
				/*
				This is called by EH, do: before_click, personalized, turn, after_click
				EH passes REAL id of targeted container, and set versus
				*/				
				this.go = function(id, ev){
					//before click
					opts.before_click();						
					/*	funzione personalizzata */
					opts.personalizzata(jQuery('#'+opts.image_base_id+id).parent());
					/* internal */
					this.turn(id, ev);
					/* after click */
					opts.after_click();
				};

				/* get the index of the clicked silhouette, or -1  */
				this.get_wanted = function(index, ret){
					var next = {
						cw : function(index){
							return (index + this.size - 1) % this.size;
						},
						ccw: function(index){
							return (index + this.size + 1) % this.size;
						}
					}
					var found 	= false,
						cur 	= index,
						tmp 	= 0;
					
					for(var j = 0;j < (this.size/2) && !found; j++){
						if( this.containers[cur].douwantme(ret) ){
							debug('I want '+cur);
							found = true; break; 
						}
						
						/* thanks to call */
						tmp = next[obj.versus].call(this,cur);
												
						ret = this.relativize(cur,tmp,ret);
						cur = tmp;
					}
					return (found) ? cur : -1;
				};
				
				/* equal for all */
				this.relativize = function(i1,i2,ret){
					return new point(
						ret.x + (this.containers[i1].position.left - this.containers[i2].position.left),
						ret.y + (this.containers[i1].position.top - this.containers[i2].position.top)
					);
				};
				
				
				this.evidence = function(show){
					var path = show ? '' : ((opts.evidence_first)?'bw/':'');
					var thissrc = jQuery('#'+opts.image_base_id+this.current).attr('src');
					var pars = thissrc.split('/');
					var name_img = pars[pars.length - 1];
					
					//
					jQuery('#'+opts.image_base_id+this.current).attr('src',opts.base_path+opts.img_path+'/'+path+name_img );
					//
					
					
					
				};
				
				
				
				
				
				
						
				/* MAIN */
				this.init();
			};
			
			
			/* OBJECTS END
			###################################################################################
			#################################################################################
			###############################################################################
			#############################################################################
			###########################################################################						                  
			*/
			
			
			
			/*
			###########################################################################
			#############################################################################
			###############################################################################
			#################################################################################
			###################################################################################
			#####################################################################################
			#######################################################################################
			#########################################################################################
			###########################################################################################
			#############################################################################################
			
			
			#########################
			##					  __ 
			##	  ___ ___  _ __  / _|
			##	 / __/ _ \| '_ \| |_ 
			##	| (_| (_) | | | |  _|
			##	 \___\___/|_| |_|_|  
			##       
			##
			*/
			var conf = {
				show_all : true,
				show_num : 3,
				rand_order : false,							
				titles : {next : 'next',prev : 'prev'},
				base_path : '/js/j3d_carousel',
				img_path : '',
				count : 0,
				/* originalbaseSpeed : 0.01, */
				baseSpeed : 0.01,				
				/*  no autostart */
				autostart : false,
				//opt_start_versus : options.start_versus || 'cw',				
				
				/* 	default restart after 5+5 seconds */  
				restart_after : 5,
				auto_rotate_timeout : 5,

				/* 	dimensions */
				radiusX : 130,
				radiusY : 10,
				centerX : 150,
				centerY : 100,
				speed : 0.3,
				car_class : 'car3d',
				load_panel : true,
				invert : false,
				
				min_alpha : 0,
				
				enable_wheel:false,
				enable_keyboard:false,
				
				/* dimensions of carousel */
				carousel_height : parseInt(jQuery(CAROBJ__).css('height'), 10) || 200,
				carousel_width : parseInt(jQuery(CAROBJ__).css('width'), 10) || 200,
				
				versus : 'cw',
				before_click : function(){debug('no before click specified');},
				after_click : function(){debug('no after click specified');},
				
				image_base_id : 'car_img_',
				
				car_id : jQuery(this).attr('id'),
				
				/* div container */
				carousel : jQuery(this),			
				
				/* bottom and top width and height */
				max_width : 200,
				/* _min_width : 20, */
				max_height: 200,
				/* _min_height: 20, */
				min_max_ratio : 10,				
				
				back_front_proportion : 10,
				image_ratio: 0,				

				move : false,				
				
				timeout : '',
				main_img_center : 0, 				
				
				/* posizione del div contenitore */
				off : Object,
				carouselX : 0,
				carouselY : 0,				
				
				dbg_class : 'all',
				
				evidence_first : true,
				
				/* wrapper per la funzione personalizzata... passata come secondo parametro */
				personalizzata : function(that){
					if(typeof func === 'function'){
						func(that);
					}else{
						//debug('nessuna funzione personalizzata!');
					}
				}
				
			};
			
			/*
			#########################
			##				 _       
			##	  ___  _ __ | |_ ___ 
			##	 / _ \| '_ \| __/ __|
			##	| (_) | |_) | |_\__ \
			##	 \___/| .__/ \__|___/
			##		  |_|
			##
			##	quelle che possono essere cambiate dall'esterno vanno in opts
			##	quelle di configurazione interna + opts vanno in conf
			*/
			var opts = {
				show_all			: options.show_all && conf.show_all,
				show_num			: options.show_num || conf.show_num,
				rand_order			: options.rand_order || conf.rand_order,
				dbg					: options.debug || false,	
				dbg_class			: options.dbg_class || conf.dbg_class,
				titles				: options.titles || conf.titles,
				//baseSpeed			: options.base_speed || conf.baseSpeed,
				autostart			: options.autostart || conf.autostart,
				restart_after		: options.restart_after || conf.restart_after,
				auto_rotate_timeout	: options.auto_rotate_timeout || conf.auto_rotate_timeout,
				radiusX				: options.radiusX || conf.radiusX,
				radiusY				: options.radiusY || conf.radiusY,
				centerX				: options.centerX || conf.centerX,
				centerY				: options.centerY || conf.centerY,
				speed				: options.speed || conf.speed,
				car_class			: options.innerclass || conf.car_class,
				
				enable_keyboard		: options.enable_keyboard || conf.enable_keyboard,
				enable_wheel		: options.enable_wheel || conf.enable_wheel,
				
				image_base_id		: options.image_base_id || conf.image_base_id,
				img_path			: options.img_path || conf.img_path,
				
				evidence_first		: options.evidence_first && conf.evidence_first,

				max_width			: options.max_width || conf.max_width,
				max_height			: options.max_height || conf.max_height,
				min_max_ratio		: options.min_max_ratio || conf.min_max_ratio,
				
				/* userdefined */
				personalizzata		: conf.personalizzata,
				
				/* min_height			: options.min_height || conf.min_height, */
				min_alpha			: options.min_alpha || conf.min_alpha,
				carousel_height		: options.carousel_height || conf.carousel_height,
				carousel_width		: options.carousel_width || conf.carousel_width,
				versus				: options.start_versus || conf.versus,
				before_click		: options.before_click || conf.before_click,
				after_click			: options.after_click || conf.after_click,
				load_panel			: (options.load_panel && conf.load_panel) || (options.load_panel === undefined),	// special case ,default is true
				invert				: options.invert || conf.invert,
				base_path			: options.base_path || conf.base_path
			};
			/*
			#
			#
			#
			*/			
			var UTILITY__ = {
						
				get_position : function(element){
					var off = jQuery(element).offset();
					return  new position( parseInt(off.left, 10), parseInt(off.top, 10) );
				},
					
				assert : function (condition, msg){
					if(!condition && opts.dbg){
						alert('ASSERT : \n'+msg);
					}
				},
				
				ext : function xxx(named, val){
					window[named] = val;
				},
				
				/* is possible to force debug */
				debug : function(msg, section, force){
					if(opts.dbg || force){
						var show = (opts.dbg_class === 'all')||(opts.dbg_class !== 'all' && (opts.dbg_class).search(section)!==-1);
						if(show){
							try {opera.postError(msg);}catch(e){}
							try {console.debug(msg);}catch(e){}
						}
					}
					
				},
				
				/*	Extract the number from id format used for images ( like xxsxssx_rerer_343) */
				myid : function(v){
					var p = v.split('_');
					return parseInt(p[p.length-1],10);
				},
				
					
				get_relative_parent : function(ev, id){
			
					var target = jQuery('#'+id);
					
					var left = ev.pageX,
						top = ev.pageY;				
		
					/*click position relative to container*/
					var ret =  new position( left-conf.carouselX , top-conf.carouselY );
				
					/*click position relative to image*/
					var parent = target.parent();
					var parent_position = new position(
						parseInt(parent.css('left'), 10),
						parseInt(parent.css('top'), 10)
					);
		
					var click_point = new point(
						ret.left - parent_position.left,
						ret.top - parent_position.top
					);
					return click_point;
				},				
				
				
				drawLine : function(point1, point2, targetobj){
					var x1 = point1.x,
						y1 = point1.y,
						x2 = point2.x,
						y2 = point2.y;
	
					var dx = Math.abs(x1-x2),
						dy = Math.abs(y1-y2),
						const1=false,
						const2=false,
						p=false,
						x=false,
						y=false,
						step=false;
	
					if(dx >= dy){
						const1 = 2 * dy;
						const2 = 2 * (dy - dx);
						p = 2 * dy - dx;
						x = Math.min(x1,x2);
						y = (x1 < x2) ? y1 : y2;
						step = (y1 > y2) ? 1 : -1;
						UTILITY__.drawPoint(new point(x,y), targetobj);
		
						while (x < Math.max(x1,x2)){
							if (p < 0){
								p += const1;
							}else{
								y += step;
								p += const2;
							}
							UTILITY__.drawPoint(new point(x,y), targetobj);
						}
					}else{
						const1 = 2 * dx;
						const2 = 2 * (dx - dy);
						p = 2 * dx - dy;
						y = Math.min(y1,y2);
						x = (y1 < y2) ? x1 : x2;
						step = (x1 > x2) ? 1 : -1;
						UTILITY__.drawPoint(new point(x,y), targetobj);
						while (y <Math.max(y1,y2)){
							if (p < 0){
								p += const1;
							}else{
								x += step;
								p += const2;
							}
							UTILITY__.drawPoint(new point(x, ++y), targetobj);
						}
					}
					return this;
				},
				
				
				drawPoint : function(point, targetobj){
					var lenx = 1,
						leny = 1,
						adding_point = jQuery('<div>').css({
							position : 'absolute',
							top : point.y+'px',
							left : point.x+'px',
							width : lenx+'px',
							height : leny+'px',
							backgroundColor : 'white'
						});
					targetobj.append(adding_point);
				},
				
				
				mix_literal : function(arr){
					var count = 0,
						done = 0,
						insert = new Object(),
						ret = new Object();
						
					for(var el in arr){
						insert[count] = el;
						count++;
					}	
									
					for(;done<count;){
						var sel = true;
						while(sel){
							sel = insert[Math.floor(Math.random()*count)];
							if(typeof ret[sel] === 'undefined'){
								/*console.debug('writing '+ sel);*/
								ret[sel] = arr[sel];
								done++;
							}else{
								/*console.debug('NOT writing '+ sel);*/
							}
							sel = !sel;
						}
					}
					return ret;
				},
				
				get_vis_range : function(i, current, show_num, num){
					for(var i1= current, i2 = current, j = 0; j <= show_num; i1=(i1-1+num)%num, i2=(i2+1+num)%num, j++){
						if (i === i1 || i === i2){ return true;}
					}
					return false;
				}
			};
			
			UTILITY__.ext('drawline', UTILITY__.drawLine);
			UTILITY__.ext('point', point);
			
			/* funzione di debug */
			var debug = function(msg, classe, force){
				UTILITY__.debug(msg, classe, force);
			};
			
			/* TEST FUNCTION */			
			var test = function(section){
				switch(section){
					case 0:	
						var  p = new point(opts.centerX, opts.centerY);
						p.draw(1);
						var  p2 = new point(5,5);
						p2.draw(5);
						debug(p2);
					break;
					case 1:			
						var poly = new polygon(polys['bianco0.png'].poly);
						poly.getreal(new dimension(300,200));
						debug(poly.real_points);
					break;
					case 2: var anotherpoint = Object.create(point); break;
					case undefined: debug('No section value passed to the test() function'); break;
					default: debug('VALUE '+section+ ' has no associated test section in the test() function '); break;
				}				
			};
			
			
			
			
			
			
			
			
			
			
			
			/*
			##############################
			  ##############################
			##############################
			## 	 __  __    _    ___ _   _  
			##  |  \/  |  / \  |_ _| \ | |
			##  | |\/| | / _ \  | ||  \| |
			##  | |  | |/ ___ \ | || |\  |
			##  |_|  |_/_/   \_\___|_| \_|
			##
			###############################
			  ###############################
			###############################
			##
			*/
			
			jQuery(
				function(){
					var C = new carousel_obj();
					// UTILITY__.ext('opts',opts);
					// test(0);
					
				}
			);
		
		
		/*
		@@@@@@@@@@@@@
		@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
		@@@@@@@@@@@@@
		*/	
		};
	}
)(jQuery);


