/*
 * 条件选择输入组件
 */
Fangbole.Select = Class.create({
	initialize: function(data){
		this.initData(data);
		
	},
	initData: function(data){
		
		if(Object.isString(data)){
			this.dataPath = data;
			try{
				data = eval(data) || [];
			}catch(e){data = []}
		}

		//TODO: implement Class Fangbole.data
		//if(!Fangbole.DB.check(data, Fangbole.Select.schema))
		//	throw 'invalid data format @ Fangbole.Select::initialize';
		this.data = data.clone();		
		
		//TODO: 
		if(this.options){
			if(this.options.hasNullOption !== false){
				var nullOption = Object.isArray(this.options.hasNullOption) ? this.options.hasNullOption : Fangbole.DB.option.NULL[0];
				this.data.unshift(nullOption);
			}
			
			if(this.options.hasAllOption !== false){
				var allOption = Object.isArray(this.options.hasAllOption) ? this.options.hasAllOption : Fangbole.DB.option.ALL[0];
				this.data.unshift(allOption);
			}
		}
		

		
		//format
		//TODO: implement in SourceData->Data
		if(!Object.isArray(this.data[0])){
			this.data = this.data.collect(function(d, i){
				return [d, i];
			})
		}
	},
	select: function(){
	
	},
	getValue: function(){
		
	},
	update: function(data){
		if(data)
			this.initData(data);
		this.element.update();
		
		this.data.each(function(d, i){
			d[2] = this.createItem(d, i);		
			this.element.insert(d[2])
		}, this)
	},
	render: function(element, position){
		element = element || this.options.submitElement || null;
		if(!element){
			throw 'render element must be specify';
		}
		
		if(element === this.options.submitElement)
			position = 'after';
		else
			position = position || 'bottom';
		var insertion = {};
		insertion[position] = this.element;
		$(element).insert(insertion);
		this.afterRender();
		return this;
	}
})

Object.extend(Fangbole.Select, {
	getRelationPathByOptionPath: function(optionPath){
		return optionPath.replace('option', 'optionRelationship');
	}
})

Fangbole.Selector = Class.create(Fangbole.Select, {
	initialize: function($super, data, options){
		this.options = Object.extend({
			submitElement: null,//structs表单回填字段，只针对java structs，下同
			id: '',
			className: 'g_sel',
			name: '',
			relative: null,
			relationship: null,
			hasNullOption: false,
			hasAllOption: false,
			onSelect: Prototype.emptyFunction
		}, options || {})
		
		$super(data);
			
		/*if(this.options.hasNullOption !== false){
			var nullOption = Object.isArray(this.options.hasNullOption) ? this.options.hasNullOption : Fangbole.DB.option.NULL[0];
			this.data.unshift(nullOption);
		}
		
		if(this.options.hasAllOption !== false){
			var allOption = Object.isArray(this.options.hasAllOption) ? this.options.hasAllOption : Fangbole.DB.option.ALL[0];
			this.data.unshift(allOption);
		}*/
		
		if(this.options.relative)
			this.relative = Object.isArray(this.options.relative) ? this.options.relative : [this.options.relative];
		if(this.options.submitElement){
			this.elSubmit = $(this.options.submitElement);
			this.initValue = $F(this.elSubmit);
		}
			
		this.element = new Element('select', {id: this.options.id, className: this.options.className, name: this.options.name});
		this.element.observe('change', this.onChange.bindAsEventListener(this));
		this.update();
		Fangbole.Selector.instance.push(this);
	},
	onChange: function(){
		this._setSubmitValue();
		if(!this.options.relative)return;
		this.relative.each(function(sel){
			var relationPath;
			if(this.options.relationship && Object.isFunction(this.options.relationship)){
				sel.update(this.options.relationship());
			}else if(this.dataPath){
				
				var addedItem = 0;
				if(this.options.hasNullOption)addedItem ++;
				if(this.options.hasAllOption)addedItem ++;
				var relationship = Fangbole.Select.getRelationPathByOptionPath(this.dataPath) + '[' + (this.element.selectedIndex - addedItem) + ']';
				if(relationship){
					if(Prototype.Browser.IE){
						//bug, refresh select
						//WARNING: if select has been referenced, refresh reference also
						sel.element.update();
						var element = sel.element.clone();
						sel.element.replace(element);
						sel.element = element;
						sel.element.observe('change', sel.onChange.bindAsEventListener(sel));
					}
					sel.update(relationship);
				}
			}
		}, this)
	},
	setRelationship: function(relationship){
		if(relationship)
			this.options.ralationship = relationship;
	},
	createItem: function(d, i){
		return new Element('option', {
			value: d[1]
		}).update(d[0]);
	},
	select: function(value){
		var result = false;
		for(var i = 0, j = this.data.length; i < j; ++ i){
			if(this.data[i][1] == value){
				if(this.data[i][2]){
					this.data[i][2].setAttribute('selected', 'selected');
					result = true
				}
				break;
			}
		}
		
		if(result){
			this.element.fireNative('change');
			if(this.options.onSelect != Prototype.emptyFunction)
				this.options.onSelect(value);
		}
	},
	setValue: function(value, isView){
		if(isView){
			var found = this.data.find(function(d){return d[0] == value});
			if(!found)return;
			this.select(found[1]);
		}else{
			this.select(value);
		}
	},
	getValue: function(isView){
		var val = this.element.getValue();
		if(!isView)
			return val;
		else{
			var found = this.data.find(function(d){return d[1] == val});
			return found ? found[0] : null;
		}
	},
	_setSubmitValue: function(value){
		if(!this.elSubmit)return;
		value = value || this.getValue();
		this.elSubmit.value = value;
	},
	_getSubmitValue: function(value){
		var selectedValue;
		if(!this.elSubmit || $F(this.elSubmit).empty())return;
		try{
			selectedValue = value || $F(this.elSubmit); 
		}catch(e){
			selectedValue = 0;
		}
		this.select(selectedValue);
	},
	afterRender: function(){
		if(this.elSubmit){
			this._getSubmitValue();
			//this._setSubmitValue();
			if(this.relative){
				this.relative.each(function(sel){
					sel._getSubmitValue(sel.initValue);
					//sel._setSubmitValue();
				})
			}
			
		}
		
	},
	focus: function(){
		this.element.focus();
	},
	setRelative: function(relative){
		this.relative = Object.isArray(relative ? relative : [relative]);
	}
})

