
// =============================================================================================================
// constructor - this should NOT be called directly; instead use
// 		this static factory method, i.e., Scheduler.attach (id, program)
// =============================================================================================================

function Scheduler (id, program) {

	// exit if a scheduler for this element already exists
	if (Scheduler.instances[id] !== undefined) {
		return;
	}
	
	this.defaultDescText = "Hover your mouse over an activity to view a description.<br>Click an activity to select it for this block.";

	this.selections = {}; /* user selections are saved here; this is 2 dimentional arrays.  1st: camper, 2nd: activity id */	
	this.id = id;
	this.data = null;
	this.cells = null;
	this.backend = 'scheduler.php';
	this.program = program;
	this.initialized = false; // set to true when initialized
	this.container = document.getElementById(id);
	this.children = [];
	if (!this.container) {
		Scheduler.error("Invalid container id in constructor");
	}

	// add handler to week dropdown and camper dropdown
	var self = this;
	$('#skd_toolbar_week').change	(function() { self.onWeekChange(); });
	$('#skd_toolbar_child').change(function() { self.onChildChange(); });
	
	// save schedule
	$('#skd_toolbar_save').click( function() { 

		$('#dimPanel')
			.css('top', $('#dimPanel').scrollTop() + 'px')
			.show();
			
		$('#skd_save')
			.show()
			.center();
	});

	// skd close button
	$('#skd_save_close').click(function () {
		$('#dimPanel,#skd_save').hide();
	});
	
	// save submit button
	$('#skd_save_submit').click(function () {
		self.submitForm();
	});
	
	// show loader
	$('#skd_loader').show();
	
	// add event to close button on popup
	var self = this;
	$('#skd_popup_close').click(function() {
		self.closePopup();
	}); 	
	
	// init schedule in background; this gets scheduler data and will call initWelcome in callback
	this.initSchedule();
}

// =============================================================================================================
// create account related
// =============================================================================================================

////////////////////////////////////////////////////
// init welcome screen
////////////////////////////////////////////////////

Scheduler.prototype.initWelcome = function () {

	// hide loader
	$('#loader').hide();

	// show welcome popup
	$('#loader_finished').show();

	// attach handler on submit
	var self = this;	
	$('#skd_child_start').click(function () {
		
		// populate table
		self.populateTable();		

		// begin app
		$('#skd_loader').hide();
		$('#skd_app').show();	
	});

}

// =============================================================================================================
// submit form
// =============================================================================================================

Scheduler.prototype.submitForm = function () {

	// disable submit button
	$('#skd_save_submit').attr('disabled', 'true');
	
	// place all form data into json tree
	var inputs = $("#skd_save_form :input");
	var data = {};
	for (var i = 0; i < inputs.length; i++) {
		var parts = inputs[i].name.split('.');
		var tmp = data;
		var oldTmp = null;
		for (var part in parts) {
			
			if (typeof tmp[parts[part]] == 'undefined') {
				tmp[parts[part]] = {};
			}
			oldTmp = tmp;
			tmp = tmp[parts[part]];
		} 
		oldTmp[parts[part]] = inputs[i].value;
	}
	
	// add in selected activities
	data.selections = this.selections;
	
	// submit json to server
	this.talk({
		'action' : 'save', 
		'type' : 'session',
		'data' : $.toJSON(data),
		'session' : 1	
	}, function (success, data) {
		$('#skd_save_submit').removeAttr('disabled');	
		
		if (!success) {
			var msg = data != null ? data.error : "There was a problem with your submission. Try again later.";
			alert(msg);
		} else {		
			// clear form
			$("#skd_save_form :input").clearForm();
		
			$('#skd_save').hide();
			$('#skd_save_complete')
				.center()
				.show();
			
		}
	});	
}

// =============================================================================================================
// init schedule
// =============================================================================================================

