if(!LOG){
	var LOG = {warn:function(){}};
}

if(!Richfaces) var Richfaces={};

Richfaces.evalMacro = function(template, object)
{
	var value="";
	try { with (object) value=eval(template) } catch (e) { LOG.warn("Exception: "+e.Message + "\n[" + template + "]"); }
	return value;
}

Object.extend(Event, {
	findElementByAttr : function(event, tagName, attribute, value, flag) {
    	var element = Event.findElement(event, tagName);
    	while (!element[attribute] || (flag ? element[attribute].startsWith(value)==0 : element[attribute]!=value) )
    	{
	      	element = element.parentNode;
	    }
    	return element;
	}
});

Object.extend(Element, {
	replaceClassName : function (element, whichClassName, toClassName) {
		if (!(element = $(element))) return;
	    var e = Element.classNames(element);
	    e.remove(whichClassName);
	    e.add(toClassName);
	    return element;
	}
});

function isLeapYear(year) {
	return new Date(year, 1, 29).getDate()==29;
}

function daysInMonth(year,month) {
	return 32 - new Date(year, month, 32).getDate();
}

function daysInMonthByDate(date) {
	return 32 - new Date(date.getYear(), date.getMonth(), 32).getDate();
}

function getDay(date, firstWeekDay ) {
	var value = date.getDay() - firstWeekDay;
	if (value < 0) value = 7 + value;
	return value;
}

function getFirstWeek(year, mdifw, fdow) {
	var date = new Date(year,0,1);
	var firstday = getDay(date, fdow);
	
	var weeknumber = (7-firstday<mdifw) ? 0 : 1;
	
	return {date:date, firstDay:firstday, weekNumber:weeknumber, mdifw:mdifw, fdow:fdow};
}

function getLastWeekOfPrevYear(o) {
	var year = o.date.getFullYear()-1;
	var days = (isLeapYear(year) ? 366 : 365);
	var obj = getFirstWeek(year, o.mdifw, o.fdow);
	days = (days - 7 + o.firstDay);
	var weeks = Math.floor(days/7)+1;
	  
	return  weeks+obj.weekNumber;
}

function weekNumber(year, month, mdifw, fdow) {
	
	var o = getFirstWeek(year, mdifw, fdow);
	
	if (month==0) 
	{
		if (o.weekNumber==1) return 1;
		return getLastWeekOfPrevYear(o);
	}
	var	oneweek =  604800000;
	var d = new Date(year, month,1);
		d.setDate( 1+o.firstDay + (getDay(d,fdow)==0?1:0));
		
	weeknumber = o.weekNumber + Math.floor((d.getTime() - o.date.getTime()) / oneweek);
	
	return weeknumber;
}


