/**
 * @author D.Spurr
 * @version 0.8 31-05-05
 * @dependencies
 *              lib/dom.js - For getElementByClassName(), xGetElementsByTagName(), getAbsolutePos()
 *              lib/event.js - For addLoadEvent() & getTarg() & addEvent()
 *              lib/date.js - For getDaysInMonth()
 *              lib/date.lang.xx.js - For _dateDays_short constant  
 *              lib/cookie.js - For setCookie()
 * @support PC IE 5.x, 6, Mozilla 1.x, Opera 7.x,8.x, Firefox 1.x, Netscape 6.x,7.x,8.x
 *          Mac Mozilla 1.x, Opera 7.x,8.x, Firefox 1.x, Netscape 6.x,7.x,8.x
 */ 
/**
 * Attaches the calendar to the selects & inserts the HTML
 * Relies on the following HTML structure:
 * <... class="calDate">                                      (Wrapper node passed to the object)
 *   <input type="hidden" name="calURL" value="http://..." /> (Full url to the calendar html)
 *   [<input type="hidden" name="cal... />]                   (Optional parameters to pass through to the calendar in a query string, see /createCalendar.js)
 *   <select name="startDay">...</select>                     (The day select to attach the calendar to)
 *   <select name="startMonthYearCombi">...</select>          (The combined month & year select to attach the calendar to)
 * </...>
 *
 * @access public
 * @param node The wrapper node element
 * @param int The index of the global cals array that this calendar lives in
 * @return Cal (implied)
 */
LibManager.require( 'Event' ); 
LibManager.require( 'dom' );
LibManager.require( 'date.date' );
LibManager.require( 'date.lang.en' );
LibManager.require( 'cookie' );

