/**
 * FBTO Keuzehulp Zorg
 * V2.0 - 18 november 2009
 */
(function($){
	
	window.KeuzeHulpZorg = {
		initialize:function() {
			
			var url = $('head link[rel=questions]').attr('href');

			this.questionManager = new QuestionManager({
				container: '#keuzehulp-zorg',
				intro: '.keuzehulp-intro',
				questions: '.keuzehulp-questions',
				answers: '.keuzehulp-answers',
				selected: '.keuzehulp-selected',
				advice: '.keuzehulp-advice',
				tooltip: '.keuzehulp-tooltip',
				tipcontent: '.tooltip-content',
				tiparrow: '.tooltip-arrow',
				buttons: '.keuzehulp-buttons',
				pages: '.keuzehulp-steps',
				data: url
			});			
		}		
	};

	var REG_CHOICE = /qm-choose/i;
	var REG_CONTENT = /[a-z]/;
	
	/**
	 * Question manager
	 */
	function QuestionManager(settings) {
		this.root = $(settings.container);
		this.container = $(settings.questions);
		this.answers = $(settings.answers);
		this.advice = $(settings.advice);
		this.$selected = $(settings.selected);
		this.$tooltip = $(settings.tooltip);
		this.$tipcontent = $(settings.tipcontent);
		this.$tiparrow = $(settings.tiparrow);
		
		this.form = $('form', settings.container);
		this.questions = [];
		this.index = -1;

		this.validator = new Validator(settings);
		this.view = new QuestionView(settings);
		
		this.inputData = new LBi.HashMap();
		this.adviceData = new LBi.HashMap();
		this.selected = new LBi.HashMap();
		
		this.relations = new LBi.LinkRelations();
		this.relations.subscribe(/qm-/, this.handleClick.bind(this));

		LBi.Dispatcher.subscribe('click:input', this.handleInputClick.bind(this));

		this.initTooltip();
		this.loadQuestions(settings.data);
	}

	QuestionManager.prototype = {
		constructor: QuestionManager,

		loadQuestions:function(url) {
			$.get(url, this.parseQuestions.bind(this), 'xml');
		},

		parseQuestions:function(response) {
			var $xml = $(response);
			var config = $xml.find('config');
			var questions = $xml.find('step');
			var l = questions.length;
			
			for(var i=0; i<l; i++) {
				this.questions.push(
					new Question(questions[i])
				);
			}

			this.applySettings(config);
			this.renderQuestions();
			this.next();
		},

		applySettings:function(config) {
			this.remoteURL = config.attr('action');
			this.remoteMethod = config.attr('method');
			this.description = config.find('description').text();
			this.groups = config.find('groups').text();
		},

		handleClick:function(e) {
			var link = e.target;
			var rel = link.rel;
			var type = /qm-([^ ]+)/.exec(rel)[1];

			switch (type) {
				case 'next': this.tryNext(); break;
				case 'previous': this.previous(); break;
				
				case 'answer':
					if(this.validate()) {
						this.answerQuestion();
					}
				break;
				case 'advice':
					if(this.validate()) {
						this.advise();
					}
				break;
				case 'choose':
					this.chooseAnswer(link);
				break;
				
				case 'reset':	this.reset(); break;
				case 'start':	this.start(); break;
				case 'submit':	this.submit(); break;

				case 'description':
					this.toggleTooltip(true, link, this.description);
				break;
				
				case 'open':
					this.reset();
					this.root.fadeIn();
				break;
				case 'close':
					this.root.fadeOut();
				break;
			}

			e.preventDefault();
		},


		handleInputClick:function(e) {
			var input = e.target;
			var name = input.name;
			var uid = input.id;
			if(!uid) {
				return;
			}

			var advice = this.view.getAdvice(uid);
			if(advice.length > 1) {
				advice = this.filterAdvice(advice);
			}

			var value = advice[0].getAttribute('value');
			if(value) {
				input.value = value;
			}

			this.inputData.put(name, input.value);

			var question = this.currentQuestion.id;
			this.adviceData.put(question, advice.text());
		},

		initTooltip:function() {
			this.root.bind('mouseover', this.handleTooltip.bind(this));
			this.$tooltip.hover(
				this.tooltipOver.bind(this),
				this.tooltipOut.bind(this)
			);
		},
		
		handleTooltip:function(e) {
			var link = $(e.target).closest('a');
			var button = link[0];
			if(button) {
				var rel = button.rel;
				if(REG_CHOICE.test(rel)) {
					var id = /#(.*)$/.exec(button.href)[1];
					var advice = this.view.getAdvice(id);
					if(advice.length > 1) {
						advice = this.filterAdvice(advice);
					}
					var text = advice? advice.text() : null;
					if(advice && text && REG_CONTENT.test(text)) {
						this.delayedTooltip(true, button, text);
					} else {
						this.toggleTooltip(false);
					}
				}
			} else {
				this.delayedTooltip(false);
			}
		},

		delayedTooltip:function(toggle, button, html) {
			clearTimeout(this.tooltipTimer);
			if(this.tooltipHover) {
				return;
			}

			var self = this;
			if(toggle) {
				this.tooltipTimer = setTimeout(function(){
					self.toggleTooltip(toggle, button, html);
				}, 100);
			} else {
				this.tooltipTimer = setTimeout(function(){
					self.toggleTooltip(false);
				}, 300);
			}
		},
			tooltipOver:function () {
				this.tooltipHover = true;
				clearTimeout(this.tooltipTimer);
			},

			tooltipOut:function () {
				this.tooltipHover = false;
				this.delayedTooltip(false);
			},

		toggleTooltip:function(toggle, button, html){
			if(toggle) {
				var off = this.root.offset();
				var top = off.top;
				var left = off.left;

				var b = $(button).offset();
				this.$tipcontent.html(html);
				this.$tooltip.css({top: (b.top - top + 70)});
				this.$tiparrow.css({
					left: (b.left - left) + (button.offsetWidth/2) - 55 
				});
				this.$tooltip.hide();
				this.$tooltip.fadeIn(100);
			} else {
				this.$tooltip.fadeOut(100);
			}
		},

		filterAdvice:function(adviceList) {
			var l = adviceList.length;
			for(var i=0; i<l; i++) {
				var advice = adviceList[i];
				var target = advice.getAttribute('for');
				var exclude = advice.getAttribute('not');
				
				if(this.matchesInputFor(target, exclude)) {
					return adviceList.eq(i);
				}
			}
		},

		matchesInputFor:function(values, exclude) {
			var includers = values.split(',');
			var excluders = (exclude || '').split(',');

			function filter(requirements, data) {
				for(var j=0; j<requirements.length; j++) {
					var pair = requirements[j].split('=');
					var value = data.get(pair[0]);
					if(value && value === pair[1]) {
						return true;
					}
				}
				return false;
			}
			
			var data = this.inputData;
			var valid = filter(includers, data) & !filter(excluders, data);

			return valid;
		},

		validate:function(){
			return LBi.Dispatcher.fire('validate', this.currentQuestion);
		},

		renderQuestions:function() {
			var questions = this.view.renderQuestions(this.questions);
			LBi.DOM.append(this.container, $(questions));
			this.$questions = $('.qm-question', this.container);
		},

		answerQuestion:function(skipAdvice) {
			var question = this.currentQuestion.id;
			var advice = this.adviceData.get(question);
			var pages = this.$questions.length;

			this.answers.html(advice);
			this.view.setPercentage((this.index+1)/pages);
			this.view.setState('answer');

			var title = this.answers.find('h2:first').clone();
			if(title.length && !title.hasClass('informative')) {
				this.selected.put(question, title);	
			}

			this.view.displaySummary(this.selected);

			if(skipAdvice || !REG_CONTENT.test(advice)) {
				this.tryNext();
			}
		},

		chooseAnswer:function(link) {
			var id = /#(.*)$/.exec(link.href)[1];
			var input = document.getElementById(id);
			input.checked = true;
			LBi.Dispatcher.fire('click', input);

			this.answerQuestion(true);
		},

		next:function() {
			var last = this.questions.length -1;
			if(++this.index > last ) { 
				this.index = last;
				return false;
			}

			var question = this.questions[this.index];
			var filter = question.filter;
			var exclude = question.exclude;
			if(filter && !this.matchesInputFor(filter, exclude)) {
				return this.next();
			}
			
			this.setQuestion(this.index);
			return true;
		},

		tryNext:function() {
			if(!this.next()) {
				this.advise();
			}
		},

		previous:function() {
			if(this.currentQuestion) {
				var question = this.currentQuestion.id;
				this.selected.remove(question);
				this.view.displaySummary(this.selected);
			}

			switch(this.view.currentState) {
				case 'question':
				case 'triggered':
					if(--this.index < 0) {
						this.index = 0; 
					}
				break;
			}

			var question = this.questions[this.index];
			var filter = question.filter;
			var exclude = question.exclude;
			if(filter && !this.matchesInputFor(filter, exclude)) {
				this.previous();
				return;
			}

			this.setQuestion(this.index, true);
		},

		setQuestion:function(index, reset) {
			this.toggleTooltip(false);

			var $question = this.$questions.eq(index);
			var question = this.questions[this.index];
			var questionMode = question.mode;

			if(reset) {
				var inputs = $(this.currentQuestion).find('input:radio, input:checkbox');
				inputs.attr('checked', false);
			}
			
			this.$questions.hide();
			this.currentQuestion = $question[0];
			$question.show();

			var group = parseInt(question.group, 10);
			var total = parseInt(this.groups, 10);
			
			this.view.setPage(group, total);
			this.view.setState(questionMode);
		},

		advise:function(){
			var pages = parseInt(this.groups, 10);
			this.view.setPage(pages + 1, pages);
			this.view.setState('advice');
		},

		submit:function() {
			this.form.submit();
		},

		reset:function() {
			this.index = -1;
			this.next();
			this.form[0].reset();
			this.inputData.clear();
			this.adviceData.clear();
			this.selected.clear();
			this.view.reset();
			this.view.displaySummary(this.selected);
		},

		start:function() {
			this.reset();
			this.index = -1;
			this.next();
		//	this.view.setState('question');
			this.setQuestion(0);
		}
	};

	QuestionManager.getUID = function() {
		if(!this._uid) { this._uid = 1; }
		return 'fbto-uid-' + (this._uid ++);
	};

	/**
	 * Question
	 */
	function Question(node) {
		this.answers = [];
		this.parseQuestion(node);
		this.uid = QuestionManager.getUID();
	}

	Question.prototype = {
		parseQuestion:function(node) {
			var $node = $(node);
			var $options = $node.find('option');
			var l = $options.length;
			
			this.filter = $node.attr('for');
			this.exclude = $node.attr('not');
			this.group = $node.attr('group');
			this.mode = $node.attr('mode') || 'question';
			this.label = $node.find('question');
			
			for(var i=0; i<l; i++) {
				this.answers.push(
					new Answer($options[i])	
				);
			}
		},

		getLabel:function() {
			return this.label;
		},

		getAnswers:function() {
			return this.answers;
		},
		
		getUID:function() {
			return this.uid;
		}
	};

	/**
	 * Answer
	 */
	function Answer(node) {
		this.parseAnswer(node);
		this.uid = QuestionManager.getUID();
	}

	Answer.prototype = {
		parseAnswer:function(node) {
			var $node = $(node);
			this.label = $node.find('answer');
			this.advice = $node.find('advice');
		},

		getLabel:function() {
			return this.label;
		},

		getAdvice:function() {
			return this.advice;
		},
		
		getUID:function() {
			return this.uid;
		}
	};

	
	/**
	 * Question view
	 */
	function QuestionView(settings) {
		this.$container = $(settings.container);
		this.$buttons = $(settings.buttons);
		this.$pages = $(settings.pages);

		this.$intro = $(settings.intro);
		this.$questions = $(settings.questions);
		this.$answers = $(settings.answers);
		this.$selected = $(settings.selected);
		this.$advice = $(settings.advice);

		this.tips = new LBi.HashMap();
	}

	QuestionView.prototype = {
		renderQuestions:function(questions) {
			var html = [];
			var l = questions.length;
			for(var i=0; i<l; i++) {
				html.push(this.renderQuestion(questions[i]));
			}

			return html.join('');
		},


		renderQuestion:function(question) {
			var label = question.getLabel();
			var uid = question.getUID();
			var html = [
				'<div class="qm-question" id="', uid, '">', 
					label.text(), 
					'<ul class="qm-options">'
			];
			
			var answers = question.getAnswers();
			var l = answers.length;
			for (var i=0; i<l; i++) {
				html.push(this.renderAnswer(answers[i], question.mode));
			}

			html.push(
					'</ul>',
				'</div>'
			);

			return html.join('');
		},

		renderAnswer:function(answer, mode) {
			var label = answer.getLabel();
			var type = label.attr('type');
			var name = label.attr('name');
			var value = label.attr('value');
			var cls = label.attr('class');
			var text = label.text();
			var uid = answer.getUID();

			this.tips.put(uid, answer.getAdvice());

			var answerHTML = [
				'<input type="', type, '" name="', name, '" value="', value, '" id="', uid, '" /> '				
			];

			switch (mode) {
				case 'triggered':
					answerHTML.push('<a href="#', uid, '" class="',cls,'" rel="qm-choose"><span>', text, '</span></a>');
				break;
				case 'question':
				default:
					answerHTML.push('<label for="', uid, '" class="',cls,'">', text, '</label>');
				break;
			}

			return [
				'<li class="qm-answer ', mode, '">',
					answerHTML.join(''),					
				'</li>'
			].join('');
		},

		renderAdvice:function(advice) {
			return [
				'<div class="qm-advice">', 
					advice.text(), 
				'</div>'
			].join('');
		},

		displaySummary:function(data) {
			var keys = data.getKeys();
			var html = [];

			if(keys.length == 0) {
				html.push('<p>Nog geen tips beschikbaar.</p>');
			} else {
				html.push(
					'<h2>Basisverzekering</h2><ul>'
				);

				var ext = false;
				var titles = [];

				for(var i=0; i<keys.length; i++) {
					var key = keys[i];
					var header = data.get(key); 
					var title = header.text();

					if(title in titles) {
						continue;
					}

					titles[title] = true;

					if((i >= 1) && title && !ext) {
						ext = true;
						html.push('</ul><h2>Aanvullende modules</h2><ul class="checks">')
					}

					if(title) {
						html.push('<li class="'+ header[0].className +'">', title, '</li>');
					}
				}

				html.push('</ul>');
			}

			this.$selected.html(html.join(''));
		},

		setButtonState:function(state) {
			if(this.currentButtonState) {
				this.$buttons.removeClass(this.currentButtonState);
			}
			this.$buttons.addClass(state);
			this.currentButtonState = state;
		},

		setState:function(state) {
			if(this.currentState) {
				this.$container.removeClass(this.currentState);
			}

			this.$container.addClass(state);
			this.currentState = state;

			var buttonState = state;
			var page = this.currentPage;
			var pages = this.totalPages;
			
			this.$questions.hide();
			this.$answers.hide();
			this.$advice.hide();
			this.$intro.hide();

			this.$buttons.show();
			this.$selected.show();

			switch (state) {
				case 'question':
				case 'triggered':
					this.$questions.show();
					if(page == 1) {
						buttonState = 'first';
					}
				break;
				case 'answer':
					this.$answers.show();
				/*	if(page == pages) {
						buttonState += ' last';
					}
				*/
				break;
				case 'intro':
					this.$buttons.hide();
					this.$selected.hide();
					this.$intro.show();
					buttonState = 'intro';
				break;
				case 'advice':
					this.$advice.show();
				break;
			}
			
			this.setButtonState(buttonState);
		},

		setPage:function(page, total) {
			this.currentPage = page;
			this.totalPages = total;
			
			if(this.lastPage) {
				this.$pages.removeClass(this.lastPage);
			}

			this.lastPage = 'page-'+page;
			this.$pages.addClass(this.lastPage);

			this.$pages.find('span.state').html(
				'Vraag ' + Math.min(page, total) + ' van ' + total	
			);

			this.setPercentage((page-1)/total);
		},

		setPercentage:function(percentage) {
			this.percentage = Math.round(percentage * 10)/10;
			var width = this.percentage * 471;
			this.$pages.find('span.result span.bar').css({
				width: width + 'px'
			});

			this.$pages.find('span.result span.value').html(
				parseInt(this.percentage * 100, 10) + '%'
			);
		},

		getAdvice:function(uid) {
			return this.tips.get(uid);
		},

		reset:function() {
			this.$selected.empty();
			this.setState('intro');
		}
	};


	/**
	 * Validator
	 */
	function Validator() {
		LBi.Dispatcher.subscribe('validate', this.check.bind(this));
	}

	Validator.prototype = {
		check:function(e){
			var $radios = $(e.target).find('input:radio, input:checkbox');
			if($radios.length == 0) {
				return true;
			}

			var $checked = $radios.filter(':checked');
			if($checked.length == 0) {
				e.preventDefault();
			}
		}
	};

	/**
	 * init
	 */
	$(function(){
		KeuzeHulpZorg.initialize();
	});

})(jQuery);