//////////////////////////////////////////////////////
// this is called from scheduler constructor
//////////////////////////////////////////////////////
Scheduler.prototype.initSchedule = function () {
	var self = this;

	this.talk({ 
		'action' : 'query', 
		'type' : 'session',
		'session' : 1
	}, function (success, data) {
		self.initialized = true;	
		
		// store json in object
		self.data = data;
		
		// fill in schedule week dropdown 
		var cboWeeks = $('#skd_toolbar_week');
		cboWeeks.html('');
		for (var weekId in data.cells) {
			var option = $(document.createElement('option'));
			option.val(weekId);
			option.html('Week ' + weekId);
			cboWeeks.append(option);
		}
		
		// init welcome
		self.initWelcome();			
	});
}

// =============================================================================================================
// activity popup
// =============================================================================================================

Scheduler.prototype.togglePopup = function (cellId) {
	// vars
	var content = $('#skd_popup_content');	
	
	var cell = $('#skd_cell_' + cellId);
	
	// if popup is currently open, ignore click
	if ($('#skd_popup').css('display') != 'none') {
		return;
	}

	// add popup dialog to grid
	$(this.id).append($('#skd_popup_content'));
	
	// clear any previous content
	content.html('');
	
	// add default description
	$('#skd_popup_desc')
		.addClass('defaultDesc')
		.html(this.defaultDescText);
							
	// if user is clicking on a cell that is active, then interpret this to mean user wants to close dialog
	if (cell.hasClass('active')) {
		this.closePopup();
		return;
	}
	
	// remove all active cells
	$('#' + this.id + " td").removeClass('active');
	
	// make this cell active
	cell.addClass('active');

	// add removal link if this cell has a selection already
	var cellData = this.cells[cellId];	
	var camper = 'demo';	
	var selection = new SchedulerSelection(this, camper, cellData.weekId, cellData.dayId, cellData.blockId)
	if (selection.exists()) {
		var html = "<p class='remove'>" + 
			"<a onClick=\"Scheduler.activityClick('" + this.id + "', " + cellId + ", '');return false\" href=''>" +
			"Remove Activity</a></p>";
		content.append(html);	
	}	
	
	// create list of activities to choose from
	var self = this;	
	var ul = $(document.createElement("ul"));
	for (var activityId in this.cells[cellId].activities) { 
		var li = document.createElement("li");
		var act = this.data.activities[activityId];		
		
		// store data in jQuery object; we must do this and cannot use 
		// local vars in callbacks (via closure) because we are in a loop
		$.data(li, 'description', act.description);
		$.data(li, 'activityId', activityId);
		
		$(li)
			.html(
				'<div class="' + act.program_area.replace(/ /, '') + '">' + 
					act.program_area + 
				'</div>' + 
				'<div class="activity">' + 
					act.name +
					(act.grade != '' ? ' <span class="grade">(Grades ' + act.grade + ')</span>' : '') + 					
				'</div>')
			.click(function () { Scheduler.activityClick(self.id, cellId, $.data(this, 'activityId')); })
			.hover(
				function() {
					$('#skd_popup_desc')
						.removeClass('defaultDesc')					
						.html($.data(this, 'description'));
				},
				function () {
					$('#skd_popup_desc')
						.addClass('defaultDesc')
						.html(self.defaultDescText);
				});
		ul.append(li);	
	}	
	content.append(ul);	
	
	// show popup & dropshadow
	$('#skd_popup')
		.show()
		.center()
		.removeShadow() // remove shadow because 		
		.dropShadow();	
}
//////////////////////////////////////////////////////
// click activity
//////////////////////////////////////////////////////

Scheduler.prototype.clickActivity = function (cellId, activityId) {
	this.selectActivity(cellId, activityId);
	this.renderCell(cellId);
	this.closePopup();	
}

//////////////////////////////////////////////////////
// close popup
//////////////////////////////////////////////////////

