/*
 * Schedule Coordinator
 *   by Weston Ruter <http://weston.ruter.net/>
 * Copyright 2007. All Rights Reserved.
 * Licensed under Attribution-Noncommercial-Share Alike 3.0 United States
 *  <http://creativecommons.org/licenses/by-nc-sa/3.0/us/>
 *
 */


$(document).ready(function(){
	coordinate();
	
	function checkClickedState(){
		if(!this.checked)
			$('#dataRows').addClass('hidePersons');
		else
			$('#dataRows').removeClass('hidePersons');
	}
	$('#showPersons').click(checkClickedState);
	checkClickedState.apply($('#showPersons')[0]);
});

//function getRange(start, end, step){
//	var vals = [];
//	for(var i = start; i <= end; i += step){
//		vals.push(i);
//	}
//	return vals;
//}
function log(m){
	var div = document.createElement('div');
	div.innerHTML = m;
	document.body.appendChild(div);
}

function coordinate(){
	$('#dataRows > *').remove();

	//initialize the data structure
	var personsUnavailable = {};
	jQuery(['mon','tue','wed','thu','fri']).each(function(){
		var day = this;
		for(var time = 8; time <= 23.5; time += 0.5){
			personsUnavailable[day + time] = {};
		}
	});
	
	var maxUnavailableCount = 0;
	var currentPerson;
	var personVistedDays; 
	
	//get each line
	jQuery(document.getElementById('data').value.split(/\n/)).each(function(){
		var line = this;
		if( nameMatches = line.match(/^\((\w+)\)/) ){
			//block out all times for the day that the person did not supply
			if(personVistedDays){
				for(var day in personVistedDays){
					if(!personVistedDays[day]){
						for(var globalTime = 8; globalTime <= 23.5; globalTime += 0.5){
							personsUnavailable[day + globalTime][currentPerson] = true;
						}
					}
				}
			}
			currentPerson = nameMatches[1];
			personVistedDays = {mon:false,tue:false,wed:false,thu:false,fri:false};
		}
		if(!(lineParts = line.toLowerCase().match(/^(\w+)\s*(\d.+)$/)))
			return;
		var day = lineParts[1];
		personVistedDays[day] = true;
		var timeList = lineParts[2];
		
		var isAvailableTime = {};
		
		//get each time slot of a day
		if(timeList){
			jQuery(timeList.split(/[;,]\s*/)).each(function(){
				var t = this;
				var timeParts = t.split(/-/);
				var t1 = time2int(timeParts[0]);
				var t2 = time2int(timeParts[1]);
				for(var time = t1;  time < t2; time += 0.5)
					isAvailableTime[time] = true;
			});
		}
		//for(var globalTime in unavailableTimes[day]){
		for(var globalTime = 8; globalTime <= 23.5; globalTime += 0.5){
		//getRange(8, 23.5, 0.5).each(function(globalTime){
			if(!isAvailableTime[globalTime])
				personsUnavailable[day + globalTime][currentPerson] = true;
		}
	});
	
	//block out all times for the day that the person did not supply
	for(var day in personVistedDays){
		if(!personVistedDays[day]){
			//getRange(8, 23.5, 0.5).each(function(globalTime){
			for(var globalTime = 8; globalTime <= 23.5; globalTime += 0.5){
				personsUnavailable[day + globalTime][currentPerson] = true;
				
				//log('personsUnavailable[' + day + ' + ' + globalTime + '][' + currentPerson + ']');
			}
		}
	}
	
	for(var key in personsUnavailable){
		var count = 0;
		for(var x in personsUnavailable[key]){
			count++;
		}
		
		maxUnavailableCount = Math.max(maxUnavailableCount, count); //$H(personsUnavailable[key]).keys().length
	}

	for(var time = 8; time <= 23.5; time += 0.5){
		var tr = document.createElement('tr');
		var th = document.createElement('th');
		th.innerHTML = int2time(time);
		tr.appendChild(th);
		
		jQuery(['mon','tue','wed','thu','fri']).each(function(){
			var day = this;
			var td = document.createElement('td');
			var persons = []; //$H(personsUnavailable[day + time]).keys();
			for(var person in personsUnavailable[day + time]){
				persons.push(person);
			}
			
			var unavailCount = persons.length;
			td.innerHTML = unavailCount + "<div class='persons'>" + persons.sort().join(', ') + "</div>";
			td.title = "Unavailable at this time: " + persons.sort().join(', ');
			var color = new RGBHSV(false);
			color.setHSV(120-120*(unavailCount/maxUnavailableCount),30,100);
			td.style.backgroundColor = color.value();
			tr.appendChild(td);
		});
		th = document.createElement('th');
		th.innerHTML = int2time(time);
		tr.appendChild(th);
		
		$('#dataRows')[0].appendChild(tr);
	}
}

