function WorkingAnimation() {
	var timer = null;
	var targets = new Array();
	var speed = 600;

	this.animate = function (obj) {
		var t;

		for (t in targets) {
			if (targets[t] == obj)
				return;
		}

		var child = document.createElement('div');
		child.style.width = '100px;';
		child.style.textAlign = 'left';
		child.style.margin = 'auto';
		child.innerHTML = 'working';

		while(obj.hasChildNodes())
			obj.removeChild(obj.firstChild);
		obj.appendChild(child);


		targets.push(obj);

		if (timer == null)
			timer = setInterval(this.step, speed);
	}

	this.stop = function (obj) {
		var t;

		for (t in targets) {
			if (targets[t] == obj) {
				targets.splice(t, 1);
				break;
			}
		}

		if (targets.length == 0 && timer != null) {
			clearInterval(timer);
			timer = null;
		}
	}

	this.step = function() {
		var t;

		for (t in targets) {
			var obj = targets[t];

			if (obj.innerHTML == null)
				continue;

			if (obj.firstChild.innerHTML == 'working...')
				obj.firstChild.innerHTML = 'working';
			else
				obj.firstChild.innerHTML += '.';

		}
	}
}

function Errors() {
	var errors = new Array();

	this.init = function (xml) {
	//xml should be a <errors> element containing one or more <error> tags, with no nesting

		if (xml.nodeName.toLowerCase() != 'errors')
			return;

		errors = new Array();

		for(xml = xml.firstChild; xml != null; xml = xml.nextSibling) {
			if (xml.nodeName.toLowerCase() == 'error') {
				errors.push(new Array(xml.getAttribute('id'), xml.firstChild.nodeValue));
			}
		}

	};
	
	this.num_errors = function() { return errors.length; };

	this.get_error = function (i) { return errors[i]; };


	if (arguments[0] != null)
		this.init(arguments[0]);
}

function Actions() {
	var actions = new Array();

	this.init = function(xml) {
		if (xml.nodeName.toLowerCase() != 'actions')
			return;

		actions = new Array();

		for (xml = xml.firstChild; xml != null; xml = xml.nextSibling) {
			actions[xml.nodeName] = xml.firstChild.nodeValue;
		}
	}

	this.num_actions = function() { return actions.length; };

	this.get_action = function(key) { return actions[key]; };

	if (arguments[0] != null)
		this.init(argument[0]);
}

function User() {
	var id = -1;
	var email = '';
	var errors = new Errors();
	var actions = new Actions();

	this.init = function (xml) {
		for (xml = xml.firstChild; xml != null; xml = xml.nextSibling) {
			switch (xml.nodeName.toLowerCase()) {
			case 'id':
				id = xml.firstChild.nodeValue;
				break;

			case 'email':
				email = xml.firstChild.nodeValue;
				break;

			case 'errors':
				errors.init(xml);
				break;

			case 'actions':
				actions.init(xml);
				break;
			}	
		}
	};

	this.get_id = function () { return id; };

	this.get_email = function() { return email; };

	this.get_errors = function() { return errors; };

	this.get_actions = function() { return actions; };

	if (arguments[0] != null)
		this.init(arguments[0]);
}