Scheduler.prototype.closePopup = function () {
	// remove all active cells
	$('#' + this.id + " td").removeClass('active');
	
	// hide and remove shadow
	$('#skd_popup')	
		.removeShadow()
		.hide();
}

// =============================================================================================================
// toolbar related
// =============================================================================================================

Scheduler.prototype.onWeekChange = function() {
	this.populateTable();
}

Scheduler.prototype.onChildChange = function() {
	this.populateTable();
}

// =============================================================================================================
// grid related
// =============================================================================================================

//////////////////////////////////////////////////////
// populate table
//////////////////////////////////////////////////////

// NOTE: CHILDREN AND SERVER DATA MUST BE PROVIDED BEFORE POPULATING TABLE

Scheduler.prototype.populateTable = function () {

	// prep dictionary lookup of cells; this gets populated later below
	// this lookup only applies to what is currently populated in table, so if user changes week/child
	// then this lookup gets refreshed
	this.cells = {};
	
	// empty table
	this.container.innerHTML = '';

	var data = this.data;

	var html = "<table>\n";
	
	// column groups
	var dowNames = new Array('Sun', 'Mon', 'Tue', 'Wed', 'Thr', 'Fri', 'Sat');
	html += "<colgroup class='leftcol'><colgroup span='" + data.days.length + "'>\n";
	
	// header section
	html += "<thead><tr><th>&nbsp;</th>\n";
	for (var i in data.days) {
		html += "<th>" + dowNames[data.days[i]] + "</th>\n";
	}
	html += "</tr></thead>\n";
	
	// get current week
	var weekId = 1; //$('#skd_toolbar_week').val();
	
	var cellId = 1;
	html += "<tbody>\n";
	
	// loop for each block row for this week
	for (var blockId in data.blocks) {		
		html += "<tr>\n";		
	
		// create first col of block: block name			
		html += 
			"\t<th class='block'>" + 
				"<p class='label'>" + data.blocks[blockId].name + "</p>" + 
				"<p class='time'>" + data.blocks[blockId].begins + "</p>" + 			
			"</th>\n";

		// loop through days of week for this block row
		for (var i in data.days) {
			var dayId = data.days[i];
			var cell =
				typeof data.cells[weekId][blockId] != 'undefined' &&
				typeof data.cells[weekId][blockId][dayId] != 'undefined' ? data.cells[weekId][blockId][dayId] : '';

			if (cell != '') {
				// add helper function for this cell with choice count
				cell.id = cellId;
				cell.dayId = dayId;
				cell.weekId = weekId;
				cell.blockId = blockId;
				
				cell.choices = 0;
				for (var activityId in cell.activities) {
					cell.choices++;
				}
	
				// add cell to dictionary for easy access; this step MUST be done before
				// rendering cell
				this.cells[cellId] = cell;
				
				// add empty cell
				html += "<td id=\"skd_cell_" + cellId + "\">&nbsp;</td>";
				
				cellId++;
			} else {
				html += "<td class='disabled'>&nbsp;</td>";
			}
		}		
		html += "</tr>\n";
	}
	html += "</tbody>\n";

	
	// end table
	html += "</table>\n\n";
	
	
	// place table in document
	this.container.innerHTML = html;
	
	// render cells
	this.renderCells(cellId);
}

//////////////////////////////////////////////////////
// cell click event
//////////////////////////////////////////////////////

Scheduler.prototype.clickCell = function (cellId) {
	var cell = this.cells[cellId];
	if (cell.choices > 0) {
		this.togglePopup(cellId);	
		return;	
	}	else if (cell.choices == 0) {
		return;
	}	
}

//////////////////////////////////////////////////////
// select activity for cell -> 
// remove activity by passing blank activity id
//////////////////////////////////////////////////////

Scheduler.prototype.selectActivity = function (cellId, activityId) {
	var cell = this.cells[cellId];
	
	// store selections in format: selections[user][week_block_day] = activityId
	var selector = new SchedulerSelection(this, /*$('#skd_toolbar_child').val()*/ 'demo', cell.weekId, cell.dayId, cell.blockId);	
	if (activityId == '') {
		selector.remove();
	} else {
		selector.set(activityId);
	}
	this.renderCell(cellId);	
}