function time2int(t){
	var mins = t.split(/:/)[1]/60;
	var hours = parseInt(t.replace(/^0/,''));
	return hours + mins;
}
function int2time(i){
	var mins = i-parseInt(i) == 0.5 ? '30' : '00';
	return parseInt(i) + ":" + mins;
}

function array2hash(array, bucketValue){
	var hash = {};
	jQuery(array).each(function(){
		hash[this] = bucketValue;
	});
	return hash;
}





/* RGB <-> HSV */
/* R. McFarland 20060601 ... http://blog.elinc.ca/rod/2006/06/01/javascript-hsvrgb-object/files/rgbhsv/rgbhsv.js */

function RGBHSV(cache){
	this.R=0;
	this.G=0;
	this.B=0;
	this.H=0;
	this.S=0;
	this.V=0;
	this.caching=cache;

	this.rgbdict=Array();
	this.hsvdict=Array();

	this.hash=function(arr){
		h=360*360*arr[0]+256*arr[1]+arr[2];
		return h;
	}		
	
	this.setRGB=function(r,g,b){ // rgb 0-255
		this.R=r;
			if(this.R<0)this.R=0;
			if(this.R>255)this.R=255;
		this.R /= 255.0;
		this.G=g;
			if(this.G<0)this.G=0;
			if(this.G>255)this.G=255;
		this.G /= 255.0;
		this.B=b;
			if(this.B<0)this.B=0;
			if(this.B>255)this.B=255;
		this.B /= 255.0;
		h=this.hash(Array(r,g,b));
		if(this.caching && this.hsvdict[h]){
			this.H=this.hsvdict[h][0];
			this.S=this.hsvdict[h][1];
			this.V=this.hsvdict[h][2];
		}else{
			this.calcHSV();
			this.hsvdict[h]=Array(this.H,this.S,this.V);
		}
	}

	this.setHSV=function(h,s,v){ // hsv is hue (0-360),saturation (0-100),value (0-100)
		this.H=h%360; //degrees
		this.S=s;
			if(this.S<0)this.S=0;
			if(this.S>100)this.S=100;
		this.S /= 100.0;
		this.V=v;
			if(this.V<0)this.V=0;
			if(this.V>100)this.V=100;
		this.V /= 100.0;
		h=this.hash(Array(h,s,v));
		if(this.caching && this.rgbdict[h]){
			this.R=this.rgbdict[h][0];
			this.G=this.rgbdict[h][1];
			this.B=this.rgbdict[h][2];
		}else{
			this.calcRGB();
			this.rgbdict[h]=Array(this.R,this.G,this.B);
		}
	}
	
	this.calcRGB=function(){
		if (this.S==0.0){   // color is on black-and-white center line
			R=this.V;          // achromatic: shades of gray
			G=this.V;          // supposedly invalid for h=0 but who cares
			B=this.V;
		}else{ // chromatic color
			if (this.H==360.0){// 360 degrees same as 0 degrees
				hTemp=0.0;
			}else{
				hTemp=this.H;
			}
			hTemp=hTemp/60.0;   // h is now in [0,6)
		    
			fi=Math.floor(hTemp);  // largest integer <= h
			fr=hTemp-fi;          // fractional part of h 
			p=this.V*(1.0-this.S);
			q=this.V*(1.0-(this.S*fr));
			t=this.V*(1.0-(this.S*(1.0-fr)));
		
			if(fi==0){
				R = this.V;
				G = t;
				B = p;
			}else if(fi==1){
				R = q;
				G = this.V;
				B = p;
			}else if(fi==2){
				R = p;
				G = this.V;
				B = t;
			}else if(fi==3){
				R = p;
				G = q;
				B = this.V;
			}else if(fi==4){
				R = t;
				G = p;
				B = this.V;
			}else{
				R = this.V;
				G = p;
				B = q;
			}
		}
		this.R=R;
		this.G=G;
		this.B=B;
		return;		
	}
	
	this.calcHSV=function(){
		minVal=Math.min(Math.min(this.R, this.G), this.B);
		V=Math.max(Math.max(this.R, this.G), this.B);
		Delta=V-minVal;

		// Calculate saturation: saturation is 0 if r, g and b are all 0
		if (V==0.0){
			S=0.0;
		}else{
			S=Delta / V;
		}
		if (S==0.0){
			H=0.0;    // Achromatic: When s = 0, h is undefined but who cares
		}else{       // Chromatic
			if (this.R==V){ // between yellow and magenta [degrees]
				H=60.0*(this.G-this.B)/Delta;
			}else{
				if (this.G==V){ // between cyan and yellow
					H=120.0+60.0*(this.B-this.R)/Delta;
				}else{ // between magenta and cyan
					H=240.0+60.0*(this.R-this.G)/Delta;
				}
			}
		}
		if (H<0.0) H=H+360.0;
  		this.H=H; this.S=S; this.V=V;
		return;
	}
	
	this.value=function(){
		string='rgb('
			+Math.floor(this.R*255)+','
			+Math.floor(this.G*255)+','
			+Math.floor(this.B*255)+')';
		return string;
	}
}
