// YUI3 Execution
var eZCalendar = function()
{
	// variables
	var object = {};
	
	/*
	 * All values are required in settings:
	 * prevMonthLink
	 * nextMonthLink
	 * prevYearLink
	 * nextYearLink
	 * monthsWraperNode
	 * monthLabelNode
	 * yearsWraperNode
	 * yearLabelNode
	 * calendarBodyNode
	 * aMonths
	 * currentMonth
	 * prevMonth
	 * nextMonth
	 * currentYear
	 * prevYear
	 * nextYear
	 * duration
	 * errorMessage
	 * calendarNodeID
	 * linkURL
	 */
	object.cfg = {};

	// object init
	object.init = function()
    {
        var instance = YUI( YUI3_config ).use( 'node',
        									   'event',
        									   'io-ez',
        									   'anim',
        									   yCallback );
    }
	
	// callback object
	var yCallback = function( Y, result )
	{
		// AJAX method to build calendar body
		var buildCalendarBody = function( newMonth, newYear )
		{
			var backendUri = 'ezjsctemplate::build_calendar_body::' + object.cfg.calendarNodeID + "::" + newMonth + "::" + newYear;
			
			// send AJAX request
			Y.io.ez( backendUri,
					{
						on : { success : successCallback, failure : failureCallback }
					});
		}
		
		// success callback
		var successCallback = function( id, o )
		{
			// set handler false to not execute again during exucation
			object.cfg.handleBuildCalendar = false;
			object.cfg.handleUpdatePosition = false;
			
			// check status
			if ( o.readyState === 4 &&
				 o.status === 200 )
			{
				// check response exist
				if ( o.responseJSON !== undefined )
				{
					var response = o.responseJSON;
					
					if ( response.error_text !== "" )
					{
						// alert error message
						alert( response.error_text );
						
						// authorize handler
						object.cfg.handleBuildCalendar = true;
						object.cfg.handleUpdatePosition = true;
					}
					else
					{
						if ( response.content === "" )
						{
							// alert error message
							alert( object.cfg.errorMessage );
							
							// authorize handler
							object.cfg.handleBuildCalendar = true;
							object.cfg.handleUpdatePosition = true;
						}
						else
						{
							// set content result
							object.cfg.calendarBodyContent = response.content;
							
							// change content in animation
							switch ( object.cfg.linkType )
							{
								case 'prevMonth' :
									if ( object.cfg.handlePrevMonth ) doHiddeLabelOnPrevMonth();
									if ( object.cfg.handlePrevYear ) changePrevYear();
								break;
								case 'nextMonth' :
									if ( object.cfg.handleNextMonth ) doHiddeLabelOnNextMonth();
									if ( object.cfg.handleNextYear ) changeNextYear();
								break;
								case 'prevYear' :
									if ( object.cfg.handlePrevYear ) doHiddeLabelOnPrevYear();
								break;
								case 'nextYear' :
									if ( object.cfg.handleNextYear ) doHiddeLabelOnNextYear();
								break;
								default :
									// alert error message
									alert( object.cfg.errorMessage );
									
									// authorize handler
									object.cfg.handleBuildCalendar = true;
									object.cfg.handleUpdatePosition = true;
									return false;
								break;
							}
							
							// anim hidde calendar body
							hiddeCalendarBody();
						}
					}
				}
				else
				{
					// authorize handler
					object.cfg.handleBuildCalendar = true;
					object.cfg.handleUpdatePosition = true;
				}
			}
			else
			{
				// authorize handler
				object.cfg.handleBuildCalendar = true;
				object.cfg.handleUpdatePosition = true;
			}
		}
		
		// failure callback
		var failureCallback = function( id, o )
		{
			alert( object.cfg.errorMessage );
		}
		
		// init calendar
		var initCalendar = function()
		{
			// get links
			var prevMonthLink = Y.one( object.cfg.prevMonthLink );
			var nextMonthLink = Y.one( object.cfg.nextMonthLink );
			var prevYearLink = Y.one( object.cfg.prevYearLink );
			var nextYearLink = Y.one( object.cfg.nextYearLink );
			
			// add handler
			prevMonthLink.on( 'click', handleClickPrevMonth );
			nextMonthLink.on( 'click', handleClickNextMonth );
			prevYearLink.on( 'click', handleClickPrevYear );
			nextYearLink.on( 'click', handleClickNextYear );
			
			Y.on( 'windowresize', handleWindowResize );
			
			// set handler variables
			object.cfg.handlePrevMonth = true;
			object.cfg.handleNextMonth = true;
			
			object.cfg.handlePrevYear = true;
			object.cfg.handleNextYear = true;
			
			object.cfg.handleBuildCalendar = true;
			object.cfg.handleUpdatePosition = true;
			
			// init coinitCalendarordinates
			var monthLabelNode = Y.one( object.cfg.monthLabelNode );
			var marginLeftMonthLabelNode = monthLabelNode.getStyle( "marginLeft" ).substr( 0, monthLabelNode.getStyle( "marginLeft" ).indexOf( "px" ) );
			
			var monthsWraperNode = Y.one( object.cfg.monthsWraperNode );
			
			object.cfg.getXMonthLabel = monthLabelNode.getX() - marginLeftMonthLabelNode - monthsWraperNode.getX();
			object.cfg.getXPrevMonthLink = prevMonthLink.getX() - monthsWraperNode.getX();
			object.cfg.getXNextMonthLink = nextMonthLink.getX() - monthsWraperNode.getX();
			
			var yearLabelNode = Y.one( object.cfg.yearLabelNode );
			var marginLeftYearLabelNode = yearLabelNode.getStyle( "marginLeft" ).substr( 0, yearLabelNode.getStyle( "marginLeft" ).indexOf( "px" ) );
			
			var yearsWraperNode = Y.one( object.cfg.yearsWraperNode );
			
			object.cfg.getXYearLabel = yearLabelNode.getX() - marginLeftYearLabelNode - yearsWraperNode.getX();
			object.cfg.getXPrevYearLink = prevYearLink.getX() - yearsWraperNode.getX();
			object.cfg.getXNextYearLink = nextYearLink.getX() - yearsWraperNode.getX();
			
			bindEvent();
			
		}
		
		/* Show/Hide event when mouse on/off caldndar days */
		var bindEvent = function()
		{
			$('.show').bind('click', function() {
				var rel = $(this).attr("rel").split('#node_');
				var nodeID = rel[1];
				
				$('#event_' + nodeID ).siblings().hide();
				$('#event_' + nodeID ).show();
			});
		}
		
		// handle events
		/** GENERAL **/
		var handleWindowResize = function( e )
		{
			// prevent default behavior of the event for which it is a handler
			e.preventDefault();
			
			// update position
			if ( object.cfg.handleUpdatePosition ) updatePosition();
		}
		
		/** MONTHS **/
		var handleClickPrevMonth = function( e )
		{
			// prevent default behavior of the event for which it is a handler
			e.preventDefault();
			
			// set link type
			object.cfg.linkType = 'prevMonth';
			
			// build params to AJAX request
			var newMonth = object.cfg.prevMonth;
			var newYear = object.cfg.currentYear;
			
			if ( newMonth == 12 ) newYear = object.cfg.prevYear;
			
			// call AJAX request
			if ( object.cfg.handleBuildCalendar ) buildCalendarBody( newMonth, newYear );
		}
		
		var handleClickNextMonth = function( e )
		{
			// prevent default behavior of the event for which it is a handler
			e.preventDefault();
			
			// set link type
			object.cfg.linkType = 'nextMonth';
			
			// build params to AJAX request
			var newMonth = object.cfg.nextMonth;
			var newYear = object.cfg.currentYear;
			
			if ( newMonth == 1 ) newYear = object.cfg.nextYear;
			
			// call AJAX request
			if ( object.cfg.handleBuildCalendar ) buildCalendarBody( newMonth, newYear );
		}
		
		var changePrevYear = function()
		{
			var currentPrevMonth = object.cfg.prevMonth;
			if ( currentPrevMonth == 12 ) doHiddeLabelOnPrevYear();
		}
		
		var changeNextYear = function()
		{
			var currentNextMonth = object.cfg.nextMonth;
			if ( currentNextMonth == 1 ) doHiddeLabelOnNextYear();
		}
		
		/** YEARS **/
		var handleClickPrevYear = function( e )
		{
			// prevent default behavior of the event for which it is a handler
			e.preventDefault();
			
			// set link type
			object.cfg.linkType = 'prevYear';
			
			// build params to AJAX request
			var newMonth = object.cfg.currentMonth;
			var newYear = object.cfg.prevYear;
			
			// call AJAX request
			if ( object.cfg.handleBuildCalendar ) buildCalendarBody( newMonth, newYear );
			
		}
		
		var handleClickNextYear = function( e )
		{
			// prevent default behavior of the event for which it is a handler
			e.preventDefault();
			
			// set link type
			object.cfg.linkType = 'nextYear';
			
			// build params to AJAX request
			var newMonth = object.cfg.currentMonth;
			var newYear = object.cfg.nextYear;
			
			// call AJAX request
			if ( object.cfg.handleBuildCalendar ) buildCalendarBody( newMonth, newYear );
		}
		
		// update positions function
		var updatePosition = function()
		{
			// set handler false to not execute again during exucation
			object.cfg.handleUpdatePosition = false;

			// get links
			var prevMonthLink = Y.one( object.cfg.prevMonthLink );
			var nextMonthLink = Y.one( object.cfg.nextMonthLink );
			var prevYearLink = Y.one( object.cfg.prevYearLink );
			var nextYearLink = Y.one( object.cfg.nextYearLink );
			
			// init coordinates
			/** MONTHS **/
			var monthsWraperNode = Y.one( object.cfg.monthsWraperNode );
			
			var widthNextMonthLink = nextMonthLink.getStyle( 'width' ).substr( 0, nextMonthLink.getStyle( "width" ).indexOf( "px" ) );
			var newMonthLabelNodeX = ( parseInt( prevMonthLink.getX() ) + parseInt( nextMonthLink.getX() ) + parseInt( widthNextMonthLink ) + ( parseInt( monthsWraperNode.getX() ) - parseInt( monthsWraperNode.get( 'parentNode' ).getX() ) ) ) / 2;
			
			var monthLabelNode = Y.one( object.cfg.monthLabelNode );
			var marginLeftMonthLabelNode = monthLabelNode.getStyle( "marginLeft" ).substr( 0, monthLabelNode.getStyle( "marginLeft" ).indexOf( "px" ) );
			
			if ( monthLabelNode.getX() != newMonthLabelNodeX )
			{
				monthLabelNode.setX( parseInt( newMonthLabelNodeX ) + parseInt( marginLeftMonthLabelNode ) );
			}
			
			object.cfg.getXMonthLabel = monthLabelNode.getX() - marginLeftMonthLabelNode - monthsWraperNode.getX();
			
			/** YEARS **/
			var yearsWraperNode = Y.one( object.cfg.yearsWraperNode );
			
			var widthNextYearLink = nextYearLink.getStyle( 'width' ).substr( 0, nextYearLink.getStyle( "width" ).indexOf( "px" ) );
			var newYearLabelNodeX = ( parseInt( prevYearLink.getX() ) + parseInt( nextYearLink.getX() ) + parseInt( widthNextYearLink ) + ( parseInt( yearsWraperNode.getX() ) - parseInt( yearsWraperNode.get( 'parentNode' ).getX() ) ) ) / 2;
			
			var yearLabelNode = Y.one( object.cfg.yearLabelNode );
			var marginLeftYearLabelNode = yearLabelNode.getStyle( "marginLeft" ).substr( 0, yearLabelNode.getStyle( "marginLeft" ).indexOf( "px" ) );
			
			if ( yearLabelNode.getX() != newYearLabelNodeX )
			{
				yearLabelNode.setX( parseInt( newYearLabelNodeX ) + parseInt( marginLeftYearLabelNode ) );
			}
			
			object.cfg.getXYearLabel = yearLabelNode.getX() - marginLeftYearLabelNode - yearsWraperNode.getX();
			
			// authorize handler
			object.cfg.handleUpdatePosition = true;
		}
		
		// anim functions
		/** MONTHS **/
		var doHiddeLabelOnPrevMonth = function()
		{
			// set handler false to not execute again during exucation
			object.cfg.handlePrevMonth = false;
			
			// anim hidde
			hiddeLabelOnPrevMonth();
		}
		
		var doHiddeLabelOnNextMonth = function()
		{
			// set handler false to not execute again during exucation
			object.cfg.handleNextMonth = false;
			
			// anim hidde
			hiddeLabelOnNextMonth();
		}
		
		var hiddeLabelOnPrevMonth = function()
		{
			// create new anim
			var anim = new Y.Anim({
                node : object.cfg.monthLabelNode,
                easing : Y.Easing.easeIn,
                duration : object.cfg.duration,
                
                from: 
                {
					left : object.cfg.getXMonthLabel,
					opacity : 1
                },

                to: 
                {
                	left : object.cfg.getXNextMonthLink,
                	opacity : 0
                }
            });
			
			// run it
            anim.run();
            
            // handler end of anim
            anim.on( "end", doDisplayLabelOnPrevMonth );
		}
		
		var hiddeLabelOnNextMonth = function()
		{
			// create new anim
			var anim = new Y.Anim({
                node : object.cfg.monthLabelNode,
                easing : Y.Easing.easeIn,
                duration : object.cfg.duration,
                
                from: 
                {
					left : object.cfg.getXMonthLabel,
					opacity : 1
                },

                to: 
                {
                	left : object.cfg.getXPrevMonthLink,
                	opacity : 0
                }
            });
			
			// run it
            anim.run();
            
            // handler end of anim
            anim.on( "end", doDisplayLabelOnNextMonth );
		}
		
		var doDisplayLabelOnPrevMonth = function()
		{
			// change label
			var monthLabelNode = Y.one( object.cfg.monthLabelNode );
			monthLabelNode.setContent( object.cfg.aMonths[ object.cfg.prevMonth ] ) ;
			
			// anim display
			displayLabelOnPrevMonth();
		}
		
		var doDisplayLabelOnNextMonth = function()
		{
			// change label
			var monthLabelNode = Y.one( object.cfg.monthLabelNode );
			monthLabelNode.setContent( object.cfg.aMonths[ object.cfg.nextMonth ] ) ;
			
			// anim display
			displayLabelOnNextMonth();
		}
		
		var displayLabelOnPrevMonth = function()
		{
			// create new anim
			var anim = new Y.Anim({
                node : object.cfg.monthLabelNode,
                easing : Y.Easing.easeOut,
                duration : object.cfg.duration,
                
                from: 
                {
					left : object.cfg.getXPrevMonthLink,
					opacity : 0
                },

                to: 
                {
                	left : object.cfg.getXMonthLabel,
                	opacity : 1
                }
            });
			
			// run it
            anim.run();
            
            // handler end of anim
            anim.on( "end", initOnPrevMonth );
		}
		
		var displayLabelOnNextMonth = function()
		{
			// create new anim
			var anim = new Y.Anim({
                node : object.cfg.monthLabelNode,
                easing : Y.Easing.easeOut,
                duration : object.cfg.duration,
                
                from: 
                {
					left : object.cfg.getXNextMonthLink,
					opacity : 0
                },

                to: 
                {
                	left : object.cfg.getXMonthLabel,
                	opacity : 1
                }
            });
			
			// run it
            anim.run();
            
            // handler end of anim
            anim.on( "end", initOnNextMonth );
		}
		
		/** YEARS **/
		var doHiddeLabelOnPrevYear = function()
		{
			// set handler false to not execute again during exucation
			object.cfg.handlePrevYear = false;
			
			// anim hidde
			hiddeLabelOnPrevYear();
		}
		
		var doHiddeLabelOnNextYear = function()
		{
			// set handler false to not execute again during exucation
			object.cfg.handlePrevYear = false;
			
			// anim hidde
			hiddeLabelOnNextYear();
		}
		
		var hiddeLabelOnPrevYear = function()
		{
			// create new anim
			var anim = new Y.Anim({
                node : object.cfg.yearLabelNode,
                easing : Y.Easing.easeIn,
                duration : object.cfg.duration,
                
                from: 
                {
					left : object.cfg.getXYearLabel,
					opacity : 1
                },

                to: 
                {
                	left : object.cfg.getXNextYearLink,
                	opacity : 0
                }
            });
			
			// run it
            anim.run();
            
            // handler end of anim
            anim.on( "end", doDisplayLabelOnPrevYear );
		}
		
		var hiddeLabelOnNextYear = function()
		{
			// create new anim
			var anim = new Y.Anim({
                node : object.cfg.yearLabelNode,
                easing : Y.Easing.easeIn,
                duration : object.cfg.duration,
                
                from: 
                {
					left : object.cfg.getXYearLabel,
					opacity : 1
                },

                to: 
                {
                	left : object.cfg.getXPrevYearLink,
                	opacity : 0
                }
            });
			
			// run it
            anim.run();
            
            // handler end of anim
            anim.on( "end", doDisplayLabelOnNextYear );
		}
		
		var doDisplayLabelOnPrevYear = function()
		{
			// change label
			var yearLabelNode = Y.one( object.cfg.yearLabelNode );
			yearLabelNode.setContent( object.cfg.prevYear );
			
			// anim display
			displayLabelOnPrevYear();
		}
		
		var doDisplayLabelOnNextYear = function()
		{
			// change label
			var yearLabelNode = Y.one( object.cfg.yearLabelNode );
			yearLabelNode.setContent( object.cfg.nextYear );
			
			// anim display
			displayLabelOnNextYear();
		}
		
		var displayLabelOnPrevYear = function()
		{
			// create new anim
			var anim = new Y.Anim({
                node : object.cfg.yearLabelNode,
                easing : Y.Easing.easeOut,
                duration : object.cfg.duration,
                
                from: 
                {
					left : object.cfg.getXPrevYearLink,
					opacity : 0
                },

                to: 
                {
                	left : object.cfg.getXYearLabel,
                	opacity : 1
                }
            });
			
			// run it
            anim.run();
            
            // handler end of anim
            anim.on( "end", initOnPrevYear );
		}
		
		var displayLabelOnNextYear = function()
		{
			// create new anim
			var anim = new Y.Anim({
                node : object.cfg.yearLabelNode,
                easing : Y.Easing.easeOut,
                duration : object.cfg.duration,
                
                from: 
                {
					left : object.cfg.getXNextYearLink,
					opacity : 0
                },

                to: 
                {
                	left : object.cfg.getXYearLabel,
                	opacity : 1
                }
            });
			
			// run it
            anim.run();
            
            // handler end of anim
            anim.on( "end", initOnNextYear );
		}
		
		/** CALENDAR BODY **/
		var hiddeCalendarBody = function()
		{
			// create new anim
			var anim = new Y.Anim({
                node : object.cfg.calendarBodyNode,
                easing : Y.Easing.easeIn,
                duration : object.cfg.duration,
                
                from: 
                {
					opacity : 1
                },

                to: 
                {
                	opacity : 0
                }
            });
			
			// run it
            anim.run();
            
            // handler end of anim
            anim.on( "end", doDisplayCalendarBody );
		}
		
		var doDisplayCalendarBody = function()
		{
			// change content
			var calendarBodyNode = Y.one( object.cfg.calendarBodyNode );
			calendarBodyNode.setContent( object.cfg.calendarBodyContent );
			
			// anim display
			displayCalendarBody();
		}
		
		var displayCalendarBody = function()
		{
			// create new anim
			var anim = new Y.Anim({
                node : object.cfg.calendarBodyNode,
                easing : Y.Easing.easeOut,
                duration : object.cfg.duration,
                
                from: 
                {
					opacity : 0
                },

                to: 
                {
                	opacity : 1
                }
            });
			
			// run it
            anim.run();
            
            // handler end of anim
            anim.on( "end", initOnCalendarBody );
		}
		
		// init general config
		/** MONTHS **/
		var initOnPrevMonth = function()
		{
			// decremente month
			object.cfg.currentMonth--;
			object.cfg.prevMonth--;
			object.cfg.nextMonth--;
			
			// check if change year
			if ( object.cfg.currentMonth == 0 )
			{
				object.cfg.currentMonth = 12;
			}
			
			if ( object.cfg.prevMonth == 0 )
			{
				object.cfg.prevMonth = 12;
			}
			
			if ( object.cfg.nextMonth == 0 )
			{
				object.cfg.nextMonth = 12;
			}
			
			// authorize handler
			object.cfg.handlePrevMonth = true;
		}
		
		var initOnNextMonth = function()
		{
			// incremente month
			object.cfg.currentMonth++;
			object.cfg.prevMonth++;
			object.cfg.nextMonth++;
			
			// check if change year
			if ( object.cfg.currentMonth == 13 )
			{
				object.cfg.currentMonth = 1;
			}
			
			if ( object.cfg.prevMonth == 13 )
			{
				object.cfg.prevMonth = 1;
			}
			
			if ( object.cfg.nextMonth == 13 )
			{
				object.cfg.nextMonth = 1;
			}
			
			// authorize handler
			object.cfg.handleNextMonth = true;
		}
		
		/** YEARS **/
		var initOnPrevYear = function()
		{
			// decremente year
			object.cfg.currentYear--;
			object.cfg.prevYear--;
			object.cfg.nextYear--;
			
			// authorize handler
			object.cfg.handlePrevYear = true;
		}
		
		var initOnNextYear = function()
		{
			// incremente year
			object.cfg.currentYear++;
			object.cfg.prevYear++;
			object.cfg.nextYear++;
			
			// authorize handler
			object.cfg.handlePrevYear = true;
		}
		
		/** CALENDAR BODY **/
		var initOnCalendarBody = function()
		{
			// get links
			var prevMonthLink = Y.one( object.cfg.prevMonthLink );
			var nextMonthLink = Y.one( object.cfg.nextMonthLink );
			var prevYearLink = Y.one( object.cfg.prevYearLink );
			var nextYearLink = Y.one( object.cfg.nextYearLink );
			
			// change prevMonthLink URL
			var newMonth = object.cfg.prevMonth;
			var newYear = object.cfg.currentYear;
			
			if ( newMonth == 12 ) newYear = object.cfg.prevYear;
			
			prevMonthLink.set( "href", object.cfg.linkURL.replace( "__newMonth__", newMonth ).replace( "__newYear__", newYear ) );
			
			// change nextMonthLink URL
			var newMonth = object.cfg.nextMonth;
			var newYear = object.cfg.currentYear;
			
			if ( newMonth == 1 ) newYear = object.cfg.nextYear;
			
			nextMonthLink.set( "href", object.cfg.linkURL.replace( "__newMonth__", newMonth ).replace( "__newYear__", newYear ) );
			
			// change prevYearLink URL
			var newMonth = object.cfg.currentMonth;
			var newYear = object.cfg.prevYear;
			
			prevYearLink.set( "href", object.cfg.linkURL.replace( "__newMonth__", newMonth ).replace( "__newYear__", newYear ) );
			
			// change nextYearLink URL
			var newMonth = object.cfg.currentMonth;
			var newYear = object.cfg.nextYear;
			
			nextYearLink.set( "href", object.cfg.linkURL.replace( "__newMonth__", newMonth ).replace( "__newYear__", newYear ) );
			
			// init showEvent
			bindEvent();
			
			// authorize handler
			object.cfg.handleBuildCalendar = true;
			object.cfg.handleUpdatePosition = true;
		}
		
		// init all process
		initCalendar();
	}
	
	return object;
}();
