
Calendar = createSingleton(Calendar = {});
Calendar.prototype.init = function(args) {
	 /*
		parent			: Parent div for calendar
		date			: Default date
		onSelect		: Handler to be invoked on date-select
		onClose			: Handler to be called on popup-close
		allowBackDate	: Enable back dates
		sysDate			: Use as system date
    */
	if(!isDefined(args))
		args = {};
	defaults = { parent: "CalendarDiv", date: new Date(), onSelect: null, onClose: null, 
	             allowBackDate: false, sysDate: new Date(), root: ""};
	for(var pr in defaults)
		this[pr] = isDefined(args[pr]) ?args[pr] :defaults[pr];
	
	this.layout = null;
	this.active = false;
	this.activeCell = null;
	this.domMgr = null;

	var cal = this;

    var headerTitle = {
		tagName: "tr",
		
		layout: [{
			tagName: "td",
			cssClass: "title",
			attributes: { colSpan: "6"},
			layout: function(el, mgr) {
				el.colSpan = 6;
				el.style.paddingTop = "2px"; // Fix for mozilla
				el.style.paddingBottom = "2px"; // Fix for mozilla
				el.style.paddingLeft = "2px"; // Fix for mozilla
				el.style.paddingRight = "2px"; // Fix for mozilla
			},
			updateFunc: function(el, mgr) {	
				el.innerHTML =  "Select date :";
			}
			
			
		},
		{
			tagName: "td",
			cssClass: "close",
			attributes: {align: "right", disable: "false", type: "closeDiv", unselectable: true},
			layout: "<img src='" + cal.root + "images/cross-close.gif' />"
		}
		]
	};
	
	var headerNav = {
		tagName: "tr", 
		layout: [{
			tagName: "td", 
			cssClass: "navBtn",
			attributes: { type: "prevMon", disable: "false", unselectable: true },
			updateFunc: function(el, mgr) {
			    if(!cal.belongsToMonth(cal.sysDate))
			        el.innerHTML =  "<img src='" + cal.root + "images/callt.gif' />";
			    else
			        el.innerHTML =  ""; 
			    }			    
			},{
			tagName: "td",
			cssClass: "heading",	
			attributes: { type: "heading", colSpan: "5" },
			layout: function(el, mgr) {
				el.colSpan = 5; // Fix for mozilla
					el.style.paddingTop = "2px"; // Fix for mozilla
				el.style.paddingBottom = "2px"; // Fix for mozilla
					el.style.paddingLeft = "2px"; // Fix for mozilla
				el.style.paddingRight = "2px"; // Fix for mozilla
			},
			updateFunc: function(el, mgr) {	
				el.innerHTML = cal.getHeading();
			}
		},{
			tagName: "td", 
			cssClass: "navBtn",
			attributes: { type: "nextMon", disable: "false", unselectable: true },
			layout: "<img src='" + cal.root + "images/calrt.gif' />"
		}]
	};
	
	var headerCap = {
		tagName: "tr",
		foreach: { startIndex: 0, stopIndex: 6, indexKey: "day"	},
		layout: {
			tagName: "td",
			cssClass: "week",
			layout: function(el, mgr) {
			
					el.style.paddingTop = "2px"; // Fix for mozilla
				el.style.paddingBottom = "2px"; // Fix for mozilla
					el.style.paddingLeft = "2px"; // Fix for mozilla
				el.style.paddingRight = "2px"; // Fix for mozilla
			},
			updateFunc: function(el, mgr) {
				el.innerHTML = Util.shortWeekDays[mgr.indices.day];
			}
		}
	};

	var header = {
		tagName: "thead",
		cssClass: "header",
		layout: [ headerTitle, headerNav, headerCap ]
	};

	var bodyCell = {
		tagName: "td",
		cssClass: "cell",
		attributes: { type: "dateCell", disable: "true", unselectable: true },
		layout: function(el, mgr) {
			
					el.style.paddingTop = "2px"; // Fix for mozilla
				el.style.paddingBottom = "2px"; // Fix for mozilla
					el.style.paddingLeft = "2px"; // Fix for mozilla
				el.style.paddingRight = "2px"; // Fix for mozillaz
				
			},
		updateFunc: function(el, mgr) {
			var dt = cal.getDateFor(mgr.indices.row, mgr.indices.col);
			var show = cal.belongsToMonth(dt);
			var disable = cal.isBackDate(dt);
			var hiLite = cal.isCurDate(dt);
			
			el.innerHTML = (show) ?dt.getDate() :"&nbsp;"; 
			el.setAttribute("day", (show) ?dt.getDate() :-1);
			el.style.cursor = (show && !disable) ?"pointer" : "";
			el.style.backgroundColor = (hiLite && !disable) ?"#fff782" :"#ffffff";
			el.style.color = (disable) ? "#999999" :(hiLite) ?"#000000" :"#000000";
			el.setAttribute("disable", (show && !disable) ?"false" :"true");
		},
		handlers: {
			"mouseover": function(ev, mgr) {
				var el = DOM.getTargetElement(ev);
				
				if(el.getAttribute("disable") == "false") {
				    
					el.setAttribute("bgColor", el.style.backgroundColor);
					el.style.backgroundColor = "#d59f32";
				}
			},
			"mouseout": function(ev, mgr) {
				var el = DOM.getTargetElement(ev);
				var c = el.getAttribute("bgColor");
				if(!isEmpty(c))	el.style.backgroundColor = c;	
				el.setAttribute("bgColor", "");
			}
		}
	};

	var bodyRow = {
		tagName: "tr",
		foreach: { startIndex: 0, stopIndex: 6, indexKey: "col" },
		layout: bodyCell
	};

	var body = {
		tagName: "tbody",
		cssClass: "body",
		foreach: { startIndex: 0, stopIndex: 5, indexKey: "row" },
		layout: bodyRow
	};

	var onDocMouseUp = function(ev, mgr) {
		var el = DOM.getTargetElement(ev);
		if(/img/i.test(el.tagName))
			el = el.parentNode;
		var cal = Calendar.getInstance();
		var mgr = cal.domMgr;
		if(cal.activeCell == el) {
			switch (el.getAttribute("type")) {
				case "prevMon":	cal.updateMonthBy(-1);	break;
				case "dateCell": 
					el.setAttribute("bgColor", "");
					cal.onDateSelect(el.getAttribute("day")); 
					break;
				case "nextMon":	cal.updateMonthBy(1);	break;
				case "closeDiv": cal.hide();	break;
			}
		}
		cal.activeCell = null;
		DOM.removeEvent(document, "mouseup", onDocMouseUp);
	};

	this.layout = {
		tagName: "div",
		cssClass: "cal",
		layout: {
			tagName: "table",
			style: { borderCollapse: "collapse", verticalAlign: "middle"},
			layout: [ header, body ]
		},
		handlers: {
			"mousedown": function(ev, mgr) {			    
				var el = DOM.getTargetElement(ev);				
				if(/img/i.test(el.tagName))
				    el = el.parentNode;
				switch (el.getAttribute("type")) {
					case "prevMon":	
					case "dateCell": 
					case "closeDiv":
					case "nextMon":	
						if(el.getAttribute("disable") == "false") {
							cal.activeCell = el; 
							DOM.addEvent(document, "mouseup", onDocMouseUp);
						}
					break;
					
					default: cal.activeCell = null;
				}
				DOM.stopEvent(ev);
			}
		}
	};

	this.update = function(dt) {
		dt = dt || this.date;
		this.firstCellDate = this.getFirstCellDate();
		this.domMgr.update();
	};
	
	// Get date for cell at ith row and jth column
	this.getDateFor = function(i, j) {
		var offset = i*7+j;
		var first = this.firstCellDate;
		var dt = new Date(first.getFullYear(), first.getMonth(), (first.getDate() + offset));
		return dt;
	};
	
	// Does date belong to current date
	this.belongsToMonth = function(dt) {
		var mn = this.date.getMonth();
		var yr = this.date.getFullYear();
		return (dt.getMonth() == mn && dt.getFullYear() == yr);
	};

	// Is date a back-date
	this.isBackDate = function(dt) {
		if(this.allowBackDate) return false;
		var diff = Util.compareTimes(dt, this.sysDate, true);
		return diff < 0;
	};

	// Is date equal to calendar date
	this.isCurDate = function(dt) {
		var d = this.date.getDate();
		var mn = this.date.getMonth();
		var yr = this.date.getFullYear();
		return (dt.getDate() == d && dt.getMonth() == mn && dt.getFullYear() == yr);
	};

	// Get text for heading
	this.getHeading = function() {
		var dt = this.date;
		return Util.months[dt.getMonth()] + ", " + dt.getFullYear();
	};

	// Update month, and calendar
	this.updateDayBy = function(offset) {
		this.date.setDate(this.date.getDate()+offset);
		this.validateDate();
		this.update();
	};

	// Update month, and calendar
	this.updateMonthBy = function(offset) {
		this.date.setMonth(this.date.getMonth()+offset);
		this.validateDate();
		this.update();
	};

	// Method to handle date-select
	this.onDateSelect = function(day) {
		if(day == -1) return;
		this.date.setDate(day);
		this.select();
	};
	
	// Validate date, and reset accordingly if invalid
	this.validateDate = function() {
		var diff = Util.compareTimes(this.date, this.sysDate, true);
		if(diff < 0 && !this.allowBackDate)
			this.date = new Date(this.sysDate);
	};

	// Method to get date for first cell (sun)
	this.getFirstCellDate = function() {
		var dt = new Date( this.date.getFullYear(), this.date.getMonth(), 1);
		var weekDay = dt.getDay();
		
		if(weekDay != 0)
			dt.setDate(-1*(weekDay-1));
		return dt;
	};

	// Method to display calendar
	this.show = function(onSelect, onClose, el) {
		var s =	this.parent.style;
		if(isDefined(el)) {
			var pos = DOM.getAbsolutePos(el);
			s.left = pos.x + 'px';
			s.top = pos.y + el.offsetHeight + 'px';
		}
		s.display = "";

		this.update();
		this.onSelect = isDefined(onSelect) ?onSelect :null;
		this.onClose = isDefined(onClose) ?onClose :null;
		
		if(isDefined(el.blur))
			el.blur();
		this.active = true;
		DOM.register("mousedown", this, this.onMouseDown);
		DOM.register("keydown", this, this.onKeyDown);
	};

	// Method for callback
	this.select = function() {
		if(this.onSelect)
			this.onSelect(this.date);
		this.hide();
	};

	// Method to hide calendar
	this.hide = function(onSelect) {
		this.parent.style.display = "none";
		if(this.onClose)
			this.onClose();

		this.active = false;
		DOM.unRegister("mousedown", this);
		DOM.unRegister("keydown", this);
	};

	// Method for handling clicks outside calendar
	this.onMouseDown = function(ev, cal) {
		cal.hide();
	};

	// Method for handling keyboard events
	this.onKeyDown = function(ev, cal) {
		switch(ev.keyCode) {
			case DOM.keys.ESC:
				cal.hide(); break;
			case DOM.keys.SPACE:
			case DOM.keys.ENTER:
				cal.select(); break;
			case DOM.keys.UP: cal.updateDayBy(-7); break;
			case DOM.keys.DOWN: cal.updateDayBy(7); break;
			case DOM.keys.LEFT: cal.updateDayBy(-1); break;
			case DOM.keys.RIGHT: cal.updateDayBy(1); break;
		}
	};

	// Calendar dom-manager (manage DOM manipulation)
	this.domMgr = new DomMgr({ parent: this.parent, layout: this.layout});
	this.domMgr.create();
	this.parent = this.domMgr.parent;
	DOM.setStyle(this.parent, { position: "absolute", display: "none", width: "162px" , height: "170px"});
	
};