'use strict'

module.exports = visitParents

var convert = require('unist-util-is/convert')
var color = require('./color')

var CONTINUE = true
var SKIP = 'skip'
var EXIT = false

visitParents.CONTINUE = CONTINUE
visitParents.SKIP = SKIP
visitParents.EXIT = EXIT

function visitParents(tree, test, visitor, reverse) {
  var is

  if (func(test) && !func(visitor)) {
    reverse = visitor
    visitor = test
    test = null
  }

  is = convert(test)

  one(tree, null, [])()

  function one(child, index, parents) {
    var value = object(child) ? child : {}
    var name

    if (string(value.type)) {
      name = string(value.tagName)
        ? value.tagName
        : string(value.name)
        ? value.name
        : undefined

      node.displayName =
        'node (' + color(value.type + (name ? '<' + name + '>' : '')) + ')'
    }

    return node

    function node() {
      var result = []
      var subresult

      if (!test || is(child, index, parents[parents.length - 1] || null)) {
        result = toResult(visitor(child, parents))

        if (result[0] === EXIT) {
          return result
        }
      }

      if (!child.children || result[0] === SKIP) {
        return result
      }

      subresult = toResult(children(child.children, parents.concat(child)))
      return subresult[0] === EXIT ? subresult : result
    }
  }

  // Visit children in `parent`.
  function children(children, parents) {
    var min = -1
    var step = reverse ? -1 : 1
    var index = (reverse ? children.length : min) + step
    var child
    var result

    while (index > min && index < children.length) {
      child = children[index]
      result = one(child, index, parents)()

      if (result[0] === EXIT) {
        return result
      }

      index = typeof result[1] === 'number' ? result[1] : index + step
    }
  }
}

function toResult(value) {
  if (object(value) && 'length' in value) {
    return value
  }

  if (typeof value === 'number') {
    return [CONTINUE, value]
  }

  return [value]
}

function func(d) {
  return typeof d === 'function'
}

function string(d) {
  return typeof d === 'string'
}

function object(d) {
  return typeof d === 'object' && d !== null
}
