var DropDownList = Class.create({
  initialize : function(element, options) {
    //optional parameters workaround
    options = options || {};

    //make self a reference to this
    var self = this;

    //set encapsulated element, add styles and event listeners
    if(!Object.isElement(element))
      throw new Error('Cannot instantiate DropDownList: ' + element + ' is not a DOM node.');
    this.element = element;
    this.element.setStyle({
      position : 'relative'
    });
    this.element.observe('mouseover', this.open.bind(this));
    this.element.observe('mouseout', this.close.bind(this));

    //set classNameSelected - default is 'selected'
    this.classNameSelected = options.classNameSelected || 'selected';

    //set classNameList - default is 'list'
    this.classNameList = options.classNameList || 'list';

    //set selectedElement
    var selectedElements = this.element.select('.' + this.classNameSelected);
    if(selectedElements.length == 0)
      throw new Error('Cannot instantiate DropDownList: ' + element + ' does not have a DOM node of CSS class ' + this.classNameSelected + '.');
    this.selectedElement = selectedElements[0];
    this.selectedElement.onclick = function() {
      return false;
    }

    //set listElement and add styles
    var listElements = this.element.select('.' + this.classNameList);
    if(listElements.length == 0)
      throw new Error('Cannot instantiate DropDownList: ' + element + ' does not have a DOM node of CSS class ' + this.classNameList + '.');
    this.listElement = listElements[0];
    this.listElement.setStyle({
      display : 'none',
      position : 'absolute',
      width : '100%'
    });

    //set valueElements, add styles and event listeners
    var valueElements = this.listElement.select('a');
    for(var i = 0; i < valueElements.length; i++) {
      //set styles
      valueElements[i].setStyle({
        display : 'block'
      });

      //set prototype event handler
      valueElements[i].observe('click', function(event) {
        //cache current state
        var cache = Object.toJSON(self.getSelected());

        var element = Event.findElement(event, 'a');
        self.setSelected({
          value : element.hash.substring(1),
          label : element.innerHTML
        });

        self.close();

        //call onClick callback
        if(self.onClick)
          self.onClick(self);

        //call onChange callback if current state differs from cached state
        if(self.onChange && cache != Object.toJSON(self.getSelected()))
          self.onChange(self);
      });

      //set standard event handler to avoid linking
      valueElements[i].onclick = function() {
        return false;
      };
    }

    //set onClick callback
    if(options.onClick && !Object.isFunction(options.onClick))
      throw new Error('Cannot instantiate SelectableList: ' + options.onClick + ' is not a function.');
    this.onClick = options.onClick;

    //set onChange callback
    if(options.onChange && !Object.isFunction(options.onChange))
      throw new Error('Cannot instantiate SelectableList: ' + options.onChange + ' is not a function.');
    this.onChange = options.onChange;
  },

  open : function() {
    this.listElement.setStyle({
      display : 'block'
    });
  },

  close : function() {
    this.listElement.setStyle({
      display : 'none'
    });
  },

  setSelected : function(object) {
    //return if value and label do not change
    if(object.value == this.getValue() && object.label == this.getLabel())
      return;

    //change value and label
    this.setValue(object.value);
    this.setLabel(object.label);
  },

  getSelected : function() {
    return {
      value : this.getValue(),
      label : this.getLabel()
    };
  },

  // provide cross browser write access to link.hash
  setValue : function(value) {
    this.selectedElement.href = this.selectedElement.href.substring(0, this.selectedElement.href.indexOf('#')) + '#' + value;
  },

  // provide cross browser read access to link.hash
  getValue : function() {
    return this.selectedElement.hash.substring(1);
  },

  setLabel : function(value) {
    this.selectedElement.update(value);
  },

  getLabel : function() {
    return this.selectedElement.innerHTML;
  }
});
