“use strict”; const lengthFromProperties = require(“../utils”).lengthFromProperties; const getAttributeValue = require(“./attributes”).getAttributeValue; const idlUtils = require(“./generated/utils”);

const privates = Symbol(“HTMLCollection internal slots”);

const conflictKeys = new Set([“length”, “item”, “namedItem”]);

class HTMLCollection {

constructor(secret, element, query) {
  if (secret !== privates) {
    throw new TypeError("Invalid constructor");
  }

  this[privates] = { element, query, keys: [], length: 0, version: -1, conflictElements: Object.create(null) };
  updateHTMLCollection(this);
}

get length() {
  updateHTMLCollection(this);
  return this[privates].length;
}

item(index) {
  updateHTMLCollection(this);
  return this[index] || null;
}

namedItem(name) {
  updateHTMLCollection(this);

  if (conflictKeys.has(name)) {
    return this[privates].conflictElements[name] || null;
  }

  if (Object.prototype.hasOwnProperty.call(this, name)) {
    return this[name];
  }
  return null;
}

}

HTMLCollection.prototype = Array.prototype;

function updateHTMLCollection(collection) {

if (collection[privates].version < collection[privates].element._version) {
  resetHTMLCollectionTo(collection, collection[privates].query());
  collection[privates].version = collection[privates].element._version;
}

}

function resetHTMLCollectionTo(collection, impls) {

const wrappers = impls.map(idlUtils.wrapperForImpl);

const startingLength = lengthFromProperties(collection);
for (let i = 0; i < startingLength; ++i) {
  delete collection[i];
}

for (let i = 0; i < wrappers.length; ++i) {
  collection[i] = wrappers[i];
}
collection[privates].length = wrappers.length;

const keys = collection[privates].keys;
for (let i = 0; i < keys.length; ++i) {
  delete collection[keys[i]];
}
keys.length = 0;

for (let i = 0; i < impls.length; ++i) {
  addIfAttrPresent(impls[i], wrappers[i], "name");
}
for (let i = 0; i < impls.length; ++i) {
  addIfAttrPresent(impls[i], wrappers[i], "id");
}

function addIfAttrPresent(impl, wrapper, attr) {
  const value = getAttributeValue(impl, attr);

  if (value === null || value === "") {
    return;
  }

  // Don't overwrite numeric indices with named ones.
  const valueAsNumber = Number(value);
  if (!Number.isNaN(valueAsNumber) && valueAsNumber >= 0) {
    return;
  }

  // Don't override existing named ones
  if (keys.indexOf(value) !== -1) {
    return;
  }

  if (conflictKeys.has(value)) {
    collection[privates].conflictElements[value] = wrapper;
  } else {
    collection[value] = wrapper;
  }
  keys.push(value);
}

}

module.exports = function (core) {

core.HTMLCollection = HTMLCollection;

};

module.exports.create = function (element, query) {

return new HTMLCollection(privates, element, query);

};

module.exports.update = updateHTMLCollection;