Calendar = Class.create();
Object.extend(Calendar.prototype, {
    initialize: function(id,parameters) {

		// dayListTableId, weekNumberBarId, weekDayBarId - 3 tables ids',
		// dayListMarkupArray - array [0..41] of day cell markup
		// weekNumberMarkup - week number cell markup
		// weekDayMarkup - week day cell markup
		
		// currentDate - date to show month (day not used) (mm/yyyy) 
		// selectedDate - selected date (mm/dd/yyyy)
		// weekDayLabels - collection of week day labels keyed by week day numbers
		// minDaysInFirstWeek - locale-specific constant defining number of days in the first week
		// firstWeekDay - (0..6) locale-specific constant defining number of the first week day
		
		this.id = id;
		this.params = parameters;
		this.currentDate = this.params.currentDate;
		this.selectedDate = this.params.selectedDate;

		this.todayDate = new Date();
		
		this.selectedDateElement;
		
		this.firstWeekendDayNumber = 6-this.params.firstWeekDay;
		this.secondWeekendDayNumber = (this.params.firstWeekDay>0 ? 7-this.params.firstWeekDay : 0);
		
		this.daysData = {startDate:null, days:[]};
		this.days = [];
		
		var htmlTextHeader = '<div id="div_'+this.params.dayListTableId+'"></div><table cellspacing="0" border="1" frame="void" rules="all" id="'+this.params.dayListTableId+'">\n';
		var htmlTextFooter = '</table><div id="div_'+this.params.dayListTableId+'_selected_date"></div>\n';

		// days bar creation
		var htmlTextWeekDayBar = '<tr id="'+this.params.weekDayBarId+'"><td style="border-top:0px"></td>';
		var weekDayCounter = this.params.firstWeekDay;
		for (var i=0;i<7;i++)
		{
			var weekDayHtml = this.params.weekDayMarkup( {weekDay: this.params.weekDayLabels[weekDayCounter]} );
			if (weekDayCounter==6) weekDayCounter=0; else weekDayCounter++;

			htmlTextWeekDayBar+='<td id="'+this.params.weekDayBarId+i+'" class="'+(i==this.firstWeekendDayNumber || i==this.secondWeekendDayNumber ? "Weekendclass" : "Dayclass")+'">'+weekDayHtml+'</td>';
		}
		htmlTextWeekDayBar+='</tr>\n';

		// week & weekNumber creation
		var htmlTextWeek="";
		var p=0;

		for (k=1;k<7;k++)
		{

			var weekNumberHtml = this.params.weekNumberMarkup( {weekNumber: k} );

			htmlTextWeek+='<tr><td id="'+this.params.weekNumberBarId+k+'">'+weekNumberHtml+'</td>';
			for (var i=0;i<7;i++)
			{
				htmlTextWeek+='<td id="'+this.params.dayListTableId+'_cell'+(p++)+'" style="vertical-align:top"></td>';
			}
			htmlTextWeek+='</tr>';
		}

		// set content
		var obj=$(id);
		obj.innerHTML = htmlTextHeader+htmlTextWeekDayBar+htmlTextWeek+htmlTextFooter;
		
		this.prepareEvents();
		obj.innerHTML
	},
	
	isWeekend: function(weekday) {
		return (weekday == this.firstWeekendDayNumber || weekday == this.secondWeekendDayNumber);
	},
	
	prepareEvents: function() {
		this.eventCellOnClick = this.eventCellOnClick.bindAsEventListener(this);
		this.eventCellOnMouseOver = this.eventCellOnMouseOver.bindAsEventListener(this);
		this.eventCellOnMouseOut = this.eventCellOnMouseOut.bindAsEventListener(this);
	},
	
	setCellEvents: function(obj) {
		Event.observe(obj, "click", this.eventCellOnClick, false);
		Event.observe(obj, "mouseover", this.eventCellOnMouseOver, false);
		Event.observe(obj, "mouseout", this.eventCellOnMouseOut, false);
	},
	stopCellEvents: function(obj) {
		Event.stopObserving(obj, "click", this.eventCellOnClick, false);
		Event.stopObserving(obj, "mouseover", this.eventCellOnMouseOver, false);
		Event.stopObserving(obj, "mouseout", this.eventCellOnMouseOut, false);
	},	
	
	eventCellOnClick: function (e) {
		var idstr = this.params.dayListTableId+'_cell';
		var obj = Event.findElementByAttr(e, "TD", "id", idstr, true);
		if (obj)
		{
			var daydata = this.days[parseInt(obj.id.substr(idstr.length))];
			if (daydata._month==0)
			{
				this.selectedDate=new Date(this.currentDate);
				this.selectedDate.setDate(obj.data);
				if (this.selectedDateElement) Element.removeClassName(this.selectedDateElement, "Selecteddayclass");
				this.selectedDateElement = obj;
				Element.addClassName(obj, "Selecteddayclass");
				
				$('div_'+this.params.dayListTableId+'_selected_date').innerHTML=obj.data+"/"+(this.currentDate.getMonth()+1)+"/"+this.currentDate.getFullYear();
			} else {
				if (daydata._month==-1) this.prevMonth(); else this.nextMonth();
			}
		}
	},

	eventCellOnMouseOver: function (e) {
		var idstr = this.params.dayListTableId+'_cell';
		var obj = Event.findElementByAttr(e, "TD", "id", idstr, true);
		if (obj)
		{
			var daydata = this.days[parseInt(obj.id.substr(idstr.length))];
			if (daydata._month==0 && Element.hasClassName(obj,daydata._className)) Element.replaceClassName(obj,daydata._className, daydata._hoverClassName);
		}
	},
	
	eventCellOnMouseOut: function (e) {
		var idstr = this.params.dayListTableId+'_cell';
		var obj = Event.findElementByAttr(e, "TD", "id", idstr, true);
		if (obj)
		{
			var daydata = this.days[parseInt(obj.id.substr(idstr.length))];
			if (daydata._month==0 && Element.hasClassName(obj,daydata._hoverClassName)) Element.replaceClassName(obj,daydata._hoverClassName, daydata._className);
		}
	},

	load:function(daysData)	{
		//	startDate,
		//	days:array[]
		//	{
		//			data
		//			enabled
		//			isEnabled function 
		//			text1: 'Meeting...',
		//			text2: 'Meeting...'
		//			tooltip 
		//			hasTooltip 
		//			dayStyleClass 
		//	}
		
		this.daysData = this.indexData(daysData);
		this.update();
	},
	
	indexData:function(daysData) {
		var dateYear = daysData.startDate.getFullYear();
		var dateMonth = daysData.startDate.getMonth();
		
		daysData.index = [];
		daysData.index[dateYear+'-'+dateMonth] = 0;
		var idx = daysInMonthByDate(daysData.startDate)-daysData.startDate.getDate()+1;
		
		while (daysData.days[idx])
		{
			if (dateMonth==11) {dateYear++; dateMonth=0;} else dateMonth++;
			daysData.index[dateYear+'-'+dateMonth] = idx;
			idx+= (32 - new Date(dateYear, dateMonth, 32).getDate());
		}
		return daysData;
	},
	
	update:function() {
		this.todayDate = new Date();		
		
		var currentYear = this.currentDate.getFullYear();
		var currentMonth = this.currentDate.getMonth();
		
		var todayflag = (currentYear == this.todayDate.getFullYear() && currentMonth == this.todayDate.getMonth());
		var todaydate =  this.todayDate.getDate();
		
		var selectedflag = (currentYear == this.selectedDate.getFullYear() && currentMonth == this.selectedDate.getMonth())
		var selecteddate = this.selectedDate.getDate();

		var dateDiv = $("div_"+this.params.dayListTableId);
		dateDiv.innerHTML = currentYear + "/" + (currentMonth+1);

		
		var wd = getDay(this.currentDate, this.params.firstWeekDay);
		var currentMonthDays = daysInMonthByDate(this.currentDate);
		var previousMonthDays = daysInMonth(currentYear, currentMonth-1);
		var wn = weekNumber(currentYear, currentMonth, this.params.minDaysInFirstWeek, this.params.firstWeekDay) /// fix it
		
		var p=0;
		this.days = [];
		var dayCounter = previousMonthDays  - wd + 1;
		// previuos month days
		if (wd>0) while (dayCounter<=previousMonthDays) {this.days.push({day:dayCounter, _isWeekend: this.isWeekend(p), _month:-1, _className:"Disableddayclass"}); dayCounter++; p++; }
		dayCounter = 1;
		
		// current month days
		if (this.daysData.index[currentYear+'-'+currentMonth]!=undefined)
		{
			var idx = this.daysData.index[currentYear+'-'+currentMonth];
			var firstDay = this.daysData.days[idx].day;
			while (dayCounter<firstDay)
			{
				if (this.isWeekend(p%7))
					this.days.push({day:dayCounter, _isWeekend: true, _month:0, _className:"Weekenddayclass", _hoverClassName:"Hoveredweekclass"});
				else
					this.days.push({day:dayCounter, _isWeekend: false, _month:0, _className:"Commondayclass", _hoverClassName:"Hovereddayclass"});
				
				dayCounter++;
				p++;
			}
			
			var len = this.daysData.days.length;
			var obj;
			var flag;
			while (idx<len && dayCounter<=currentMonthDays)
			{
				flag = this.isWeekend(p%7);
				obj = this.daysData.days[idx];
				obj._isWeekend = flag;
				obj._month = 0;
				if (flag) {obj._className = "Weekenddayclass"; obj._hoverClassName = "Hoveredweekclass";}
				else {obj._className = "Commondayclass"; obj._hoverClassName = "Hovereddayclass";}
				this.days.push(obj);
				idx++;
				dayCounter++;
				p++;
			}
		}
		while (dayCounter<=currentMonthDays) 
		{
			if (this.isWeekend(p%7))
				this.days.push({day:dayCounter, _isWeekend: true, _month:0, _className:"Weekenddayclass", _hoverClassName:"Hoveredweekclass"});
			else
				this.days.push({day:dayCounter, _isWeekend: false, _month:0, _className:"Commondayclass", _hoverClassName:"Hovereddayclass"});
			dayCounter++;
			p++;
		}
		dayCounter = 1;
		
		// next month days
		var ld = 7-p%7;
		if (ld!=7) while (dayCounter<=ld) { this.days.push({day:dayCounter++, _isWeekend: this.isWeekend(p%7), _month:1, _className:"Disableddayclass"}); }
		
		// render
		p=0;
		var element;
		var dataobj;
		this.selectedDayElement=null;
		for (var k=1;k<7;k++)
		{
			//
			dataobj = this.days[p];
			
			var obj = $(this.params.weekNumberBarId+k).parentNode;
			
			if (k!=1 && currentMonth==11 && dataobj && dataobj.day>27)
			{
				if (getFirstWeek(currentYear+1, this.params.minDaysInFirstWeek, this.params.firstWeekDay).weekNumber==1) wn=1;
			}
			
			element = obj.firstChild;
		    element.innerHTML = (dataobj) ? this.params.weekNumberMarkup( {weekNumber: wn++} ) : "";
		    
		    if (k==1&&wn>52) wn=1;
		   		    
			while (element=element.nextSibling)
			{
				// TODO fix start/stop event's calls
				this.stopCellEvents(element);
				
				if (dataobj) {
					element.data=dataobj.day;
					element.innerHTML = this.params.dayListMarkupArray[p]( dataobj );
					element.className = dataobj._className + (todayflag /*&& dataobj._month==0 */&& dataobj.day==todaydate ? " Todayclass" : "");
					if (selectedflag && dataobj._month==0 && dataobj.day==selecteddate) { this.selectedDateElement = element; element.className+=" Selecteddayclass"; }
					this.setCellEvents(element);
					p++;
				} else {
					//element.data="";
					element.innerHTML = "";
				}
				dataobj = this.days[p];
			}
		}	
	},
	
	nextMonth: function() {
		this.currentDate = new Date(this.currentDate.getFullYear(), this.currentDate.getMonth()+1,1);
		this.update();
	},
	
	prevMonth: function() {
		this.currentDate = new Date(this.currentDate.getFullYear(), this.currentDate.getMonth()-1,1);
		this.update();
	},
	
	today: function() {
		var now = new Date();
		var nowyear = now.getFullYear();
		var nowmonth = now.getMonth();
		var nowdate = now.getDate();
		var updateflag = false;

		if (nowdate!=this.todayDate.getDate()) {updateflag=true; this.todayDate = now;}
		
		if (nowyear != this.currentDate.getFullYear() || nowmonth != this.currentDate.getMonth() )
		{
			updateflag = true;
			this.currentDate = new Date(nowyear, nowmonth, 1);
		}
		
		if (updateflag) this.update();
	}
	
});