function props(o) {
	s = ""
	for (p in o)
		s += p + ", ";
	return s;
}

// Poll javascript

var Poll = Class.create();

var PollContext = Class.create();

var PollQuestion = Class.create();
var PollResponse = Class.create();

var PollQuestionLoader = Class.create();
var PollResponseLoader = Class.create();

var PollQuestionFormatter = Class.create();
var PollResponseFormatter = Class.create();

PollContext.prototype = {
	
	initialize: function(url) { 
	
		this.url = '/cgi-bin/poll.rb'
		this.attr = [];
		this.attr['percentage_threshold'] = 25;
	
	},
	
	getPollFromContext: function(name, id) {
		// returns appropriate poll by first
		// checking if it has been answered
		
		var poll_session_name = 'poll_' + id + '_s';
		this.attr['poll_session_name'] = poll_session_name;
		this.attr[poll_session_name] = document.cookie.indexOf(poll_session_name);
		
		if (this.attr[poll_session_name] == -1) {

			p = new PollQuestion(this, name, id);

		} else {
						
			var ajax_options = { method:'post',
				parameters:'id='+id +'&_method=get&r=' + Math.random(),
				requestHeaders:['Content-type','application/x-www-form-urlencoded']
			}
			
			p = new PollResponse(this, name, id, ajax_options);
		}
		
		return p;
	}
}

PollQuestionLoader.prototype = {
	// Talks to the web server
	initialize: function() { },
	loadXml: function(poll) {
		new Ajax.Request(poll.url, {
		  method: 'post',
		  parameters: 'id=' + poll.id + '&_method=get',
		  requestHeaders:['Content-type','application/x-www-form-urlencoded'],
		onSuccess: function(transport) {
		  poll.xml = transport.responseXML;
		  poll.onSuccess();
		},
		onComplete: function() {
			poll.onComplete();
		},
		onException: function() {
			poll.onException();
		}
		});
	}
}

PollResponseLoader.prototype = {
	initialize: function() {  },
	loadXml: function(poll) {
		new Ajax.Request(poll.url, {
		  	method: poll.ajax_options['method'],
			parameters:poll.ajax_options['parameters'],
			requestHeaders:poll.ajax_options['requestHeaders'],
		onSuccess: function(transport) {
		  poll.xml = transport.responseXML;
		  poll.onSuccess();
		},
		onComplete: function() {
			poll.onComplete();
		},
		onException: function() {
			poll.onException();
		}
		});

	}
}

PollResponse.prototype = {
	initialize: function(context, name, id, ajax_options) {
		this.id = id
		this.name = name;
		this.url = context.url;
		this.xml = "";
		this.html = "";
		this.ajax_options = ajax_options;
		this.context = context;
		var target;
		var message;

		if (this.context.attr[this.context.attr['poll_session_name']] > -1) {
			message = 'Loading...';
			target = document.getElementById(this.name);
		} else {
			message = 'Loading results...';
			target = $$("#" + this.name + " .answer")[0];
		}
		
		this.message = message;
		this.target = target;
	},
	load: function() {
		var loader = new PollResponseLoader();
		this.target.innerHTML = this.message;
		loader.loadXml(this);
	},
	format: function() {
		var formatter = new PollResponseFormatter(this);
		this.html = formatter.format(this.xml);
	},
	add: function() {
		new Effect.Highlight(poll.name, {queue: 'end'});
		this.target.innerHTML = this.html;
	},
	onSuccess: function() {
		this.format();
		this.add();
	},
	onComplete: function() {
		
	},
	onException: function() {
		
	}
}

