var ExpertSearch = Class.create();
ExpertSearch.LEFT_BRACKET = '(';
ExpertSearch.RIGHT_BRACKET = ')';
ExpertSearch.AND = 'AND';
ExpertSearch.OR = 'OR';
ExpertSearch.NOT = 'NOT';

ExpertSearch.prototype = {

  initialize: function() {
    this.form = document.expertSearchForm;
    this.displayArea = $('expert-search-conditions');
    this._conditions = new Array();
    this.form.field.onchange = this.onFieldChange.bindAsEventListener(this);
    this.form.addCondition.onclick = this.onAddCondition.bindAsEventListener(this);
    this.form.buttonLeftBracket.onclick = this.onLeftBracket.bindAsEventListener(this);
    this.form.buttonRightBracket.onclick = this.onRightBracket.bindAsEventListener(this);
    this.form.buttonAND.onclick = this.onAND.bindAsEventListener(this);
    this.form.buttonOR.onclick = this.onOR.bindAsEventListener(this);
    this.form.buttonNOT.onclick = this.onNOT.bindAsEventListener(this);
    this.form.buttonBack.onclick = this.onBack.bindAsEventListener(this);
    this.findClearButtons(this.form);
    this.decodeCondition();
    this.hideOperationAndValue();
    this.updateButtons();
    var request = new Ajax.Request(AppPath + '/ajax/expert/fields');
  },

  hideOperationAndValue: function() {
    this.changeOperationAndValueVisibility('hidden');
  },

  showOperationAndValue: function() {
    this.changeOperationAndValueVisibility('');
  },

  changeOperationAndValueVisibility: function(newVisibility) {
    var fieldTables = document.getElementsByClassName('expertSearchCondition', document);
    if (fieldTables && fieldTables.length == 1) {
      var fieldTable = fieldTables[0];
      var valueCells = document.getElementsByClassName('expertSearchConditionValue', fieldTable);
      if (valueCells) {
        for (var i = 0; i < valueCells.length; ++i) {
          var child = getFirstChildElement(valueCells[i]);
          if (child) {
            child.style.visibility = newVisibility;
          }
        }
      }
      var operationCells = document.getElementsByClassName('expertSearchConditionOperation', fieldTable);
      if (operationCells) {
        for (var i = 0; i < operationCells.length; ++i) {
          var child = getFirstChildElement(operationCells[i]);
          if (child) {
            child.style.visibility = newVisibility;
          }
        }
      }
    }
  },

  findClearButtons: function(parent) {
    for (var i = 0; i < parent.childNodes.length; ++i) {
      var child = parent.childNodes[i];
      if (child.nodeType == 1) {
        if (child.tagName.toLowerCase() == 'input' && child.name == 'clear') {
          child.onclick = this.onClear.bindAsEventListener(this);
        }
        else {
          this.findClearButtons(child);
        }
      }
    }
  },

  createNamedElement: function(tagName, inputName) {
    if (Prototype.Browser.IE) {
      tagName = '<' + tagName + ' name="' + inputName + '">';
      return document.createElement(tagName);
    }
    else {
      var element = document.createElement(tagName);
      element.name = inputName;
      return element;
    }
  },

  onFieldChange: function() {
    var fieldName = Form.Element.getValue(this.form.field);
    var field = this.form.fieldMap[fieldName];
    if (field) {
      this.showOperationAndValue();
      var valueCell = $('valueCell');
      elementRemoveChildNodes(valueCell);
      if (field.type == "STRING") {
        this.form.operation.options.length = 0;
        this.form.operation.options.add(new Option('Phrase', 'PHRASE'));
        this.form.operation.options.add(new Option('All Words', 'ALL'));
        this.form.operation.options.add(new Option('Some Words', 'SOME'));
        this.form.operation.selectedIndex = 1;
        var input = this.createNamedElement('input', "value");
        input.type = "text";
        input.className = "string";
        valueCell.appendChild(input);
        Event.observe(input, 'keypress', this.valueOnKeyPress.bindAsEventListener(this));
        setTimeout(function() { input.focus(); }, 10);
      }
      else if (field.type == "INT") {
        this.form.operation.options.length = 0;
        this.form.operation.options.add(new Option('=', 'EQ'));
        this.form.operation.options.add(new Option('>', 'GT'));
        this.form.operation.options.add(new Option('>=', 'GE'));
        this.form.operation.options.add(new Option('<', 'LT'));
        this.form.operation.options.add(new Option('<=', 'LE'));
        this.form.operation.selectedIndex = 0;
        var input = this.createNamedElement('input', "value");
        input.type = "text";
        input.className = "number";
        valueCell.appendChild(input);
        Event.observe(input, 'keypress', this.intValueOnKeyPress.bindAsEventListener(this));
        if (Prototype.Browser.IE) {
          // in IE prototype attaches on keydown in the previous line.
          input.onkeypress = function(e) {
            if (!(e.keyCode >= 48 || e.keyCode <= 48 + 9)) { // if !digit
              return false;
            }
          }.bindAsEventListener(this);
        }
        setTimeout(function() { input.focus(); }, 10);
      }
      else if (field.type == "ENUM_ORDERED") {
        this.form.operation.options.length = 0;
        this.form.operation.options.add(new Option('=', 'EQ'));
        this.form.operation.options.add(new Option('>', 'GT'));
        this.form.operation.options.add(new Option('>=', 'GE'));
        this.form.operation.options.add(new Option('<', 'LT'));
        this.form.operation.options.add(new Option('<=', 'LE'));
        this.form.operation.selectedIndex = 0;
        var input = this.createNamedElement('select', "value");
        input.className = "string";
        valueCell.appendChild(input);
        input.focus();
        this.loadDictionaryItems(input, field);
      }
      else if (field.type.indexOf("ENUM") == 0) {
        this.form.operation.options.length = 0;
        this.form.operation.options.add(new Option('=', 'EQ'));
        this.form.operation.selectedIndex = 0;
        var input = this.createNamedElement('select', "value");
        input.className = "string";
        valueCell.appendChild(input);
        input.focus();
        this.loadDictionaryItems(input, field);
      }
      else {
        alert(field.type + " is not supported yet");
      }
    }
  },

  loadDictionaryItems: function(input, field) {
    var request = new Ajax.Request(AppPath + '/ajax/expert/dictionary', {
      parameters: {field:field.name},
      onComplete: function(r) {
        var items = null;
        try {
          var items = eval(r.responseText);
        }
        catch (e) {
          return;
        }
        if (items) {
          for (var i = 0; i < items.length; ++i) {
            var option = new Option(abbreviate(items[i].caption, 40), items[i].code);
            if (field.name == 'ORGTYPE_CODE' && items[i].code.indexOf(':') < 0) {
              option.style.fontWeight = 'bold';
            }
            input.options.add(option);
          }
        }
      }
    });
  },

  valueOnKeyPress: function(event) {
    if (event.keyCode == Event.KEY_RETURN) {
      this.onAddCondition();
      Event.stop(event);
    }
  },

  intValueOnKeyPress: function(event) {
    if (event.keyCode == Event.KEY_RETURN) {
      this.onAddCondition();
      Event.stop(event);
    }
    else if (event.type == 'keypress' && Prototype.Browser.Gecko && event.charCode != 0) {
      if (!(event.charCode >= 48 && event.charCode <= 48 + 9)) {
        Event.stop(event);
        return false;
      }
    }
  },

  getLastCondition: function() {
    if (this._conditions.length == 0) {
      return null;
    }
    return this._conditions[this._conditions.length - 1];
  },

  onLeftBracket: function() {
    if (this.canLeftBracket()) {
      this._conditions.push({ operation: ExpertSearch.LEFT_BRACKET });
      this.display();
    }
  },

  canLeftBracket: function() {
    var last = this.getLastCondition();
    if (!last) {
      return true;
    }
    if (last.operation == ExpertSearch.RIGHT_BRACKET) {
      return false;
    }
    return last.operation == ExpertSearch.AND
        || last.operation == ExpertSearch.OR
        || last.operation == ExpertSearch.NOT
        || last.operation == ExpertSearch.LEFT_BRACKET;
  },

  canAdd: function() {
    return this.canLeftBracket();
  },

  onRightBracket: function() {
    if (this.canRightBracket()) {
      this._conditions.push({ operation: ExpertSearch.RIGHT_BRACKET });
      this.display();
    }
  },

  canRightBracket: function() {
    var last = this.getLastCondition();
    if (!last
        || last.operation == ExpertSearch.LEFT_BRACKET
        || last.operation == ExpertSearch.AND
        || last.operation == ExpertSearch.OR
        || last.operation == ExpertSearch.NOT) {
      return false;
    }
    var level = 0;
    for (var i = 0; i < this._conditions.length; ++i) {
      var condition = this._conditions[i];
      if (condition.operation == ExpertSearch.LEFT_BRACKET) {
        ++level;
      }
      else if (condition.operation == ExpertSearch.RIGHT_BRACKET) {
        --level;
      }
    }
    return level > 0;
  },

  onAND: function() {
    if (this.canAND()) {
      this._conditions.push({ operation: ExpertSearch.AND });
      this.display();
    }
  },

  canAND: function() {
    var last = this.getLastCondition();
    if (!last || last.operation == ExpertSearch.LEFT_BRACKET || last.operation == ExpertSearch.AND || last.operation == ExpertSearch.OR || last.operation == ExpertSearch.NOT) {
      return false;
    }
    return true;
  },

  onOR: function() {
    if (this.canOR()) {
      this._conditions.push({ operation: ExpertSearch.OR });
      this.display();
    }
  },

  canOR: function() {
    var last = this.getLastCondition();
    if (!last || last.operation == ExpertSearch.LEFT_BRACKET || last.operation == ExpertSearch.AND || last.operation == ExpertSearch.OR || last.operation == ExpertSearch.NOT) {
      return false;
    }
    return true;
  },

  onNOT: function() {
    if (this.canNOT()) {
      this._conditions.push({ operation: ExpertSearch.NOT });
      this.display();
    }
  },

  canNOT: function() {
    var last = this.getLastCondition();
    if (!last || last.operation == ExpertSearch.LEFT_BRACKET || last.operation == ExpertSearch.AND) {
      return true;
    }
    return false;
  },

  onAddCondition: function() {
    if (!this.canAdd()) {
      return;
    }
    var valueField = this.form.value;
    if (valueField) {
      var value = Form.Element.getValue(valueField);
      if (value) {
        value = value.strip();
        if (value.length > 0) {
          var condition = {
            field: Form.Element.getValue(this.form.field),
            operation: Form.Element.getValue(this.form.operation),
            value: value
          };
          this._conditions.push(condition);
          this.display();
          valueField.value = '';
          valueField.focus();
        }
      }
    }
  },

  onBack: function() {
    if (this._conditions.length > 0) {
      this._conditions.pop();
      this.display();
    }
  },

  initExpertSearchFieldList: function(blocks) {
    this.blocks = blocks;
    var fieldMap = new Object();
    for (var i = 0; i < blocks.length; ++i) {
      var block = blocks[i];
      for (var j = 0; j < block.fields.length; ++j) {
        var field = block.fields[j];
        fieldMap[field.name] = field;
      }
    }
    this.form.fieldMap = fieldMap;
    this.updateFieldList();
    this.display();
  },

  updateFieldList: function() {
    var fieldSelect = this.form.field;
    elementRemoveChildNodes(fieldSelect);
    fieldSelect.options.add(new Option('- Select Field -', ''));
    var blocks = this.blocks;
    for (var i = 0; i < blocks.length; ++i) {
      var block = blocks[i];
      var optGroup = document.createElement('optgroup');
      optGroup.label = block.caption;
      fieldSelect.appendChild(optGroup);
      for (var j = 0; j < block.fields.length; ++j) {
        var field = block.fields[j];
        var option = document.createElement('option');
        option.value = field.name;
        option.innerHTML = field.caption;
        optGroup.appendChild(option);
      }
    }
  },

  display: function() {
    elementRemoveChildNodes(this.displayArea);
    for (var i = 0; i < this._conditions.length; ++i) {
      var condition = this._conditions[i];
      var conditionDiv = document.createElement('div');
      conditionDiv.className = 'condition';
      if (condition.field) {
        var fieldElement = document.createElement('span');
        fieldElement.appendChild(document.createTextNode(this.form.fieldMap
                                                         ? this.form.fieldMap[condition.field].caption
                                                         : condition.field));
        conditionDiv.appendChild(fieldElement);
      }
      var operationElement = document.createElement('span');
      operationElement.appendChild(document.createTextNode(this.translateOperation(condition.operation)));
      conditionDiv.appendChild(operationElement);
      if (condition.value) {
        var valueElement = document.createElement('span');
        valueElement.appendChild(document.createTextNode(this.getDisplayValue(condition)));
        conditionDiv.appendChild(valueElement);
      }
      this.displayArea.appendChild(conditionDiv);
    }
    this.updateButtons();
    this.encodeCondition();
  },

  getDisplayValue: function(condition) {
    if (this.form.fieldMap) {
      var field = this.form.fieldMap[condition.field];
      if (field.type.indexOf('ENUM') >= 0) {
        if (!this.dictionaryCache) {
          this.dictionaryCache = {};
        }
        var cacheKey = condition.field + ':' + condition.value;
        if (this.dictionaryCache[cacheKey]) {
          return this.dictionaryCache[cacheKey];
        }
        else {
          var request = new Ajax.Request(AppPath + '/ajax/expert/dictionary/item', {
            asynchronous: false,
            parameters: {field:field.name, value:condition.value}
          });
          var displayValue = request.transport.responseText;
          this.dictionaryCache[cacheKey] = displayValue;
          return displayValue;
        }
      }
    }
    return condition.value;
  },

  translateOperation: function(operation) {
    if (operation == 'EQ') {
      return '=';
    }
    else if (operation == 'LE') {
      return '<=';
    }
    else if (operation == 'LT') {
      return '<';
    }
    else if (operation == 'GE') {
      return '>=';
    }
    else if (operation == 'GT') {
      return '>';
    }
    else {
      return operation;
    }
  },

  encodeCondition: function() {
    var result = '';
    for (var i = 0; i < this._conditions.length; ++i) {
      var condition = this._conditions[i];
      if (result != '') {
        result += ',';
      }
      if (condition.field && condition.value) {
        result += encodeURIComponent(condition.field)
                + ':'
                + encodeURIComponent(condition.operation)
                + ':'
                + encodeURIComponent(condition.value);
      }
      else {
        result += encodeURIComponent(condition.operation);
      }
    }
    this.form.condition.value = result;
  },

  decodeCondition: function() {
    if (!this.form.condition) {
      restore;
    }
    var encodedCondition = this.form.condition.value;
    if (!encodedCondition || encodedCondition.length == 0) {
      return;
    }
    var parts = encodedCondition.split(',');
    for (var i = 0; i < parts.length; ++i) {
      var part = parts[i];
      if (part.indexOf(':') >= 0) {
        var conditionParts = part.split(':');
        this._conditions.push({
          field: decodeURIComponent(conditionParts[0]),
          operation: decodeURIComponent(conditionParts[1]),
          value: decodeURIComponent(conditionParts[2])
        });
      }
      else {
        this._conditions.push({ operation: decodeURIComponent(part) });
      }
    }
    if (this._conditions.length > 0) {
      this.display();
    }
  },

  updateButtons: function() {
    this.form.addCondition.disabled = !this.canAdd();
    this.form.buttonLeftBracket.disabled = !this.canLeftBracket();
    this.form.buttonRightBracket.disabled = !this.canRightBracket();
    this.form.buttonAND.disabled = !this.canAND();
    this.form.buttonOR.disabled = !this.canOR();
    this.form.buttonNOT.disabled = !this.canNOT();
    this.form.buttonBack.disabled = this._conditions.length == 0;
  },

  onClear: function() {
    this._conditions = new Array();
    this.display();
    this.form.field.selectedIndex = 0;
    if (this.form.value) {
      this.form.value.value = '';
    }
    // clear "Select Only Records With:" checkboxes
    this.form.headquarter.checked = false;
    this.form.phone.checked = false;
    this.form.fax.checked = false;
    this.form.email.checked = false;
    this.form.url.checked = false;
    this.form.award.checked = false;
    this.form.conference.checked = false;
    this.form.publication.checked = false;
    // select all databases
    /*
    for (var i = 0; i < this.form.elements.length; ++i) {
      var element = this.form.elements[i];
      if (element.tagName.toLowerCase() == 'input' && element.name == 'database') {
        element.checked = true;
      }
    }
    */
    return false;
  }

};
