/**
 * Walks the dom tree from the given node up until it finds a match
 * Designed for optimal performance.
 *
 * @param {Element} node The from which to check the parent nodes
 * @param {Object} matchingSet Object to match against (possible properties: nodeName, className, classRegExp)
 * @param {Number} [levels] How many parents should the function check up from the current node (defaults to 50)
 * @return {null|Element} Returns the first element that matched the desiredNodeName(s)
 * @example
 *    var listElement = wysihtml5.dom.getParentElement(document.querySelector("li"), { nodeName: ["MENU", "UL", "OL"] });
 *    // ... or ...
 *    var unorderedListElement = wysihtml5.dom.getParentElement(document.querySelector("li"), { nodeName: "UL" });
 *    // ... or ...
 *    var coloredElement = wysihtml5.dom.getParentElement(myTextNode, { nodeName: "SPAN", className: "wysiwyg-color-red", classRegExp: /wysiwyg-color-[a-z]/g });
 */
wysihtml5.dom.getParentElement = (function() {

  function _isSameNodeName(nodeName, desiredNodeNames) {
    if (!desiredNodeNames || !desiredNodeNames.length) {
      return true;
    }

    if (typeof(desiredNodeNames) === "string") {
      return nodeName === desiredNodeNames;
    } else {
      return wysihtml5.lang.array(desiredNodeNames).contains(nodeName);
    }
  }

  function _isElement(node) {
    return node.nodeType === wysihtml5.ELEMENT_NODE;
  }

  function _hasClassName(element, className, classRegExp) {
    var classNames = (element.className || "").match(classRegExp) || [];
    if (!className) {
      return !!classNames.length;
    }
    return classNames[classNames.length - 1] === className;
  }

  function _hasStyle(element, cssStyle, styleRegExp) {
    var styles = (element.getAttribute('style') || "").match(styleRegExp) || [];
    if (!cssStyle) {
      return !!styles.length;
    }
    return styles[styles.length - 1] === cssStyle;
  }

  return function(node, matchingSet, levels, container) {
    var findByStyle = (matchingSet.cssStyle || matchingSet.styleRegExp),
        findByClass = (matchingSet.className || matchingSet.classRegExp);

    levels = levels || 50; // Go max 50 nodes upwards from current node

    // make the matching class regex from class name if omitted
    if (findByClass && !matchingSet.classRegExp) {
      matchingSet.classRegExp = new RegExp(matchingSet.className);
    }

    while (levels-- && node && node.nodeName !== "BODY" && (!container || node !== container)) {
      if (_isElement(node) && (!matchingSet.nodeName || _isSameNodeName(node.nodeName, matchingSet.nodeName)) &&
          (!findByStyle || _hasStyle(node, matchingSet.cssStyle, matchingSet.styleRegExp)) &&
          (!findByClass || _hasClassName(node, matchingSet.className, matchingSet.classRegExp))
      ) {
        return node;
      }
      node = node.parentNode;
    }
    return null;
  };
})();
