
import domToType from './domToType.js'

/**
 * Iterates items until an undeleted item is found.
 *
 * @private
 */
export function iterateUntilUndeleted (item) {
  while (item !== null && item._deleted) {
    item = item._right
  }
  return item
}

/**
 * Removes an association (the information that a DOM element belongs to a
 * type).
 *
 * @param {DomBinding} domBinding The binding object
 * @param {Element} dom The dom that is to be associated with type
 * @param {YXmlElement|YXmlHook} type The type that is to be associated with dom
 *
 */
export function removeAssociation (domBinding, dom, type) {
  domBinding.domToType.delete(dom)
  domBinding.typeToDom.delete(type)
}

/**
 * Creates an association (the information that a DOM element belongs to a
 * type).
 *
 * @param {DomBinding} domBinding The binding object
 * @param {Element} dom The dom that is to be associated with type
 * @param {YXmlElement|YXmlHook} type The type that is to be associated with dom
 *
 */
export function createAssociation (domBinding, dom, type) {
  if (domBinding !== undefined) {
    domBinding.domToType.set(dom, type)
    domBinding.typeToDom.set(type, dom)
  }
}

/**
 * If oldDom is associated with a type, associate newDom with the type and
 * forget about oldDom. If oldDom is not associated with any type, nothing happens.
 *
 * @param {DomBinding} domBinding The binding object
 * @param {Element} oldDom The existing dom
 * @param {Element} newDom The new dom object
 */
export function switchAssociation (domBinding, oldDom, newDom) {
  if (domBinding !== undefined) {
    const type = domBinding.domToType.get(oldDom)
    if (type !== undefined) {
      removeAssociation(domBinding, oldDom, type)
      createAssociation(domBinding, newDom, type)
    }
  }
}

/**
 * Insert Dom Elements after one of the children of this YXmlFragment.
 * The Dom elements will be bound to a new YXmlElement and inserted at the
 * specified position.
 *
 * @param {YXmlElement} type The type in which to insert DOM elements.
 * @param {YXmlElement|null} prev The reference node. New YxmlElements are
 *                           inserted after this node. Set null to insert at
 *                           the beginning.
 * @param {Array<Element>} doms The Dom elements to insert.
 * @param {?Document} _document Optional. Provide the global document object.
 * @param {DomBinding} binding The dom binding
 * @return {Array<YXmlElement>} The YxmlElements that are inserted.
 *
 * @private
 */
export function insertDomElementsAfter (type, prev, doms, _document, binding) {
  const types = domsToTypes(doms, _document, binding.opts.hooks, binding.filter, binding)
  return type.insertAfter(prev, types)
}

export function domsToTypes (doms, _document, hooks, filter, binding) {
  const types = []
  for (let dom of doms) {
    const t = domToType(dom, _document, hooks, filter, binding)
    if (t !== false) {
      types.push(t)
    }
  }
  return types
}

/**
 * @private
 */
export function insertNodeHelper (yxml, prevExpectedNode, child, _document, binding) {
  let insertedNodes = insertDomElementsAfter(yxml, prevExpectedNode, [child], _document, binding)
  if (insertedNodes.length > 0) {
    return insertedNodes[0]
  } else {
    return prevExpectedNode
  }
}

/**
 * Remove children until `elem` is found.
 *
 * @param {Element} parent The parent of `elem` and `currentChild`.
 * @param {Element} currentChild Start removing elements with `currentChild`. If
 *                               `currentChild` is `elem` it won't be removed.
 * @param {Element|null} elem The elemnt to look for.
 *
 * @private
 */
export function removeDomChildrenUntilElementFound (parent, currentChild, elem) {
  while (currentChild !== elem) {
    const del = currentChild
    currentChild = currentChild.nextSibling
    parent.removeChild(del)
  }
}