//////////////////////////////////////////////////////
// selection helper class
//////////////////////////////////////////////////////

SchedulerSelection = function (caller, camper, week, day, block) {
	if (caller == null || camper == null || week == null || day == null || block == null) {
		throw ("Bad data provided to SchedulerSelection. " + 
			'CALLER: ' + caller + ' ' + 
			'camper: ' + camper +  ' ' + 
			'week: ' + week + ' ' +  
			'day: ' + day + ' ' + 
			'block: ' + block													
		);
	}

	this.caller = caller;
	this.camper = camper;
	this.id = week + '_' + block + '_' + day;
}

SchedulerSelection.prototype.exists = function() {
	var sel = this.caller.selections;
	return typeof sel[this.camper] != 'undefined' && typeof sel[this.camper][this.id] != 'undefined' && sel[this.camper][this.id] != '';
}

SchedulerSelection.prototype.remove = function() {
	if (typeof this.caller.selections[this.camper] != 'undefined' && typeof this.caller.selections[this.camper][this.id] != 'undefined') {
		delete this.caller.selections[this.camper][this.id];
	}
}

SchedulerSelection.prototype.set = function(activityId) {
	if (typeof this.caller.selections[this.camper] == 'undefined') {
		this.caller.selections[this.camper] = {};
	}
	this.caller.selections[this.camper][this.id] = activityId;
}

SchedulerSelection.prototype.get = function() {
	if (this.exists()) {
		return this.caller.selections[this.camper][this.id];
	} else {
		return '';
	}
}

//////////////////////////////////////////////////////
// draw cell
//////////////////////////////////////////////////////

Scheduler.prototype.renderCells = function () {
	for (var cellId in this.cells) {
		this.renderCell(cellId);
	}
}

Scheduler.prototype.renderCell = function (cellId) {
	if (!document.getElementById('skd_cell_' + cellId)) {
		Scheduler.error("Cannot render cell because it does not exist in DOM.");
		return;
	}
	// prep cell, user
	var cell = this.cells[cellId];
	var camper = 'demo'; //$('#skd_toolbar_child').val();
	
	var selection = new SchedulerSelection(this, camper, cell.weekId, cell.dayId, cell.blockId).get();
	var content = "";
	var disabled = true;
	
	if (cell.choices == 1 && cell.required == '1') { // support for text display in disabled block
		for (var activityId in cell.activities) break; // get first member in activities object
		content = "<span>" + this.data.activities[activityId].name + "</span>";
	} else if (cell.choices > 0) {
		content = selection != '' ? this.actDisplayName(this.data.activities[selection]) : "<span>Click to Select</span>";
		disabled = false;
	} 
	
	var self = this;
	
	if (disabled) {
		$('#skd_cell_' + cell.id)
			.removeClass()
			.addClass('disabled')
			.unbind('click')
			.html(content);
	} else {
		$('#skd_cell_' + cell.id)
			.removeClass()
				.addClass(cell.choices > 0 ? 'hover' : '')
				.addClass(cell.choices == 0 ? 'disabled' : '')
				.addClass(selection != '' ? 'required_selected' : (cell.required == 1 ? 'required_not_selected' : ''))
			.unbind('click')
			.click(function() { Scheduler.cellClick(self.id, cell.id); })
			.html(content)
			.css('opacity', 0.1)
			.fadeTo(350, 1);
			
		//if (selection != '') {
		//	$('#skd_cell_' + cell.id).hoverIntent(
		//		function () { self.hoverCellIn(cellId); },
		//		function () { self.hoverCellOut(cellId); }
		//	);		
		//}
			

	}
}

Scheduler.prototype.actDisplayName = function(act) {
	return act.name + (act.grade != '' ? ' <b>(Grades ' + act.grade + ')</b>' : '');
}

