
/**
 * Module dependencies.
 */

var visit = require('rework-visit');

/**
 * Define custom function.
 */

module.exports = function(functions, args) {
  if (!functions) throw new Error('functions object required');
  return function(style){
    var functionMatcher = functionMatcherBuilder(Object.keys(functions).join('|'));

    visit(style, function(declarations){
      func(declarations, functions, functionMatcher, args);
    });
  }
};

/**
 * Visit declarations and apply functions.
 *
 * @param {Array} declarations
 * @param {Object} functions
 * @param {RegExp} functionMatcher
 * @param {Boolean} [parseArgs]
 * @api private
 */

function func(declarations, functions, functionMatcher, parseArgs) {
  if (!declarations) return;
  if (false !== parseArgs) parseArgs = true;

  declarations.forEach(function(decl){
    if ('comment' == decl.type) return;
    var generatedFuncs = [], result, generatedFunc;

    while (decl.value.match(functionMatcher)) {
      decl.value = decl.value.replace(functionMatcher, function(_, name, args){
        if (parseArgs) {
          args = args.split(/\s*,\s*/).map(strip);
        } else {
          args = [strip(args)];
        }
        // Ensure result is string
        result = '' + functions[name].apply(decl, args);

        // Prevent fall into infinite loop like this:
        //
        // {
        //   url: function(path) {
        //     return 'url(' + '/some/prefix' + path + ')'
        //   }
        // }
        //
        generatedFunc = {from: name, to: name + getRandomString()};
        result = result.replace(functionMatcherBuilder(name), generatedFunc.to + '($2)');
        generatedFuncs.push(generatedFunc);
        return result;
      });
    }

    generatedFuncs.forEach(function(func) {
      decl.value = decl.value.replace(func.to, func.from);
    })
  });
}

/**
 * Build function regexp
 *
 * @param {String} name
 * @api private
 */

function functionMatcherBuilder(name) {
  // /(?!\W+)(\w+)\(([^()]+)\)/
  return new RegExp("(?!\\W+)(" + name + ")\\(([^\(\)]+)\\)");
}

/**
 * get random string
 *
 * @api private
 */

function getRandomString() {
  return Math.random().toString(36).slice(2);
}

/**
 * strip quotes from string
 * @api private
 */

function strip(str) {
    if ('"' == str[0] || "'" == str[0]) return str.slice(1, -1);
    return str;
}