PollQuestion.prototype = {
	initialize: function(context, name, id) {
		this.id = id
		this.name = name
		this.url = context.url
		this.xml = ""
		this.html = ""
		this.percentage_threshold = 25;
		this.context = context
	},
	onSuccess: function() { 
		this.format();
		this.add();
	},
	onComplete: function(){
		// Fires after onSuccess
		this.options = $$("#"+this.name+" .options")[0]
		this.submitter = $$("#" + this.name + " .submitter")[0];
		this.answer = $$("#" + this.name + " .answer")[0];
		this.question = $$("#"+this.name+" .question")[0];
		Event.observe(this.submitter, 'click', this.handle.bindAsEventListener(this), false);
	},
	onException: function() {
		// Bad things
	},
	load: function() {
		loader = new PollQuestionLoader();
		$(this.name).innerHTML = 'Loading question...'
		loader.loadXml(this);
	},
	format: function() {
		var formatter = new PollQuestionFormatter(this);
		this.html = formatter.format(this.xml);
	},
	add: function() {	
		$(this.name).innerHTML = this.html;
	},
	handle: function() {

		if (!this.validate()) return false;
					
		this.ajax_options = { method:'post',
			parameters:Form.serializeElements(this.options.getInputs()),
			requestHeaders:['Content-type','application/x-www-form-urlencoded']
		}
		// TODO: Parameritize
		var poll = new PollResponse(this.context,this.name,this.id,this.ajax_options);
		poll.load();

		new Effect.Fade(this.options, {queue:'end', delay:0.1, duration: 0.25});
		new Effect.Appear(this.answer, {queue:'end', delay:0.1, duration: 0.5});
		
	},
	validate: function() {
	
		var opt_checked = $$("#"+this.name+" .option").find(
			function(opt) { 
				return opt.checked == true;
			});

		if (opt_checked) return true;
	
		alert("Please choose a response.");
	
		return false;
	},
	reset: function() {
		
		$$("#"+this.name+" .options")[0].reset();
	
	}
	
}

PollQuestionFormatter.prototype = {
	initialize: function() {
		this.option_tmpl = new Template('<input class="option" name="option" type="#{mode}" value="#{option_value}">' + ' <label>#{option_text}</label>'+'<br/>');
		this.form_tmpl = new Template('<div class="question">' +
      '<div class="block_stem"><span class="inline_stem">#{stem}</span></div>' +
      '<div class="answer" style="display:none"><span class="wait">Please wait...</span></div>' +
      '<form class="options" name="options">' +
      	'<input name="id" type="hidden" value="#{id}">' +
	'<input name="_method" type="hidden" value="post">' +
      	'<input name="remote_addr" type="hidden" value="#{remote_addr}"/>' +
		'#{option_tmpl}' +
		'<input class="submitter" type="button" value="">' +
		'</form></div>');
		
	},
	format: function(xml) {
		var poll = xml.getElementsByTagName('poll');
		var options = xml.getElementsByTagName('option');
		
		var options_html = "";

		for (i = 0; i < options.length; i++) {
			options_html += this.option_tmpl.evaluate({mode: poll.item(0).getAttribute('mode'), option_value: options[i].getAttribute('id'), option_text: options[i].firstChild.data });
		}
		
		var form_html = this.form_tmpl.evaluate({stem: xml.getElementsByTagName('stem')[0].firstChild.data, id: poll[0].getAttribute('id'), remote_addr: xml.getElementsByTagName('address')[0].firstChild.data, option_tmpl: options_html });
		
		return form_html;
	}
	
}