// =============================================================================================================
// server talk
// =============================================================================================================

Scheduler.prototype.talk = function (params, funcCallback) {
	var action = params.action;
	var type = params.type;
	params.program = this.program;
	
	// Note: we aren't using $.getJSON method because 
	// it does not have error callback if json parsing fails
  var req = {};
  req.type = "POST";
  req.url = "scheduler.php";
  req.processData = true;
  req.data = params;
  //req.dataType = "json";
  req.error = function (reqObj, textStatus, errorThrown) {
		Scheduler.error("SERVER REQUEST FAILED: " + reqObj.responseText);
		if (funcCallback != null) {
			funcCallback(false, null);
		}				
	}
	
	var self = this;
  req.success = function (data, textStatus) {
  	try {
  		var parsed = $.evalJSON(data);
  	} catch (ex) {
  		Scheduler.error("FAILED TO PARSE JSON: " + data);
 			if (funcCallback != null) {
				funcCallback(false, null);
			}	 		
  		return;
  	}
  
		if (parsed.error != '') {
			//Scheduler.error("SERVER RETURN WITH ERROR MESSAGE: " + parsed.error);
			if (funcCallback != null) {
				funcCallback(false, parsed);
			}			
			return;
		}
		
		if (funcCallback != null) {
			funcCallback(true, parsed);
		}
	}
  $.ajax(req);
}

// =============================================================================================================
// static callbacks
// =============================================================================================================

//////////////////////////////////////////////////////////
// instances related
//////////////////////////////////////////////////////////

// all schedule instances are store in associative array here
Scheduler.instances = {};

// all new instances of scheduler should be attached through here
Scheduler.attach = function (id, program) {
	if (Scheduler.instances[id] !== undefined) {
		return;
	}
	Scheduler.instances[id] = new Scheduler(id, program);
}

//////////////////////////////////////////////////////////
// static callbacks
//////////////////////////////////////////////////////////

// when a user clicks on a cell we get call here; dispatch it to the scheduler instance
Scheduler.cellClick = function (skdId, cellId) {
	if (Scheduler.instances[skdId] === undefined) {
		return;
	}
	Scheduler.instances[skdId].clickCell(cellId);	
}

// when a user clicks on a choice activity we get call here; dispatch it to the scheduler instance
Scheduler.activityClick = function (skdId, cellId, activityId) {
	if (Scheduler.instances[skdId] === undefined) {
		return;
	}
	Scheduler.instances[skdId].clickActivity(cellId, activityId);	
}

//////////////////////////////////////////////////////////
// static functions for debugging
//////////////////////////////////////////////////////////

Scheduler.log = function (msg) {
	if (typeof console != 'undefined' && typeof console.log != 'undefined') {
		console.log(msg);
	}
}

Scheduler.error = function (msg) {
	if (typeof console != 'undefined' && typeof console.error != 'undefined') {
		console.error(msg);
	} else {
		throw new exception(msg);
	}
}

// =============================================================================================================
// jquery stuff
// =============================================================================================================


jQuery.fn.clearForm = function() {
  return this.each(function() {
 var type = this.type, tag = this.tagName.toLowerCase();
 if (tag == 'form')
   return $(':input',this).clearForm();
 if (type == 'text' || type == 'password' || tag == 'textarea')
   this.value = '';
 else if (type == 'checkbox' || type == 'radio')
   this.checked = false;
 else if (tag == 'select')
   this.selectedIndex = -1;
  });
};


jQuery.fn.center = function(){
	this.css("position", "absolute");
  
  var top = ( $(window).height() - this.height() ) / 2 + $(window).scrollTop() + "px";
	var left = ( $(window).width() - this.width() ) / 2 + $(window).scrollLeft() + "px";
	//alert(top + ' ' + left);
	this.css("top", top);
  this.css("left", left);
  return this;
};

 