Object.extend(Fangbole.Selector, {
	instance: [],
	getIdentify: function(){
		return 'FBL_Selector_' + this.instance.length; 
	}
})

Fangbole.Checkbox = Class.create(Fangbole.Select, {
	initialize: function($super, data, options){
		$super(data);
		
		this.dataSchema = 'array';
		
		this.options = Object.extend({
			name: '',
			submitElement: null,
			wrapClassName: 'g_chk_group',
			className: 'g_chk',
			id: '',
			onSelect: Prototype.emptyFunction,
			onReject: Prototype.emptyFunction
		}, options || {})
		if(this.options.submitElement)
			this.elSubmit = $(this.options.submitElement);		
		this.element = new Element('div', {id: this.options.id, className: this.options.wrapClassName});
		this.element.observe('click', this.onClick.bindAsEventListener(this));
		this.update();
		Fangbole.Checkbox.instance.push(this);
	},
	onClick: function(e){
		var elt = e.element();
		if(elt.tagName.toUpperCase() == 'INPUT'){
			elt.checked ? this.options.onReject(elt.value) : this.options.onSelect(elt.value);
			this._setSubmitValue(Object.toJSON(this.getValue()));
		}
	},
	update: function(data){
		if(data)
			this.initData(data);
		this.element.update();	
		this.data.each(function(d, i){
			var label = this.createItem(d, i);
			this.data[i][2] = label.down('input');
			this.element.insert(label);
		}, this)
	},
	createItem: function(d, i){
		return new Element('label').update(Fangbole.Checkbox.template.evaluate({
			className: this.options.className,
			name: this.options.name || Fangbole.Checkbox.getIdentify(),
			value: d[1],
			label: d[0]
		}));
	},
	select: function(value){
		var selectedIndex = 0;
		for(var i = 0, j = this.data.length; i < j; ++ i){
			if(this.data[i][1] == value){
				selectedIndex = i;
				break;
			}
		}
		if(!this.data[selectedIndex][2].checked)
			this.data[selectedIndex][2].click();
	},
	reject: function(value){
		var selectedIndex = 0;
		for(var i = 0, j = this.data.length; i < j; ++ i){
			if(this.data[i][1] == value){
				selectedIndex = i;
				break;
			}
		}
		if(this.data[selectedIndex][2].checked)
			this.data[selectedIndex][2].click();
	},
	getValue: function(isView){
		if(!isView){
			var result = this.element.select('input').collect(function(item){return item.checked ? $F(item) : null}).compact();
		}else{
			var result = this.element.select('input').collect(function(item){return item.checked ? item.nextSibling.nodeValue : null}).compact();
		}
		return Object.isArray(result) ? result : [result];
	},
	setValue: function(value, isView){
		value = Object.isArray(value) ? value : [value];
		if(isView){
			var result = [];
			for(var i = 0, j = value.length; i < j; ++ i){
				for(var m = 0, n = this.data.length; m < n; ++ m){
					if((value[i] == this.data[m][0]) && !this.data[m][2].checked){
						this.data[m][2].checked = true;
						break;
					}
				}
			}
			
			value = result;
			
		}else{
			value.each(function(v){this.select(v)}, this)
			
		}
	},
	_setSubmitValue: function(value){
		if(!this.elSubmit)return;
		value = value || this.getValue();
		this.elSubmit.value = value;
	},
	_getSubmitValue: function(){
		var selectedValue;
		if(!this.elSubmit || $F(this.elSubmit).empty())return;
		try{
			var value = $F(this.elSubmit);
			selectedValue = value.indexOf('[') > -1 ? value.evalJSON() : [value];
		}catch(e){
			selectedValue = [];
		}
		selectedValue.each(function(v){
			this.select(v);
		}, this);
	},
	afterRender: function(){
		if(this.elSubmit){
			this._getSubmitValue();
		}
		//this._setSubmitValue();
	},
	clear: function(){
		this.data.each(function(d){
			d[2].checked = false;
		})
	}
})

