/**
 * Creates sets of html blocks given a datasource and a limit.
 * Each block is a class with its own rules, markup and logic.
 * 
 * @author Angelo Selvini <angelo.selvini@exmachina.ch>
 */


if(!ch){
	var ch = {};
}
if(!ch.exm){
	ch.exm = {};
}
if(!ch.exm.widgets){
	ch.exm.widgets = {};
}
if(!ch.exm.widgets.controls){
	ch.exm.widgets.controls = {};
}
;(function(){
	if(!ch.exm.widgets.controls.blocks){
		ch.exm.widgets.controls.blocks  = {};
	}

	function genNumericSelect(/* object */ p){
			var i,si,
				o = "",
				l = p.limit,
				n = p.name,
				m = p.className || "",
				x = p.prefix    || "",
				k = (!isNaN(p.selected)) ? p.selected : null,
				z = p.zero      || false,
				f = p.hasOwnProperty("novalue") ? p.novalue : "-?-",
				d = document.createElement("div"),
				sel =  ' selected="selected"'
			;

			for (i=z?0:1;i<=l;i++){
				o += '<option value="'+i+'"'+ ( k==i?sel:"" ) +'>'+i+'</option>';
			}
			if (f){
				o = '<option value=""'+ (k==null?sel:"")+'>'+f+'</option>'+o;
			}

			if (m!="") { m = 'class="'+m+'"'; }
			d.innerHTML = '<select '+m+' name="'+x+n+'" >'+o+"</select>";
			return d.firstChild;
	}

	function setSelectByValue( d,v ){
		var i,l=d.length;
		for (i=0;i<l;i++){
			if (d[i].value == v){
				d.selectedIndex = i;
				break;
			}
		}
	}

	ch.exm.widgets.controls.blocks.AdultsChildren = Class.create({
		limit : 5,
		errors: null,
		labels: null,
		target: null,
		ctrlPrefix: null,
		template: null,
		domNodes: {},
		markup  : "<div class='ht_bc_title'>#{title}</div>"
				+ "<div class='ht_bc_summary'>"
					+ "<div class='ht_bc_adults'>#{adults} <span class='comboAdults'><!-- js --></span></div>"
					+ "<div class='ht_bc_children'>#{children} <span class='comboChildren'><!-- js --></span></div>"
				+ "</div>"
				+ "<div class='ht_bc_childrenYears'>"
					+ "<div class='ht_bc_group'><span class='labelChildrenYears'>#{childrenYears}</span><span class='comboChildrenYears'><!-- js --></span></div>"
				+ "</div>"
				+ "<div class='ht_clear'><!-- clear --></div>",
		block: null,
		inspector: null,

		datasource: null,

		initialize: function(ctorArgs){
			this.limit  = 5;
			this.errors = {};
			this.target = null;
			this.labels = null;
			this.ctrlPrefix = "";
			this.datasource = 
				{ adults       : null
				, children     : null
				, childrenYears: []
				};
			this.inspector = null;
			this.domNodes =
			{ adults: null
			, children: null
			, childrenYears: []
			, comboAdults: null
			, comboChildren: null
			, comboChildrenYears: null
			, error: null
			};

			if (ctorArgs){
				if (ctorArgs.data) {
					var ds = this.datasource,
						cd = ctorArgs.data
					;
					ds.adults   = cd.adults   || null;
					ds.children = cd.children || null;
					ds.childrenYears =cd.childrenYears || [];
				}
				ctorArgs.target      && (this.target     = ctorArgs.target     );
				ctorArgs.limit       && (this.limit      = ctorArgs.limit      );
				ctorArgs.labels      && (this.labels     = ctorArgs.labels     );
				ctorArgs.errors      && (this.errors     = ctorArgs.errors     );
				ctorArgs.ctrlPrefix  && (this.ctrlPrefix = ctorArgs.ctrlPrefix );
			}

			this.template = new Template(this.markup);
			this._build();

			ctorArgs = null;
		},

		_build: function(){
			//	summary: create controls
			var i,
				C  = this.getConstants(),
				d  = this.domNodes,
				ds = this.datasource
			;
			d.adults   = genNumericSelect({prefix: this.ctrlPrefix, name:"adults"  ,limit: 4, selected: ds.adults   || 2  , zero: false, novalue: null });
			d.children = genNumericSelect({prefix: this.ctrlPrefix, name:"children",limit: 3, selected: ds.children || 0, zero: true, novalue: null  });

			if (!d.error){
				d.error    = $(document.createElement("div"));
				d.error.addClassName(C.CL_ERROR_MSG);
			}

			this._buildChildrenYears();
			this.bindEvtHandlers();
		},

		_buildChildrenYears: function(){
			var ds = this.datasource,
				cy = ds.childrenYears || [],
				cl = ds.children      || 0,
				d  = this.domNodes
			;

			d.childrenYears = [];
			for (i=0;i<cl;i++){

				var c = ((i+1)%3==0)?"ht_bc_cy_last":"",
					html = genNumericSelect({prefix: this.ctrlPrefix+"guests["+i+"].", name:"age" , limit: 12, selected: cy[i], zero: true, className:c, novalue: null });

				d.childrenYears.push( html );
			}
		},

		render: function(){
			var i,
				d = $( document.createElement("div") ),
				n = this.domNodes,
				l = this.labels || {}
			;
			d.addClassName("ht_bc_block");
			d.innerHTML = this.template.evaluate(l);
			this.block = d;

			// Get placeholder from template
			n.comboAdults        = d.select(".comboAdults")[0];
			n.comboChildren      = d.select(".comboChildren")[0];
			n.comboChildrenYears = d.select(".comboChildrenYears")[0];

			// Attach nodes
			if (n.comboAdults)   { n.comboAdults.appendChild( n.adults );  }
			if (n.comboChildren) { n.comboChildren.appendChild( n.children ); }
			this._renderChildrenYears();
			return d;
		},
		_renderChildrenYears: function(){
			var n   = this.domNodes,
				d   = this.block,
				ds  = this.datasource,
				c   = ds.children,
				cy  = ds.childrenYears,
				cyl = n.childrenYears.length,
				ccn = n.comboChildrenYears,
				cb  = $( d.select(".ht_bc_childrenYears")[0] );
			;

			// if there are no childrens hide the layer
			if (!c||c<1){
				cb.hide();
				return;
			} else {
				cb.show();
			}

			if (ccn){
				for (i=0;i<c;i++){
					var dd;
					if (n.childrenYears[i]){
						dd = n.childrenYears[i];
						if (cy && cy[i] && !isNaN(cy[i])){
							setSelectByValue(dd, cy[i]);
						}
						ccn.appendChild(dd);
					}
				}
			}

		},


		bindEvtHandlers: function(){
			var i,
				d  = this.domNodes,
				dc = d.childrenYears
			;
			Event.observe(d.children, "change", this._childrenChange.bindAsEventListener(this) );
			Event.observe(d.adults  , "change", this._adultsChange.bindAsEventListener(this) );

			for (i=0;i<dc.length;i++){
				Event.observe(dc[i], "change", this._childrenYearChange.bindAsEventListener(this,i) );
			}
		},
		unbindEvtHandlers: function(){
			var d = this.domNodes
			;
			Event.stopObserving(d.children,"change");
			Event.stopObserving(d.adults,"change");
		},
		_adultsChange: function(evt){
			var n = this.domNodes
			;
			if (!this.validate()){
				// n.adults.selectedIndex = 1;
				this._showLimitError(true);
			} else {
				this._showLimitError(false);
			}
			this.inspector();
		},
		_childrenChange: function(evt){
			var n = this.domNodes,
				c = n.comboChildrenYears,
				L = this.limit,
				t = evt.target
			;
			if (c){
				c.innerHTML = "";
			}

			if (!this.validate()){
				//n.children.selectedIndex = 1;
				this._showLimitError(true);
			} else {
				this._showLimitError(false);
			}
			this.datasource.children = t.options[ t.selectedIndex ].value;

			this.unbindEvtHandlers();

			this._buildChildrenYears();
			this._renderChildrenYears();
			this.bindEvtHandlers();

			this.inspector();
		},
		_childrenYearChange: function(evt,i){
			var v,
				ds = this.datasource,
				cy = ds.childrenYears,
				n = this.domNodes,
				c = n.comboChildrenYears,
				t = evt.target
			;

			cy[i]=parseInt(evt.target.value);
		},
		_showLimitError: function(b,m){
			var e  = this.errors,
				n  = this.domNodes,
				ne = n.error,
				d  = $(this.block),
				C  = this.getConstants(),
				c  = C.CL_ERROR_ROW
			;

			if (!d){return;}

			if (b===true){
				d.addClassName(c);
				ne.innerHTML = m|| (e&&e.ac||"Limit reached");
				d.insertBefore(ne, d.firstChild);
			} else if (b===false){
				d.removeClassName(c);
				if (ne.parentNode){
					ne.parentNode.removeChild(ne);
				}
			}
		},

		_showErrorMessage: function(t){},

		validate: function(){
			// summary: max L person total
			var na,nc,
				d = this.domNodes,
				a = d.adults,
				c = d.children,
				L = this.limit,
				r = false
			;

			na = parseInt(a[a.selectedIndex].value) || 0;
			nc = parseInt(c[c.selectedIndex].value) || 0;

			if (na + nc <= L){
				r = true;
			}
			return r;
		},
		getControls: function(){
			var d   = this.domNodes,
				cy  = d.childrenYears,
				cyl = cy ? cy.length : 0,
				cya = []
			;
			for (i=0;i<cyl;i++){
				cya.push(parseInt(cy[i].value));
			}
			return { adults   : parseInt(d.adults.value)
			       , children : parseInt(d.children.value)
			       , childrenYears: cya
			       };
		},
		setInspector: function(c){
			if (c instanceof Function){
				this.inspector = c;
			}
		},

		update: function(){
			this.unbindEvtHandlers();
			this.render();
			this.bindEvtHandlers();
		},
		destroy: function(){
			this.unbindEvtHandlers();

			var c,
				b = this.block,
				d = this.domNodes
			;
			for (c in d){
				if (d[c].parentNode && d[c].parentNode.nodeType == 1){
				 d[c].parentNode.removeChild(d[c]);
				}
			}
			if (b && b.parentNode && b.parentNode.nodeType == 1){
				b.parentNode.removeChild(b);
			}
			b = null;
		},
		getConstants: function(){
			return Object.clone(ch.exm.widgets.controls.blocks.Constants);
		},
		toString: function(){
			return "[ch.exm.widgets.controls.blocks.AdultsChildren]";
		}
	});




	ch.exm.widgets.controls.BlockComposer = Class.create({
/*
	{ blocks:
		[
			{ type: "ac"
			, data: 
				{ adults: 2
				, children: 3
				, childrenYears: [ 1,3,5 ]
				}
			}
		]
	}
*/
		limits    : {ac:5},
		messages  : {ac:null,all:null},
		blocks    : [],
		required  : null,
		showable: 1,

		markup    : '<div class="ht_bc_selector">#{roomSelector} <span class="comboSelector"><!-- js --></span></div>'
				  + '<div class="ht_bc_blocks"><!-- c --></div>'
				  + '<div class="ht_bc_clear"><!-- c --></div>',
		target    : null,
		selector  : null,
		datasource: null,
		dictionary: null,
		template  : null,

		initialize: function(ctorArgs){
			this.showable = 1;
			this.target = null;
			this.blocks = [];
			this.datasource = null;
			this.dictionary = {};

			if(ctorArgs){
				ctorArgs.dictionary && ( this.dictionary = ctorArgs.dictionary );
				ctorArgs.limits     && ( this.limits     = ctorArgs.limits     );
				ctorArgs.showable   && ( this.showable   = ctorArgs.showable   );
				ctorArgs.target     && ( this.target     = ctorArgs.target     );
				ctorArgs.datasource && ( this.datasource = ctorArgs.datasource );
			}

			this._build();
		},

		_build: function(){
			var i,
				D  = this.getDict(),
				l  = this.showable,
				ds = this.datasource,
				L  = this.limits,
				b  = ds ? ds.blocks : null,
				bl = b instanceof Array ? b.length : 0
			;

			// Create each block and initialize
			for (i=0;i<l;i++){

				// Setup block's labels
				var blk,
					lbs =  
					{ adults        : D.adults        || "[adults]"
					, children      : D.children      || "[children]"
					, childrenYears : D.childrenYears || "[childrenYears]"
					, title         : D.room+" "+(i+1)
					, novalue       : D.novalue
					};

				if (b[i]){
					switch(b[i].type){
						case "ac":
								var x = b[i].data;
								blk = new ch.exm.widgets.controls.blocks.AdultsChildren(
									{ data  : b[i].data
									, labels: lbs
									, limit : b[i].limit || (L && L.ac ? L.ac : 0)
									, ctrlPrefix: "roomList["+i+"]."
									, errors : {ac: D.error_ac || "Error ac"}
									});
							break;
						default:
							break;
					}
				} else {
					blk = new ch.exm.widgets.controls.blocks.AdultsChildren(
						{ data  : 
							{ adults: null
							, children: null
							, childrenYears: null
							}
						, labels     : lbs
						, limit      : L && L.ac ? L.ac : 0
						, ctrlPrefix : "roomList["+i+"]."
						, errors     : {ac: D.error_ac || "Error ac"}
						});
				}

				blk.setInspector(this.inspector.bindAsEventListener(this));
				this.blocks.push(blk);
				
			}
			this.template = new Template(this.markup);
			this._updateSelector();

			this.required = parseInt(this.selector.value);
			this.bindEvtHandlers();
		},
		inspector: function(){
			var i,c,
				D  = this.getDict(),
				C  = this.getConstants(),
				r  = this.required,
				b  = this.blocks,
				bl = b.length,
				dl = $(this.domList),
				L  = this.limits,
				a  = L && L.all ? L.all : 0,
				e  = this.errors,
				t  = 0,
				x  = true
			;
			for (i=0;i<r;i++){
				c = b[i].getControls();
				t += c.adults+c.children;
				if (!b[i].validate()) {
					b[i]._showLimitError(true);
					x = false;
				}
			}

			if (t>a){
				for (i=0;i<bl;i++){
					b[i]._showLimitError(false);
				}
				var tf = b[0]._showLimitError;
				if (tf){
					tf.apply(b[0], [true, D.error_overflow||"[Overflow error]"]);
				}
				dl.addClassName(C.CL_ERROR_ALL);
				x = false;
			} else {
				var bl = this.blocks[0];
				if (bl && bl.validate()){
					bl._showLimitError(false)
				}else if (bl && !bl.validate()){
					bl._showLimitError(true)
				}

				dl.removeClassName(C.CL_ERROR_ALL);
			}
			return x;
		},
		_updateSelector: function(){
			/*	summary: create combo to select number of blocks */
			var b  = this.blocks,
				ds = this.datasource,
				s  = this.selector
			;

			// remove the old one
			if (s){
				if (s.parentNode && s.parentNode.nodeType==1){
					s.parentNode.removeChild(s);
				}
			}

			var i,
				o = "",
				l = this.showable,
				c = ds.blocks.length,
				s = genNumericSelect({name:"roomListSize",limit: l, novalue: null, selected: c })
			;
			$(s).addClassName("roomListSize");

			this.selector = s;
		},
		render: function(){
			//	Summary: modify dom
			var i,
				d = $( document.createElement("div") ),
				b = this.blocks,
				t = this.target,
				s = this.selector,
				l = this.domList,
				si = this.required// parseInt( s.value ) //s.options[s.selectedIndex].value )
			;

			if (!t) {return;}

			// Cleanup target
			if (l){ l.parentNode.removeChild(l); }
			if (t.firstChild) { t.removeChild(t.firstChild) };

			// Create dom
			d.addClassName("ht_blockComposer");
			d.innerHTML = this.template.evaluate(this.getDict());
			
			// Attach selector
			if (s){
				var sPos = d.select(".comboSelector")[0];
				if (sPos){
					sPos.appendChild(s);
				}
			}

			// Prepare list
			this.domList = l = document.createElement("OL");

			// Populate list
			for (i=0;i<si;i++){
				if (!b[i]) {continue;}
				var n = b[i].render(),
					c = document.createElement("LI")
				;
				if (i==si-1){$(c).addClassName("ht_bc_last");}
				c.appendChild(n);	// attach block to list element
				l.appendChild(c);	// increment list
			}

			// Apply view filters
			if (si==1){
				// Hide room label 
				$(l).select(".ht_bc_title")[0].setStyle({display:"none"});
			}
			// Attach list
			var lPos = d.select(".ht_bc_blocks")[0];
			if (lPos){
				lPos.appendChild(l);
			}

			// Publish
			t.appendChild(d);
		},

		bindEvtHandlers: function(){
			var s = this.selector
			;
			if (s){
				Event.observe(s, "change", this._selectorChange.bindAsEventListener(this));
			}
		},
		unbindEvtHandlers: function(){
			var s = this.selector
			;
			if (s){
				Event.stopObserving(s, "change");
			}
		},
		_selectorChange: function(evt){
			// update
			this.required = parseInt(evt.target.value);
			this.update();
			this.inspector();
		},
		update: function(/** Object */args){
			// update data
			this.unbindEvtHandlers();
			this.render();
			this.bindEvtHandlers();
		},

		destroy: function(){
			this.unbindEvtHandlers();
			var i,b = this.blocks
			;
			for (i=0;i<b.length;i++){
				b[i].destroy();
			}

		},

		getDict: function(/* string */ w){
			d = this.dictionary;
			return w ? d[w] || 'key n/a' : d;
		},
		getConstants: function(){
			return Object.clone(ch.exm.widgets.controls.blocks.Constants);
		},
		toString: function(){
			return "[ch.exm.widgets.controls.BlockComposer]";
		}

	});
	ch.exm.widgets.controls.blocks.Constants = {
		CL_ERROR_ALL: "ht_bc_error_all",
		CL_ERROR_ROW: "ht_bc_error_row",
		CL_ERROR_MSG: "ht_bc_error_msg"
	};
})();