function Cal(el,myI) {
	/**
	 * Holds the wrapper element which contains the input element & will be used to insert the button
	 * @var node
	 */
	this.wrapEl = el;
	/**
	 * Holds the day select element this calendar is attached to
	 * @var node
	 */
	this.dayEl = null;
	/**
	 * Holds the month/year select element this calendar is attached to
	 * @var node
	 */
	this.monthYrEl = null;
	/**
	 * Whether the calendar is displayed or not
	 * @var boolean
	 */
	this.disp = false;
	/**
	 * Holds the calendar wrapper iFrame
	 * @var node
	 */
	this.calWrap = null;
	/**
	 * x size of the calendar
	 * @var int
	 */
	this.sizeX = null;
	/**
	 * y size of the calendar
	 * @var int
	 */
	this.sizeY = null;
	/**
	 * Holds the calendar launch button
	 * @var node
	 */
	this.lBut = null;
	/**
	 * Holds the index of the global cals array that this calendar lives in
	 * @var int
	 */
	this.myIndex = myI;
	/**
	 * Holds the node which is used to display the day of the week of the chosen date
	 * @var node
	 */
	this.weekDayEl = null;
	/**
	 * Whether to hide the form field elements that shine through (IE PC bug)
	 * @var boolean
	 */
	this.hideEls = false;
	/**
	 * Array that holds the form filed elements that were hidden to stop them from shining through
	 * @var array
	 */
	this.hiddenEls = null;
		
	/***********BEGIN INITIALISING THE OBEJCT **********/
	var uA = navigator.userAgent.toLowerCase();
	// don't do anything if we're in Safari or IE on Mac as we don't work properly & I don't have the time to look at it right now (at least we're failing gracefully)
	if((uA.indexOf('mac') != -1 && uA.indexOf('msie') != -1) || uA.indexOf('safari') != -1) return false;
	
	
	if((uA.indexOf('msie 5.5') != -1) || (uA.indexOf('msie 6') != -1)) this.hideEls = true;
	
	// pickup any hidden form fields & try and find the ones that we need
	var params = xGetElementsByTagName('input',this.wrapEl);
	var calURL = null; // holds the url for the calendar (if this is not found then we cannot continue)
	var qS = ''; // holds the queryString of params to pass to the calendar
	for(var i = 0; i < params.length; i++) {
		if(params[i].type == 'hidden') {
			if(params[i].name == 'calURL') calURL = params[i].value;
			else if(params[i].name.match(/^cal(.)+/)) qS += '&' + escape(params[i].name) + '=' + escape(params[i].value);
		}
	}

	if(calURL == null) return false;
	// add the extra parameters to the URL
	if(calURL.match(/\?/)) calURL += '&';
	else calURL += '?';
	calURL += 'calIndex='+this.myIndex+qS;
	
	/* create the calendar elements
	   We're putting all this in using innerHTML as IE 5 didn't like assigning x.src when doing a createElement to create the iframe & IE 5.2 Mac didn't like setting x.type on inputs.
		 So we then need to pick things out of after & place them in the object properties once we've stopped playing with innerHTML (otherwise we'll loose our node object references)
	*/
	this.disp = false;
	
	this.wrapEl.innerHTML = '<input type="text" size="3" disabled="true" class="calWeekDay" id="cal_day_'+this.myIndex+'"/> ' + this.wrapEl.innerHTML + '<iframe class="calWrap" src="'+calURL+'" id="cal_'+this.myIndex+'" name="cal_'+this.myIndex+'" frameborder="0" marginheight="0" marginwidth="0" scrolling="no" style="left:-99999px;top:-99999px;"></iframe>' + '<input type="button" class="calBut" value="&nbsp;" title="Choose dates from calendar" id="cal_but_'+this.myIndex+'"/>';
	// pick out the weekDay element
	this.weekDayEl = document.getElementById('cal_day_'+this.myIndex);
	// pick out the iframe
	this.calWrap = document.getElementById('cal_'+this.myIndex);
	/* 
		move the iframe to the body to allow more reliable document positioning, we don't want to use document.body.innerHTML to insert 
		the calendar into the body as we'll loose the reference to the wrapEl node & we can't use createElement (see above)
	*/
	document.body.appendChild(this.calWrap);
	// pick out the button
	this.lBut = document.getElementById('cal_but_'+this.myIndex);
	// pick up the form field elements	
	var sels = xGetElementsByTagName('select',this.wrapEl);
	for(var i=0;i<sels.length;i++) {
		if(sels[i].name == 'startDay') this.dayEl = sels[i];
		else if(sels[i].name == 'startMonthYearCombi') this.monthYrEl = sels[i];
		if(this.dayEl != null && this.monthYrEl != null) break;
	}
	// if we haven't got the required fields then we need to quit
	if(this.dayEl == null || this.monthYrEl == null) return false;
	
	// grap the dimensions of the calendar
	this.sizeX = this.calWrap.offsetWidth;
	this.sizeY = this.calWrap.offsetHeight;
	
	// assign the method that captures the document clicks, needs to be passed through a fake method to keep the object reference intact
	var _fakeThis = this;
	this._passCapDocClick = function(e) {
		_fakeThis.capDocClick(e);
	};
	if(!addEvent(window.document, 'click', this._passCapDocClick, false)) window.document.onclick += this._passCapDocClick;

	// assign method that capture the changes of the date drop downs and passes them to the calendar, needs to be passed through a fake method to keep the object reference intact
	this.dayEl.onchange = this.monthYrEl.onchange = function() { _fakeThis.dropDownChange(); };
	/*********** END INITIALISING THE OBEJCT **********/

	/**
	 * This is fired when the drop downs are changed, it sets the cookie values & updates the little
	 * day notifing guy
	 * 
	 * @access private
	 * @return void
	 * @todo Update the cookie values
	 */
	this.dropDownChange = function() {
		var d = new Date();
		var day = this.dayEl.selectedIndex+1; // get the day they've chosen
		var monthYear = this.monthYrEl.options[this.monthYrEl.selectedIndex].value.split('|');
		d.setDate(1);
		d.setMonth(monthYear[0]-1);
		d.setFullYear(monthYear[1]);
		// ensure that they day they've selected from the drop down is valid for this month & year
		var maxDays = getDaysInMonth(d);
		if(maxDays < day) {
			/*
				they've selected an invalid day for this month & year so use 
				the maxDays to populate the date object & reset the drop down accordingly
			*/
			d.setDate(maxDays);
			this.dayEl.options[maxDays - 1].selected = true;
		} else d.setDate(day);
		this.weekDayEl.value = _dateDays_short[d.getDay()];
		this.updateCookies(d);
		//	this.calWrap.window.document.ThisCal.redraw(d);
		window.frames['cal_'+this.myIndex].ThisCal.redraw(d);
	};
	
	/**
	 * Used to update the cookie values
	 * 
	 * @access private
	 * @param date The chosen date
	 * @return void
	 */
	this.updateCookies = function(d) {
		setCookie('startDay',d.getDate(),'','/');
		setCookie('startMonth',d.getMonth()+1,'','/');
		setCookie('startYear',d.getYear(),'','/'); // we're currently using getYear as the recode V1 codebase does it that way
		setCookie('startMonthYearCombi',d.getMonth()+1+ '|' + d.getFullYear(),'','/')
	};
	
	/**
	 * Used to tell the drop downs that they need to change their settings, called by calendar
	 *
	 * @access public
	 * @param date The selected date
	 * @param boolean Whether or not this has being fired from the calendar init
	 * @return void
	 * @todo Update the cookie values
	 */
	this.notifyChange = function(d,isInit) {
		var cM = d.getMonth() + 1; // plus one as the HTML doesn't use zero indexes
		var cY = d.getFullYear();
		var MYVal =  cM+'|'+cY;
		this.dayEl.options[d.getDate()-1].selected = true;
		for(i = 0; i < this.monthYrEl.options.length; i++) {
			if(this.monthYrEl.options[i].value == MYVal) { this.monthYrEl.options[i].selected = true; break; }
		}
		this.weekDayEl.value = _dateDays_short[d.getDay()];
		// if we're fired from the calendar init then we don't want to fire the display or updateCookies method
		if(!isInit) { this.calDisp(); this.updateCookies(d); }
	};
	
	/**
	 * Used to capture document clicks, figures out where the user is clicking and 
	 * decides whether the calendar should be displayed or not
	 * 
	 * @access public
	 * @param event
	 * @return void
	 */
	this.capDocClick = function(e) {
		if(!e) e = window.event;
		var targ = getTarg(e);
		var posX,posY,tX,tY = 0;
		if(e.pageX || e.pageY) {
			posX = e.pageX;
			posY = e.pageY;
		}	else if(e.clientX || e.clientY) {
			posX = e.clientX;
			posY = e.clientY;
		}
		if(targ.id == 'cal_but_'+this.myIndex) {
			var uA = navigator.userAgent.toLowerCase();
			// if it's IE map the position of the calendar to where their cursor is (with a bit of leeway to try and stop it covering the button)
			if(uA.indexOf('msie') != -1) {
				posX += document.body.scrollLeft;
				posY += document.body.scrollTop;
				tX = (posX - this.sizeX) + this.lBut.offsetWidth * 2;
				tY = posY + Math.ceil(this.lBut.offsetHeight / 2);
			} else {
				// get the pos for the calendar to be when it is displayed
				var tPos = getAbsolutePos(this.lBut);
				tX = (tPos.x2 + this.lBut.offsetWidth) - this.sizeX;
				tY = tPos.y2;
			}
			/*
				do some hackery to get the calendar to work correctly when fired from a <input type="button">
				if we don't do it like this the button gets the focus & the iFrame goes all whacky.
			*/
			var ft = this;
			var p = function() {
				ft.calDisp(tX,tY);
			};
			this.lBut.blur();
			setTimeout(p,50);
		}	else if(this.disp) {
			// figure out if they've not clicked within the area of the calendar
			if(!(posX > this.x && posX < this.x + this.sizeX) && !(posY > this.y && posY < this.y + this.sizeY)) this.calDisp(); // hide the calendar
		} 
		return;
	};
	
	/**
	 * Displays or hides the calendar, uses the passed in x & y vars only if the calendar is currently hidden
	 * otherwise hides the calendar by setting large negative offsets
	 * 
	 * @access private
	 * @param int [optional] X position
	 * @param int [optional] Y position
	 * @return void
	 */	
	this.calDisp = function(x,y) {
		if(this.disp) { tX = -99999; tY = -99999; }
		else { tX = x; tY = y; }
		this.calWrap.style.left = tX + 'px';	
		this.calWrap.style.top = tY + 'px';
		this.disp = !this.disp;
	};
}

/**
 * Static function which finds all the elements with the className of 'calDate' and tries to attach
 * Cal objects to the node
 *
 * @static
 * @access public
 * @return void
 */
function attachCals() {
	var els = getElementsByClassName('calDate', document);	
	for(var i=0;i<els.length;i++) cals[cals.length] = new Cal(els[i],i);
}

LibManager.isLoaded( 
	['addLoadEvent'], 
	function() {
		// global array that holds all the attached calendars
		cals = new Array();
		// add a call to attach the calendars when the page has loaded the DOM
		addLoadEvent(attachCals,true);
	}
);