Object.extend(Fangbole.Checkbox, {
	template: new Template('<input type="checkbox" class="#{className}" name="#{name}" value="#{value}" />#{label}'),
	instance: [],
	getIdentify: function(){
		return 'FBL_Checkbox_' + this.instance.length; 
	}
})


Fangbole.Radio = Class.create(Fangbole.Select, {
	initialize: function($super, data, options){
		$super(data);
		this.options = Object.extend({
			name: '',
			submitElement: null,
			wrapClassName: 'g_rad_group',
			className: 'g_rad',
			id: '',
			//selectedIndex:"",
			rejectable: false,
			onSelect: Prototype.emptyFunction,
			onReject: Prototype.emptyFunction
		}, options || {})
		if(this.options.submitElement)
			this.elSubmit = $(this.options.submitElement);		
		this.element = new Element('div', {id: this.options.id, className: this.options.wrapClassName});
		this.element.observe('click', this.onClick.bindAsEventListener(this));
		this.update();
		Fangbole.Radio.instance.push(this);
	},
	onClick: function(e){
		var elt = e.element();
		if(elt.tagName.toUpperCase() == 'INPUT'){
			if(this.options.rejectable && this.find(elt.value)[3])
				this.reject(elt.value);
			else
				this.select(elt.value);
			this._setSubmitValue();
		}
	},
	update: function(data){
		if(data)
			this.initData(data);
		this.element.update();	
		this.data.each(function(d, i){
			var label = this.createItem(d, i);
			this.data[i][2] = label.down('input');
			//if(this.options.selectedIndex==i)
				//this.data[i][2].checked="checked";
			this.data[i][3] = false;
			this.element.insert(label);
		}, this)
	},
	createItem: function(d, i){
		return new Element('label').update(Fangbole.Radio.template.evaluate({
			className: this.options.className,
			name: this.options.name || Fangbole.Radio.getIdentify(),
			value: d[1],
			label: d[0]
		}));
	},
	find: function(value){
		for(var i = 0, j = this.data.length; i < j; ++ i){
			if(this.data[i][1] == value){
				return this.data[i]
				break;
			}
		}
		return null;
	},
	select: function(value){
		var d = this.find(value);
		if(d){
			this.data.each(function(item){item[3] = false;})
			if(!d[3]){
				d[3] = true;
				d[2].checked = true;
			}
			this.options.onSelect(value);
		}else
			throw 'no such element exist @ Fangbole.Radio::select';
	},
	reject: function(value){
		var d = this.find(value);
		if(d){
			if(d[3]){
				d[2].checked = false;
				d[3] = false;
			}
			this.options.onReject(value);
		}else
			throw 'no such element exist @ Fangbole.Radio::select';
	},
	getValue: function(){
		var checked = this.element.select('input').find(function(item){return item.checked})
		return checked ? checked.value : null;
	},
	_setSubmitValue: function(value){
		if(!this.elSubmit)return;
		value = value || this.getValue();
		this.elSubmit.value = value;
	},
	_getSubmitValue: function(){
		var selectedValue;
		if(!this.elSubmit || $F(this.elSubmit).empty())return;
		try{
			var value = $F(this.elSubmit);
			selectedValue = value.indexOf('[') > -1 ? value.evalJSON()[0] : value;
		}catch(e){
			selectedValue = 0;
		}
		this.select(selectedValue);
	},
	afterRender: function(){
		if(this.elSubmit){
			this._getSubmitValue();
		}
		//this._setSubmitValue();
	}
})

Object.extend(Fangbole.Radio, {
	template: new Template('<input type="radio" class="#{className}" name="#{name}" value="#{value}" />#{label}'),
	instance: [],
	getIdentify: function(){
		return 'FBL_Radio_' + this.instance.length; 
	}
})