function Tile() {
	var id = -1;
	var key = -1;
	var state = '';
	var title = '';
	var description = '';
	var bw = null;
	var score = -1;
	var votes = -1;
	var total_votes = -1;
	var flickr_id = -1;
	var flickr_secret = '';
	var flickr_server = -1;
	var flickr_nsid = '';
	var flickr_user_name = '';
	var flickr_license = '';
	var flickr_size = '';

	var analysis = new Analysis();
	var tags = new Array();

	var errors = new Errors();
	var actions = new Actions();

	this.init = function (xml) {
		for (xml = xml.firstChild; xml != null; xml = xml.nextSibling) {
			switch (xml.nodeName.toLowerCase()) {
			case 'id':
				id = xml.firstChild.nodeValue;
				break;

			case 'key':
				key = xml.firstChild.nodeValue;
				break;

			case 'state':
				state = xml.firstChild.nodeValue;
				break;

			case 'title':
				title = xml.firstChild.nodeValue;
				break;

			case 'description':
				description = xml.firstChild.nodeValue;
				break;

			case 'bw':
				if (xml.firstChild.nodeValue == 'true')
					bw = true;
				else
					bw = false;
				break;

			case 'score':
				score = xml.firstChild.nodeValue;
				break;

			case 'votes':
				votes = xml.firstChild.nodeValue;
				break;

			case 'total_votes':
				total_votes = xml.firstChild.nodeValue;
				break;

			case 'flickr_id':
				flickr_id = xml.firstChild.nodeValue;
				break;

			case 'flickr_secret':
				flickr_secret = xml.firstChild.nodeValue;
				break;

			case 'flickr_server':
				flickr_server = xml.firstChild.nodeValue;
				break;

			case 'flickr_nsid':
				flickr_nsid = xml.firstChild.nodeValue;
				break;

			case 'flickr_user_name':
				flickr_user_name = xml.firstChild.nodeValue;
				break;

			case 'flickr_license':
				flickr_license = xml.firstChild.nodeValue;
				break;

			case 'flickr_size':
				flickr_size = xml.firstChild.nodeValue;
				break;

			case 'analysis':
				analysis.init(xml);
				break;

			case 'tags':
				for (tag_xml = xml.firstChild; tag_xml != null; tag_xml = tag_xml.nextSibling) {
					if (tag_xml.nodeName.toLowerCase() != 'tag')
						continue;
					var t = new Tag();
					t.init(tag_xml);
					tags.push(t);
				}
				break;

			case 'errors':
				errors.init(xml);
				break;

			case 'actions':
				actions.init(xml);
				break;
			}	
		}
	};

	this.vote_up = function (other_score) {
		if (score <= other_score && score < 10)
			score++;

		votes++;
		total_votes++;

		return score;
	};

	this.vote_down = function (other_score) {
		if (score >= other_score && score > 0)
			score--;

		total_votes++;

		return score;
	};

	this.get_flickr_img_url = function(size) {
		if (flickr_id == -1 || flickr_secret == '' || flickr_server == -1)
			return false;

		if (size != null && size != '')
			size = '_'+size;
		else
			size = '';

		return 'http://static.flickr.com/'+flickr_server+'/'+flickr_id+'_'+flickr_secret+size+'.jpg';
	};

	this.get_flickr_url = function() {
		if (flickr_id == -1 || flickr_nsid == '')
			return false;

		return 'http://flickr.com/photos/'+flickr_nsid+'/'+flickr_id+'/';
	};

	this.get_url = function() { return WWW + '/tile/' + id + '/' + key + '/'; };
	this.get_img_url = function(size) { return WWW + '/tile/' + (parseInt(id / 1000) * 1000) + '/' + id + '.' + key + '.'+(size == null ? '300' : size )+'.jpg'; };

	this.set_id = function(i) { return id = i; };
	this.set_key = function(k) { return key = k; };
	this.get_id = function() { return id; };
	this.get_key = function() { return key; };
	this.get_state = function() { return state; };
	this.get_title = function() { return title; };
	this.get_description = function() { return description; };
	this.get_bw = function() { return bw; };
	this.get_score = function() { return score; };
	this.get_votes = function() { return votes; };
	this.get_total_votes = function() { return total_votes; };
	this.get_flickr_id = function() { return flickr_id; };
	this.get_flickr_secret = function() { return flickr_secret; };
	this.get_flickr_server = function() { return flickr_server; };
	this.get_flickr_nsid = function() { return flickr_nsid; };
	this.get_flickr_user_name = function() { return flickr_user_name; };
	this.get_flickr_license = function() { return flickr_license; };
	this.get_flickr_size = function() { return flickr_size; };
	this.get_analysis = function() { return analysis; };
	this.get_tags = function () { return tags; };
	this.get_num_tags = function () { return tags.length; };
	this.get_tag = function(i) { return tags[i]; };

	this.get_errors = function() { return errors; };
	this.get_actions = function() { return actions; };

	function set_state(s) { return state = s; }

	this.flag = function() { set_state('FLAGGED'); };

}

function Tag() {
	var id = -1;
	var flickr_clean = '';
	var flickr_raw = '';

	this.init = function(xml) {
		for (xml = xml.firstChild; xml != null; xml = xml.nextSibling) {
			switch (xml.nodeName.toLowerCase()) {
			case 'id':
				id = xml.firstChild.nodeValue;
				break;

			case 'flickr_clean':
				flickr_clean = xml.firstChild.nodeValue;
				break;

			case 'flickr_raw':
				flickr_raw = xml.firstChild.nodeValue;
				break;
			}
		}
	}

	this.get_id = function() { return id; };
	this.get_flickr_clean = function() { return flickr_clean; };
	this.get_flickr_raw = function () { return flickr_raw; };
}

