/* @flow */
"use strict"

const keywordSets = require("../reference/keywordSets")
const isNumbery = require("./isNumbery")
const isStandardSyntaxValue = require("./isStandardSyntaxValue")
const isValidFontSize = require("./isValidFontSize")
const isVariable = require("./isVariable")
const postcssValueParser = require("postcss-value-parser")

const nodeTypesToCheck = new Set([ "word", "string", "space", "div" ])

function joinValueNodes(firstNode, secondNode, charactersBetween) {
  firstNode.value = firstNode.value + charactersBetween + secondNode.value

  return firstNode
}

/**
 * Get the font-families within a `font` shorthand property value.
 *
 * @param {string} value
 * @return {object} Collection font-family nodes
 */
module.exports = function findFontFamily(value/*: string*/)/*: Array<Object>*/ {
  const fontFamilies = []

  const valueNodes = postcssValueParser(value)

  // Handle `inherit`, `initial` and etc
  if (valueNodes.nodes.length === 1 && keywordSets.basicKeywords.has(valueNodes.nodes[0].value.toLowerCase())) {
    return [valueNodes.nodes[0]]
  }

  let needMergeNodesByValue = false
  let mergeCharacters = null

  valueNodes.walk((valueNode, index, nodes) => {
    if (valueNode.type === "function") {
      return false
    }
    if (!nodeTypesToCheck.has(valueNode.type)) {
      return
    }

    const valueLowerCase = valueNode.value.toLowerCase()

    // Ignore non standard syntax
    if (!isStandardSyntaxValue(valueLowerCase)) {
      return
    }

    // Ignore variables
    if (isVariable(valueLowerCase)) {
      return
    }

    // Ignore keywords for other font parts
    if (keywordSets.fontShorthandKeywords.has(valueLowerCase) && !keywordSets.fontFamilyKeywords.has(valueLowerCase)) {
      return
    }

    // Ignore font-sizes
    if (isValidFontSize(valueNode.value)) {
      return
    }

    // Ignore anything come after a <font-size>/, because it's a line-height
    if (nodes[index - 1] && nodes[index - 1].value === "/" && nodes[index - 2] && isValidFontSize(nodes[index - 2].value)) {
      return
    }

    // Ignore number values
    if (isNumbery(valueLowerCase)) {
      return
    }

    // Detect when a space or comma is dividing a list of font-families, and save the joining character.
    if ((valueNode.type === "space" || valueNode.type === "div" && valueNode.value !== ",") && fontFamilies.length !== 0) {
      needMergeNodesByValue = true
      mergeCharacters = valueNode.value
      return
    } else if (valueNode.type === "space" || valueNode.type === "div") {
      return
    }

    const fontFamily = valueNode

    if (needMergeNodesByValue) {
      joinValueNodes(fontFamilies[fontFamilies.length - 1], valueNode, mergeCharacters)
      needMergeNodesByValue = false
      mergeCharacters = null
    } else {
      fontFamilies.push(fontFamily)
    }
  })

  return fontFamilies
}