PollResponseFormatter.prototype = {
	initialize: function(poll) {

		// TODO: Refactor
		this.summary_only = poll.context.attr[poll.context.attr['poll_session_name']];
		this.percentage_threshold = poll.context.attr['percentage_threshold']

		this.question_tmpl = new Template('<div class="block_stem"><span class="inline_stem">#{question}</span></div>')
		this.user_response_tmpl = new Template('<div class="block_response"><span class="inline_response">You said: #{user_response}</span></div>');
		this.forwarder_tmpl = new Template('<div class="block_forwarder"><span class="inline_forwarder">#{forwarder}</span></div>');
		// ISSUE: How to best express optional element?
		this.answers_tmpl = new Template('<div class="block_correct"><span class="inline_correct">#{answers}</span></div>'); 
		this.response_tmpl = new Template('<div class="block_summaryitem"><span class="inline_summaryitem">#{quantity} said: #{response}</span></div>');
		this.response_group_tmpl = new Template('<div class="block_summary_group">#{response_group}</div>');
		this.responses_tmpl = new Template('<div class="block_summary">#{responses}</div>');
		
		this.bargraph_tmpl = new Template('<div class="#{block_bargraph_class}"><span class="#{inline_bargraph_class}" style="width:#{width}"></span></div>');
		
		this.total_responses_tmpl = new Template('<div class="block_total"><span class="inline_total">(Responses: #{total_responses})</span></div>');
		
		this.summary_tmpl = new Template("#{question}\n#{answers}\n#{user_response}\n#{summary}\n#{total_responses}\n#{forwarder}")
	},
	format: function(xml) {
		
		var question_html = '';
		
		if (this.summary_only > -1) {
			var question = xml.getElementsByTagName('question')[0].firstChild.data;
			question_html = this.question_tmpl.evaluate({question: question});
		}
		
		var answers = [];

		if (xml.getElementsByTagName('answers')[0] != null) {
			var el_answers = xml.getElementsByTagName('answers')[0].getElementsByTagName('answer');
			for (n = 0; n < el_answers.length; n++) {
				answers[n] = el_answers.item(n).firstChild.data;
			}
		}
		
		var response_group_html = "";
		var el_responses_group = xml.getElementsByTagName('summary')[0].getElementsByTagName('responses')[0];
		
		var el_responses = el_responses_group.getElementsByTagName('response');
		var total_responses = el_responses_group.getAttribute('total');
		
		for (n = 0; n < el_responses.length; n++) {

			var response_html = "";
			
			var percentage = el_responses.item(n).getAttribute('percentage') + '%';
			var quantity = el_responses.item(n).getAttribute('subtotal');
			
			var block_bargraph_class = 'block_bargraph';
			var inline_bargraph_class = 'inline_bargraph';
			
			if (answers.indexOf(el_responses.item(n).firstChild.data) > -1) {
				block_bargraph_class = 'block_bargraph_correct';
				inline_bargraph_class = 'inline_bargraph_correct';
			}

			response_html = this.response_tmpl.evaluate({quantity: ((total_responses > this.percentage_threshold)?percentage:quantity), response: el_responses.item(n).firstChild.data}) + this.bargraph_tmpl.evaluate({block_bargraph_class: block_bargraph_class, inline_bargraph_class: inline_bargraph_class, width: percentage });

			response_group_html += this.response_group_tmpl.evaluate({response_group: response_html});
			
		}
		
		var responses_html = this.responses_tmpl.evaluate({responses: response_group_html});
		
		// We may not have a response if just showing results summary
		
		var html_user_response = "";
		var el_user_response = xml.getElementsByTagName('user')[0].getElementsByTagName('response')[0];
		if (el_user_response.childNodes.length > 0) {
			html_user_response = el_user_response.firstChild.data;
		}

		var el_forwarder = xml.getElementsByTagName('forwarder')[0];
		
		if (html_user_response.length > 0)
			html_user_response = this.user_response_tmpl.evaluate({user_response: html_user_response});

		var html_answers = ''
		if (answers.length > 0)
			html_answers = this.answers_tmpl.evaluate({answers: answers.join(', ')})

		var html_forwarder = this.forwarder_tmpl.evaluate({forwarder: el_forwarder.firstChild.data});
		var html_total_responses = this.total_responses_tmpl.evaluate({total_responses: total_responses});

		var html_body = this.summary_tmpl.evaluate(
			{ question: question_html,
				answers: html_answers, 
			user_response: html_user_response, 
			summary: responses_html,
			total_responses: html_total_responses,
			forwarder: html_forwarder }
		);

		// TODO: Return results as JSON?
		return html_body;
	}
	
}