/**
 * DependentSelect implements a common pattern of having a select tag depending
 * on another input, usually another select.
 *
 * A good example is a select of states depending on a select of countries.
 * 
 * There're two implementations for that:
 *  DependentSelect.Local -> Will look for data in a local prefetched variable.
 *  DependentSelect.Ajax  -> Will perform an AJAX request to fetch the data.
 */
var DependentSelect = {};
DependentSelect.Base = function() {};
DependentSelect.Base.prototype = {
  baseInitialize: function(select_id, dependency_id, options) {
                    this.select     = $(select_id);
                    this.dependency = $(dependency_id);
                    this.options    = options || {
                      prompt: "Please select..."
                    };

                    this.setupObserver();

                    if (!this.__initialized && this.select.value == "")
                      this.clear();
                  },

  setupObserver: function() {
                   this.dependency.observe('change', this.handleDependencyChange.bindAsEventListener(this));
                 },

  handleDependencyChange: function() {
                            this.clear();
                            this.updateOptions(this.dependency.value);
                          },

  clear: function() {
           if (!this.select.disabled)
             this.select.disabled = true;

           this.select.innerHTML = "";

           if (this.options.prompt)
             this.select.appendChild(Builder.node('option', {selected:'selected'}, this.options.prompt));

           this.__initialized = true;
         },

  show: function() {
          this.select.selectedIndex = 0;
          this.select.disabled     = false;
          this.select.focus();
        },

  populateSelect: function(options) {
                    if (options == undefined || options == null)
                      return false;

                    this.clear();

                    $A(options).each(function(pair) {
                      value = pair[0];
                      text  = pair[1];
                      node = Builder.node('option', {'value':value}, text);
                      this.select.appendChild(node);
                    }.bind(this));

                    this.show();
                  }
};

/**
 * Implement a DependentSelect that will look for the data in a prefetched local variable
 *
 * Example
 *  var data = $H({ 
 *    1: [[1,'California'],[2,'Florida']],
 *    2: [[3,'Rio de Janeiro'],[4,'São Paulo']]
 *  })
 *  new DependentSelect.Local('state_id', 'country_id', data)
 *
 * Once the element identifier by country_id has changed, it will update the state_id options
 * using the data in hash. It will use the country_id selected element's value to lookup the hash.
 */
DependentSelect.Local = Class.create();
Object.extend(Object.extend(DependentSelect.Local.prototype, DependentSelect.Base.prototype), {
  initialize: function(select_id, dependency_id, items, options) {
    this.baseInitialize(select_id, dependency_id, options);
    this.items = items;
  },

  updateOptions: function(value) {
    this.populateSelect(this.items[value]);
  }
});

/**
 * Implements a DependentSelect that will perform a AJAX request to fetch the data.
 * You specify an URL to be used and the class will bind the selected id to the string :id.
 * The returning data from the request must be an array JSON, in the following format:
 *   [[1,'Florida'],[2,'California']]
 *
 * Example
 *  new DenpendentSelect.Ajax('state_id', 'country_id', '/countries/:id/states')
 *
 * You can also bind more values to the url, example:
 *  new DependentSelect.Ajax('city_id', 'state_id', '/countries/:country_id/states/:id/cities', {replacements:
 *    countryId: function() { return $('country_id').value }
 *  ))
 */
DependentSelect.Ajax = Class.create();
Object.extend(Object.extend(DependentSelect.Ajax.prototype, DependentSelect.Base.prototype), {
  initialize: function(select_id, dependency_id, uri, options) {
    this.baseInitialize(select_id, dependency_id, options);
    this.uri = uri;
    this.options.replacements = $H({id:Prototype.K}).merge($H(this.options.replacements));
  },

  generateUri: function(value) {
    generatedUri = this.uri;

    $H(this.options.replacements).each(function(item) {
      bindTo = ":"+item.key.underscore();
      generatedUri = generatedUri.gsub(bindTo, item.value(value));
    });

    return generatedUri;
  },

  updateOptions: function(value) {
    if (!value)
      return;

    new Ajax.Request(this.generateUri(value), {
      method: 'get',
      onComplete: function(transport) {
        this.populateSelect(transport.responseText.evalJSON());
      }.bind(this)
    });
  }
});
