/**** /js/doLoad.js ****/
function DependingSelect( select, params ) {
	var th = this;
	
	var invitingOptionValue = 'null'; // TODO: FIX ME
	var otherOptionValue = ''; // TODO: FIX ME
	
	this.select = $( select );
	
	// flag if this select is multiple-choice
	this.multiple = params.multiple;
	this.command = params.command;
	
	// additional handle callback that would be invoked on load
	this.onLoadHandler = params.onLoadHandler;
	
	// save handle to field "other"
    this.otherFields = params.otherField;
    this.otherFieldDefault = params.otherFieldDefault;
    if (this.otherFields) {
        if (!(this.otherFields instanceof Array)) {
            this.otherFields = [ this.otherFields ];
        }
        if (!(this.otherFieldDefault instanceof Array)) {
            this.otherFieldDefault = [ this.otherFieldDefault ];
        }
        for (var i = 0; i < this.otherFields.length; i++) {
            this.otherFields[i] = $(this.otherFields[i]);
            this.otherFields[i].origValue = this.otherFields[i].value;
            if (this.otherFieldDefault && this.otherFieldDefault[i] !== null) {
                setDefaultValueForInput(this.otherFieldDefault[i], this.otherFields[i]);
            }
        } 
    }
    if (params.otherHandler) {
        this.otherHandler = params.otherHandler;
    }

	// cache of this select's options: id => "Option object"
	this.id2option = {};
	
	// relation "parent value" => array of this select's corresponding values
	this.parentCache = {}; 
	
	if ( params.otherWord ) {
	    th.otherOption = new Option( params.otherWord, otherOptionValue );
	    th.otherOption.innerHTML = params.otherWord;
	}

    // this is patch for education. Schools list has different values depending on degree selected.
    this.setCommand = function( newCommand ) {
        th.command = newCommand;

        var parentValue = th.parentSelect.getValue();
        // parent select is single-choice in education, so erasing cache is easy
        if (parentValue[0] == 'value') {
            th.parentCache[parentValue[1]] = null;
            th.parentChoseValue(parentValue[1]);
        }
    };

	// CHILD METHODS
	
	// stop current loading of new data
	this.stopLoading = function() {
		if ( th.req ) {
			th.req.onreadystatechange = null;
			th.req = null;
		}
	};
	
	// set parent select
	this.bindToParent = function( parentSelect ) {
		// save parent select
		th.parentSelect = parentSelect;
		
		// bind parent select to myself
		parentSelect.bindToChild( th );
		
		var parentValue = th.parentSelect.getValue();

		// check what's value of parent
		switch ( parentValue[0] ) {
		// parent's value is some real value or values
		case "value": 
			var value = parentValue[1];
			
			// if parent has only one value selected
			if ( !( value instanceof Array ) ) {
				// put my options to cache
				th.parentCache[value] = [];
				for ( var index = 0; index < th.select.options.length; index++ ) {
					var curOptionValue = th.select.options[index].value;
					
					// ignore inviting option & "other" option
					if ( curOptionValue == invitingOptionValue || curOptionValue == otherOptionValue ) {
						continue;
					}
					
					// cache value
					th.id2option[curOptionValue] = th.select.options[index];
					th.parentCache[value].push( curOptionValue );
				}
			} else {
			// if parent has many values selected, load correspondence between values
				th.loadOptionsForValues( value ); 
			}
			break;
			
		// parent's value is "please choose", repeat it
		case "invite": 
			th.parentChoseInvite( parentValue[1] );
			break;
			
		// parent's value is "other"
		case "other": 
			break;
		}
		
		th.getValue();
		th.detectOtherState();
	};
	
	// parent should call this when "please choose grandparent" was chosen in it
	this.parentChoseInvite = function( invitingOptionName ) { 
		th.enableOther( false );
		th.enableSelect( false );

		// remove all options
		th.select.innerHTML = '';
		/*while ( th.select.firstChild ) {
			th.select.removeChild( th.select.firstChild );
		}*/

		// add option "please choose parent"
		var invitingOption = new Option( invitingOptionName, invitingOptionValue );
    	invitingOption.innerHTML = invitingOptionName; // fix for IE
		th.select.appendChild( invitingOption );
		
		// handle changes
		th.valueChanged();
	};
	
	// parent should call this when 'other' was chosen in it
	this.parentChoseOther = function() { 
		// IMPORTANT: "other field" is enabled, so that one "other field" may be used for several selects
		th.enableOther( true ); 
		th.enableSelect( false );

		// remove all options
		while ( th.select.firstChild ) {
			th.select.removeChild( th.select.firstChild );
		}
		
		// handle changes
		th.valueChanged();
	};
	
	// parent should call this when real value(s) was chosen in it
	this.parentChoseValue = function( values ) { 
		if ( !( values instanceof Array ) ) {
			values = [values];
		}

		th.enableOther( false );
		th.enableSelect( true );

		// memorize selected options
		var selected = {};
		var selectedArray = [];
		var index;
		for ( index = 0; index < th.select.options.length; index++ ) {
			var opt = th.select.options[index];
			if ( opt.selected ) {
				selected[opt.value] = true;
				selectedArray.push( opt.value );
			}
		}

		// detect options to load
		var need2load = [];
		for ( index = 0; index < values.length; index++ ) {
			if ( !th.parentCache[values[index]] ) {
				need2load.push( values[index] );
			}
		}

		if ( window.DIC && DIC.loading_option ) {
    		// remove all options from select
    		while ( th.select.firstChild ) {
    			th.select.removeChild( th.select.firstChild );
    		}
    	    var loadingOption = new Option( DIC.loading_option, '' );
    	    loadingOption.innerHTML = DIC.loading_option;
    	    th.select.appendChild( loadingOption ); 
		}
		
		// load nesessary options, put them to select & restore selection
		th.loadOptionsForValues( need2load, function() { 
			// remove all options from select
			while ( th.select.firstChild ) {
				th.select.removeChild( th.select.firstChild );
			}

			var realOptionsCount = 0;
			var lastRealOption = null;
			// add needed options to select
			for ( var index = 0; index < values.length; index++ ) {
				var optionIDs4value = th.parentCache[values[index]];
				
				// there may be no options for some parent value
				if ( !optionIDs4value ) {
					continue;
				}
				
				for ( var innerIndex = 0; innerIndex < optionIDs4value.length; innerIndex++ ) {
					var optionID = optionIDs4value[innerIndex];
					realOptionsCount++;
					lastRealOption = th.id2option[optionID];
					th.select.appendChild( lastRealOption );
				}
			}
			
			// if there is at least one real option
			if ( realOptionsCount > 0 ) {
			    
			    // if there's only one real option & it should be instantly selected, disable select
			    if ( realOptionsCount == 1 && params.instantSelect ) {
    			    th.enableSelect( false );
    			    if ( params.instantSelect && th.multiple ) {
    			        try { th.select.options[0].selected = true; } catch ( e ) { }
    			    }
			    } else {
			    // else we have choice, so we add "other option" and inviting option

    			    // enable self first
    			    th.enableSelect( true );

        			// add "other option" to select, if possible
        			if ( th.otherOption ) {
        			    th.select.appendChild( th.otherOption );
        			}

        			// if this select is single-choice, add invitingOption to it and select it
        			if ( !th.multiple ) { 
        			    var invitingOption = new Option( params.invitingOptionName, invitingOptionValue );
        			    invitingOption.innerHTML = params.invitingOptionName;
        			    th.select.insertBefore( invitingOption, th.select.options[0] ); 
        			    th.select.value = invitingOptionValue;
        			} else {
        			// else this select is multi-choice & we should restore selection
            			for ( index = 0; index < th.select.options.length; index++ ) {
            				var opt = th.select.options[index];
            				try { 
                				if ( selected[opt.value] ) {
                					opt.selected = true;
                				}
            				} catch ( e2 ) { } // IE has excitingly misterious error here
            			}
        			}
    
			    }
			} else { 
		    // else no real options exist, so add only "other option", but not inviting option
    			if ( th.otherOption ) {
    			    th.select.appendChild( th.otherOption );
    			    th.select.value = otherOptionValue;
    			    
    			    th.enableSelect( false );
    			}
			}
			
			// handle changes in value, if any
			th.valueChanged();
		} );
	};
	
	// this is select onchange handler
	this.valueChanged = function() {
		// stop any loading operation of child
		if ( th.childSelect ) {
			th.childSelect.stopLoading();
		}
		
		// detect type of selected value
		th.getValue();
		// disable otherFields, if needed
	    th.detectOtherState();
		
        // call appropriate method of child, if any
        if ( !th.childSelect ) {
        	return;
        }

	    var value = th.getValue();
	    switch ( value[0] ) {
        case 'value':
		    th.childSelect.parentChoseValue( value[1] );
	        break;
        case 'invite':
		    // detect what invitingOptionName to use
		    var name = params.invitingOptionName;
		    if ( th.parentSelect ) {
		        var parentValue = th.parentSelect.getValue();
		        if ( parentValue[0] == 'invite' ) {
                    name = parentValue[1];
		        }
		    }

            // pass invitingOptionName to child
            th.childSelect.parentChoseInvite( name );
	        break;
        case 'other':
		    th.childSelect.parentChoseOther();
	        break;
	    }
	};
	
	// shortcut function to disable/enable otherFields
	this.enableOther = function ( state ) {
	    if (th.otherHandler) {
	        th.otherHandler( state );
	        return;
	    }
	    if (!th.otherFields) {
	    	return;
	    }

        var func_enabler = (function(elem){
            elem.enable();
        });

        var func_disabler = (function(elem){
            elem.disable();
            elem.value = '';
        });

        for (var i = 0; i < th.otherFields.length; i++) {
            var otherField = th.otherFields[i];
            otherField.className = otherField.className.replace( /\bhidden\b/i, '' );
            if (state) {
                otherField.disabled = false;
                otherField.select('input').each(func_enabler);
            } else {
                otherField.className += ' hidden';
                otherField.disabled = true;
                otherField.select('input').each(func_disabler);
                if (!otherField.nodeName.match(/select/i)) {
                    otherField.value = params.otherFieldDefault instanceof Array
                        ? (params.otherFieldDefault[i] !== null? params.otherFieldDefault[i] : '') 
                        : params.otherFieldDefault;
                }
            }
	    }
	};
	
	// shortcut function to disable/enable select
	this.enableSelect = function ( state ) {
        th.select.className = th.select.className.replace( /\bdisabled\b/i, '' );
	    if ( state ) {
	        th.select.disabled = false;
	    } else {
	        th.select.className += ' disabled';
	        th.select.disabled = true;
	    }
	};
	
	// shortcut function to disable otherFields if needed 
	this.detectOtherState = function() {
	    var value = th.getValue();
	    switch (value[0]) {
        case 'value':
    		th.enableOther(false);
	        break;
        case 'invite':
      		th.enableOther(false);
	        break;
        case 'other':
       		th.enableOther(true);
       		if (th.otherFields) {
       			try {
       				th.otherFields[0].focus();
				} catch(e) {}       				
       		}
	        break;
	    }
	};
	
	// load data for values, put them in cache, and optionally call function after that
	this.loadOptionsForValues = function( values, onloadHandler ) { 
	    // check do we really need to load anything?
	    if ( values.length === 0 ) {
	        if ( onloadHandler ) {
	        	onloadHandler();
	        }
	        if ( th.onLoadHandler ) {
	        	th.onLoadHandler();
	        }
	        return;
	    }
	    
		var req = new JsHttpRequest();
		th.req = req;
		req.onreadystatechange = function() {
            if ( req.readyState != 4 ) {
            	return;
            }
            if ( !req.responseJS ) {
            	return;
            }
            
            if (req.responseJS.errorMessage) {
                alert(req.responseJS.errorMessage);
                return;
            }
            
            for ( var k in req.responseJS.result ) {
                if (req.responseJS.result.hasOwnProperty(k)) {
	    			var value = req.responseJS.result[k];
	    			if ( value instanceof Function ) {
	    				continue;
	    			}
	
	    			th.id2option[value.id] = new Option( value[params.language], value.id );
	    			th.id2option[value.id].innerHTML = value[params.language]; // fix for IE
	    			
	    			if ( !th.parentCache[value.parent_id] ) {
	    			    th.parentCache[value.parent_id] = [];
	    			}
	    			th.parentCache[value.parent_id].push( value.id );
                }	
            }
            
            if ( onloadHandler ) {
            	onloadHandler();
            }
            if ( th.onLoadHandler ) {
            	th.onLoadHandler();
            }
		};
		req.caching = true;
		
		// if command has a single double slash - consider that it provides full url, else - path part only
		var url = (th.command.split('//').length == 2)  ?  th.command : window.location.protocol + '//' + window.location.host + th.command;
		
		req.open( 'get', url, true );
		req.send( { 'q': values.join( ',' ) } );
	};
	
	// PARENT METHODS
	
	// return what's my type of value and some additional params
	this.getValue = function() { 
		// if i'm single-choice
		if ( !th.multiple ) {

			// if i'm inviting user to choose parent
			if ( th.select.value == invitingOptionValue ) { 
				th.valueType = "invite";
				return ['invite', params.invitingOptionName];
			}
			
			// if my value is "other"
			if ( (th.select.value === "") || (th.select.value == "choose") ) {
				th.valueType = "other";
				return ['other'];
			}

			// if I have real value, send it to my child
			th.valueType = "value";
			return ['value', th.select.value];
		} else {
		// if i'm multiple-choice
			// collect selected values
			var values = [];
			for ( var index = 0; index < th.select.options.length; index++ ) {
				if ( th.select.options[index].selected ) {
					values.push( th.select.options[index].value );
				}
			}
		
			// detect self valueType
			if ( values.length === 0 ) {
    			th.valueType = "invite";
    			return ['invite', params.invitingOptionName];
			} else {
    			th.valueType = "value";
    			return ['value', values];
			}
		}
	};
	
	// save handle to my shild
	this.bindToChild = function( child ) { 
		th.childSelect = child;
	};
	
	// anti-FF hack: select only options that are selected in html
	for (var index = 0; index < th.select.options.length; index++) {
	    th.select.options[index].selected = th.select.options[index].getAttribute('selected');
	}
	
	
	th.getValue();
    th.detectOtherState();
	
	Event.observe( this.select, 'change', this.valueChanged );
}
;
