'use strict'

/**
 * @license
 * node-scrapy <https://github.com/eeshi/node-scrapy>
 * Copyright Stefan Maric, Adrian Obelmejias, and other contributors <https://github.com/eeshi/node-scrapy/graphs/contributors>
 * Released under MIT license <https://github.com/eeshi/node-scrapy/blob/master/LICENSE>
 */

/**
 * Module dependencies
 */

let cssSelect = require('css-select')
let htmlparser2 = require('htmlparser2')
let getters = require('./getters')
let parseQuery = require('./query-parser')
let utils = {
  isArray: Array.isArray,
  isPlainObject: require('lodash.isplainobject'),
  isString: require('lodash.isstring')
}

module.exports = exports = extract

/**
 * Given an `html` string/buffer, extract data as described in the `model`.
 * @static
 * @param  {string}           html    HTML string to parse
 * @param  {Object|string}    model   String or object describing the data to be extracted from the given HTML
 * @return {Object}
 */

function extract (html, model) {
  let dom = htmlparser2.parseDOM(html)
  return getItem(dom, model)
}

/**
 * Given a `dom`, traverse it to get the desired item
 * @param  {Object}                 dom       DOM node
 * @param  {(string|Array|Object)}  item      Data to extract
 * @return {string}                           a string or an array of strings with the extracted data
 */

function getItem (dom, item) {
  let data
  let matches

  if (utils.isPlainObject(item)) {
    data = {}
    for (let key in item) data[key] = getItem(dom, item[key])
  } else if (utils.isArray(item)) {
    let query = parseQuery(item[0])

    matches = cssSelect.selectAll(query.selector, dom)

    if (utils.isArray(item[1]) || utils.isPlainObject(item[1])) {
      data = matches.map(context => getItem(context, item[1]))
    } else {
      data = matches.map(
        getters.hasOwnProperty(query.getter)
          ? getters[query.getter]
          : getters.$textContent
      )
    }
  } else if (utils.isString(item)) {
    let query = parseQuery(item)
    matches = cssSelect.selectOne(query.selector, dom)
    data = (
      getters.hasOwnProperty(query.getter)
        ? getters[query.getter]
        : getters.$textContent
    )(matches)
  }

  return data
}

@@ -1373,22 +1373,8 @@
 data
-%0A  let matches
 %0A%0A  
@@ -1554,21 +1554,24 @@
 tem%5B0%5D)%0A
-%0A
     
+let 
 matches 
@@ -1785,127 +1785,37 @@
 map(
-%0A        getters.hasOwnProperty(query.getter)%0A          ? 
+resolveGetter(
 getters
-%5B
+, 
 query
-.getter%5D%0A          : getters.$textContent%0A      
+)
 )%0A  
@@ -1892,16 +1892,20 @@
 em)%0A    
+let 
 matches 
@@ -1947,16 +1947,17 @@
 r, dom)%0A
+%0A
     data
@@ -1963,61 +1963,326 @@
 a = 
-(%0A      getters.hasOwnProperty(query.getter)%0A
+resolveGetter(getters, query)(matches)%0A  %7D%0A%0A  return data%0A%7D%0A%0A/**%0A * Given a getters collection and a query, decide what getter function to use%0A * @param  %7BObject%7D     getters Simple collection of getter functions%0A * @param  %7BObject%7D     query   Query object, containing a getter property%0A * @return %7BFunction%7D 
         
 ? ge
@@ -2281,41 +2281,181 @@
     
-?
+  A
  getter
-s%5Bquery.getter%5D%0A        :
+ function%0A */%0Afunction resolveGetter (getters, query) %7B%0A  // Default to $text / $textContent if no getter was provided in query%0A  if (query.getter === null) return
  get
@@ -2476,40 +2476,322 @@
 ent%0A
+%0A
   
-  )(matches)%0A  %7D%0A%0A  return data
+// Use requested getter function if it is part of the getters collection%0A  if (getters.hasOwnProperty(query.getter)) return getters%5Bquery.getter%5D%0A%0A  // Simply return node attribute if a getter was requested in query%0A  // but doesn't exist in the getters collection%0A  return el =%3E el.attribs%5Bquery.getter%5D %7C%7C null
 %0A%7D%0A

Use a function to resolve what getter call