// adopted from: https://github.com/jonschlinkert/expand
// Copyright (c) 2015, Jon Schlinkert (MIT)

var get = require('../object/get')
var kindOf = require('../lang/kindOf')
var isPrimitive = require('../lang/isPrimitive')

var cache = {prev: null}

function expand (val, data, opts) {
  return resolve(val, data || val, opts)
}

function resolve (val, data, opts) {
  switch(kindOf(val)) {
    case 'string': return resolveString(val, data, opts)
    case 'object': return resolveObject(val, data, opts)
    case 'array' : return resolveArray(val, data, opts)
    default      : return val
  }
}

function resolveString (str, data, opts) {
  // pass a regex as an option. not an object.
  var regex = opts || /\{\{([^\}]+)\}\}/g

  if (!regex.test(str)) return str
  var result = str

  str.replace(regex, function (match, es6, erb, index) {
    var prop = trim(es6 || erb)
    var val

    if (kindOf(erb) === 'number') index = erb

    // return if prop is undefined
    if (!prop) return

    // if prop is a function try and execute it. This is a hack!!
    // Not using method from https://github.com/jonschlinkert/expand.
    // this does not cover all cases as with above. But then again,
    // I have no to little use calling functions in this type
    // of situation
    if (/[()]/.test(prop)) {
      var firstparens = prop.indexOf('(')
      var lastparens = prop.indexOf(')')
      var exp = prop.slice(0, firstparens)
      var expResult = prop.slice(firstparens + 1, prop.length -1)
      val = data[exp](data[expResult])
    } else {
      val = data[prop] || get(data, prop)
    }

    if (isPrimitive(val)) {
      if (kindOf(index) !== 'number') index = str.indexOf(match)

      var len = match.length
      if (str.length > len) {
        var head = str.slice(0, index)
        var tail = str.slice(index + len)
        result = head + val + tail
      } else {
        result = val
      }
    } else if (val) {
      // prevent inifinte loops
      if (result === cache.prev) return

      cache.prev = result
      result = resolve(val, data, opts)
    }

    if (typeof result === 'string') result = resolveString(result, data, opts)
  })

  return result
}

function resolveObject (obj, data, opts) {
  Object.keys(obj).forEach(function (key, idx, arr) {
    obj[key] = resolve(obj[key], data, opts)
  })

  return obj
}

function resolveArray (arr, data, opts) {
  var len = arr.length
  var i = - 1

  while (++i < len) {
    arr[i] = resolve(arr[i], data, opts)
  }

  return arr
}

function trim (str) {
  return str == null ? '' : String(str).trim()
}

module.exports = expand

@@ -370,16 +370,17 @@
   switch
+ 
 (kindOf(
@@ -397,22 +397,41 @@
   case '
-string
+array' :%0A    case 'object
 ': retur
@@ -439,22 +439,22 @@
  resolve
-String
+Object
 (val, da
@@ -465,38 +465,38 @@
 opts)%0A    case '
-object
+string
 ': return resolv
@@ -488,38 +488,38 @@
 : return resolve
-Object
+String
 (val, data, opts
@@ -528,93 +528,201 @@
     
-case 'array' : return resolveArray(val, data, opts)%0A    default      : return val%0A  %7D
+default      : return val%0A  %7D%0A%7D%0A%0Afunction resolveObject (obj, data, opts) %7B%0A  Object.keys(obj).forEach(function (key, idx, arr) %7B%0A    obj%5Bkey%5D = resolve(obj%5Bkey%5D, data, opts)%0A  %7D)%0A%0A  return obj
 %0A%7D%0A%0A
@@ -1611,61 +1611,159 @@
  1, 
-prop.length -1)%0A      val = data%5Bexp%5D(data%5BexpResult%5D
+lastparens)%0A      val = typeof data%5Bexp%5D === 'function'%0A        ? data%5Bexp%5D(data%5BexpResult%5D)%0A        : resolve('%7B%7B' + exp + '%7D%7D', data, opts)(expResult
 )%0A  
@@ -2411,336 +2411,8 @@
 %0A%7D%0A%0A
-function resolveObject (obj, data, opts) %7B%0A  Object.keys(obj).forEach(function (key, idx, arr) %7B%0A    obj%5Bkey%5D = resolve(obj%5Bkey%5D, data, opts)%0A  %7D)%0A%0A  return obj%0A%7D%0A%0Afunction resolveArray (arr, data, opts) %7B%0A  var len = arr.length%0A  var i = - 1%0A%0A  while (++i %3C len) %7B%0A    arr%5Bi%5D = resolve(arr%5Bi%5D, data, opts)%0A  %7D%0A%0A  return arr%0A%7D%0A%0A
 func

refactor string/expander.js