“use strict”;

const conversions = require(“webidl-conversions”);

const HTMLElementImpl = require(“./HTMLElement-impl”).implementation; const idlUtils = require(“../generated/utils”); const NODE_TYPE = require(“../node-type”); const createHTMLCollection = require(“../html-collection”).create; const domSymbolTree = require(“../helpers/internal-constants”).domSymbolTree; const descendantsByHTMLLocalName = require(“../helpers/traversal”).descendantsByHTMLLocalName; const closest = require(“../helpers/traversal”).closest;

class HTMLSelectElementImpl extends HTMLElementImpl {

_formReset() {
  Array.prototype.forEach.call(this.options, option => {
    const optionImpl = idlUtils.implForWrapper(option);
    optionImpl._selectedness = option.defaultSelected;
    optionImpl._dirtyness = false;
  });
  this._askedForAReset();
}

_askedForAReset() {
  if (this.hasAttribute("multiple")) {
    return;
  }

  const selected = Array.prototype.filter.call(this.options, option => {
    option = idlUtils.implForWrapper(option);
    return option._selectedness;
  });

  // size = 1 is default if not multiple
  if ((!this.size || this.size === 1) && !selected.length) {
    // select the first option that is not disabled
    for (let i = 0; i < this.options.length; ++i) {
      const option = idlUtils.implForWrapper(this.options[i]);
      let disabled = this.options[i].disabled;
      const parentNode = domSymbolTree.parent(option);
      if (parentNode &&
          parentNode.nodeName.toUpperCase() === "OPTGROUP" &&
          idlUtils.wrapperForImpl(parentNode).disabled) {
        disabled = true;
      }

      if (!disabled) {
        // (do not set dirty)
        option._selectedness = true;
        break;
      }
    }
  } else if (selected.length >= 2) {
    // select the last selected option
    selected.forEach((option, index) => {
      option = idlUtils.implForWrapper(option);
      option._selectedness = index === selected.length - 1;
    });
  }
}

_descendantAdded(parent, child) {
  if (child.nodeType === NODE_TYPE.ELEMENT_NODE) {
    this._askedForAReset();
  }

  super._descendantAdded.apply(this, arguments);
}

_descendantRemoved(parent, child) {
  if (child.nodeType === NODE_TYPE.ELEMENT_NODE) {
    this._askedForAReset();
  }

  super._descendantRemoved.apply(this, arguments);
}

_attrModified(name) {
  if (name === "multiple" || name === "size") {
    this._askedForAReset();
  }
  super._attrModified.apply(this, arguments);
}

get options() {
  // TODO: implement HTMLOptionsCollection
  return createHTMLCollection(this, () => descendantsByHTMLLocalName(this, "option"));
}

get length() {
  return this.options.length;
}

get selectedIndex() {
  return Array.prototype.reduceRight.call(this.options, (prev, option, i) => {
    option = idlUtils.implForWrapper(option);
    return option.selected ? i : prev;
  }, -1);
}

set selectedIndex(index) {
  Array.prototype.forEach.call(this.options, (option, i) => {
    option = idlUtils.implForWrapper(option);
    option.selected = i === index;
  });
}

get value() {
  let i = this.selectedIndex;
  if (this.options.length && (i === -1)) {
    i = 0;
  }
  if (i === -1) {
    return "";
  }
  return this.options[i].value;
}

set value(val) {
  const self = this;
  Array.prototype.forEach.call(this.options, option => {
    option = idlUtils.implForWrapper(option);
    if (option.value === val) {
      option.selected = true;
    } else if (!self.hasAttribute("multiple")) {
      // Remove the selected bit from all other options in this group
      // if the multiple attr is not present on the select
      option.selected = false;
    }
  });
}

get form() {
  return closest(this, "form");
}

get type() {
  return this.hasAttribute("multiple") ? "select-multiple" : "select-one";
}

add(opt, before) {
  if (before) {
    this.insertBefore(opt, before);
  } else {
    this.appendChild(opt);
  }
}

remove(index) {
  const opts = this.options;
  if (index >= 0 && index < opts.length) {
    const el = idlUtils.implForWrapper(opts[index]);
    domSymbolTree.parent(el).removeChild(el);
  }
}

get size() {
  if (!this.hasAttribute("size")) {
    return 0;
  }
  const size = conversions["unsigned long"](this.getAttribute("size"));
  if (isNaN(size)) {
    return 0;
  }
  return size;
}

set size(V) {
  this.setAttribute("size", V);
}

}

module.exports = {

implementation: HTMLSelectElementImpl

};