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

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

class NodeList {

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

  if (config.nodes) {
    this[privates] = {
      isLive: false,
      length: config.nodes.length
    };

    for (let i = 0; i < config.nodes.length; ++i) {
      this[i] = config.nodes[i];
    }
  } else {
    this[privates] = {
      isLive: true,
      element: config.element,
      query: config.query,
      snapshot: undefined,
      length: 0,
      version: -1
    };
    updateNodeList(this);
  }
}

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

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

// TODO reimplement this in webidl2js so these become more per-spec
* keys() {
  updateNodeList(this);
  const length = this[privates].length;
  for (let i = 0; i < length; ++i) {
    yield i;
  }
}

* entries() {
  updateNodeList(this);
  const length = this[privates].length;
  for (let i = 0; i < length; ++i) {
    yield [i, this[i]];
  }
}

forEach(callback) {
  const thisArg = arguments[1]; // TODO Node v6: use default arguments
  let values = Array.from(this);
  let i = 0;
  while (i < values.length) {
    callback.call(thisArg, values[i], i, this);
    values = Array.from(this);
    ++i;
  }
}

}

NodeList.prototype = NodeList.prototype.values = Array.prototype;

function updateNodeList(nodeList) {

if (nodeList[privates].isLive) {
  if (nodeList[privates].version < nodeList[privates].element._version) {
    nodeList[privates].snapshot = nodeList[privates].query();
    resetNodeListTo(nodeList, nodeList[privates].snapshot);
    nodeList[privates].version = nodeList[privates].element._version;
  }
} else {
  nodeList[privates].length = lengthFromProperties(nodeList);
}

}

function resetNodeListTo(nodeList, nodes) {

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

for (let i = 0; i < nodes.length; ++i) {
  const wrapper = idlUtils.wrapperForImpl(nodes[i]);
  nodeList[i] = wrapper ? wrapper : nodes[i];
}
nodeList[privates].length = nodes.length;

}

module.exports = function (core) {

core.NodeList = NodeList;

};

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

return new NodeList(privates, { element, query });

};

module.exports.createStatic = function (nodes) {

return new NodeList(privates, { nodes });

};

module.exports.update = updateNodeList;