function Analysis() {
	var id = -1;
	var average = new Array(-1,-1,-1);
	var blocks = new Array(new Array(average,average,average), new Array(average,average,average), new Array(average,average,average));

	this.init = function(xml) {
		for (xml = xml.firstChild; xml != null; xml = xml.nextSibling) {
			switch (xml.nodeName.toLowerCase()) {
			case 'id':
				id = xml.firstChild.nodeValue;
				break;

			case 'average':
				average = xml.firstChild.nodeValue.split(',');
				break;

			case 'block_00':
			case 'block_01':
			case 'block_02':
			case 'block_10':
			case 'block_11':
			case 'block_12':
			case 'block_20':
			case 'block_21':
			case 'block_22':
				blocks[parseInt(xml.nodeName.charAt(6))][parseInt(xml.nodeName.charAt(7))] = xml.firstChild.nodeValue.split(',');
				break;
			}
		}
	};

	this.get_analysis_html = function() {
		var html = document.createElement('div');
		var i, j;

		for (i = 0; i < blocks.length; i++) {
			var row = document.createElement('div');
			for (j = 0; j < blocks[i].length; j++) {
				var block = document.createElement('div');
				block.style.width = '25px';
				block.style.height = '25px';
				block.style.backgroundColor = 'rgb(' + blocks[i][j][0] + ',' + blocks[i][j][1] + ',' + blocks[i][j][2] + ')';
				block.innerHTML = '&nbsp;';

				row.appendChild(block);
			}
			row.style.cssFloat = 'left';
			html.appendChild(row);
		}

//		html.style.height = '75px';
		return html;
	};
}

function Mozaiq() {
	var id = -1;
	var key = -1;
	var owner_id = -1;
	var owner_name = '';
	var title = '';
	var creator = '';
	var bw = false;
	var variance = -1;
	var neighbor_distance = -1;
	var place_in_queue = -1;
	var percent_complete = -1;
	var state = '';

	var errors = new Errors();
	var actions = new Actions();

	this.init = function (xml) {
		for (xml = xml.firstChild; xml != null; xml = xml.nextSibling) {
			switch (xml.nodeName.toLowerCase()) {
			case 'id':
				id = xml.firstChild.nodeValue;
				break;

			case 'key':
				key = xml.firstChild.nodeValue;
				break;

			case 'owner_id':
				owner_id = xml.firstChild.nodeValue;
				break;

			case 'owner_name':
				owner_name = xml.firstChild.nodeValue;
				break;

			case 'title':
				if(xml.firstChild == null)
					title = '';
				else
					title = xml.firstChild.nodeValue;
				break;

			case 'creator':
				if (xml.firstChild == null)
					creator = '';
				else
					creator = xml.firstChild.nodeValue;
				break;

			case 'bw':
				if (xml.firstChild.nodeValue == 'true')
					bw = true;
				else
					bw = false;
				break;

			case 'variance':
				variance = xml.firstChild.nodeValue;
				break;

			case 'neighbor_distance':
				neighbor_distance = xml.firstChild.nodeValue;
				break;

			case 'place_in_queue':
				place_in_queue = xml.firstChild.nodeValue;
				break;

			case 'percent_complete':
				percent_complete = xml.firstChild.nodeValue;
				break;
				
			case 'state':
				state = xml.firstChild.nodeValue;
				break;

			case 'errors':
				errors.init(xml);
				break;

			case 'actions':
				actions.init(xml);
				break;
			}
		}

	};

	this.get_id = function () { return id; };

	this.get_key = function() { return key; };
	
	this.get_owner_id = function() { return owner_id; };
	
	this.get_owner_name = function() { return owner_name; };
	
	this.get_title = function() { return title; };
	
	this.get_creator = function() { return creator; };

	this.get_description = function() { return description; };
	
	this.get_bw = function() { return bw; };
	
	this.get_variance = function() { return variance; };

	this.get_neighbor_distance = function() { return neighbor_distance; };
	
	this.get_place_in_queue = function() { return place_in_queue; };

	this.get_percent_complete = function () { return percent_complete; };
	
	this.get_state = function() { return state; };

	this.get_errors = function() { return errors; };

	this.get_actions = function() { return actions; };
	
	this.get_url = function(www, size) {
		if (id == -1 || key == -1)
			return www;
		return www + '/mozaiq/' + Math.floor(id / 1000) + '/' + id + '.' + key + '.' + size + '.jpg';
	}

	if (arguments[0] != null)
		this.init(arguments[0]);
}
