var balanced = require('balanced-match');
var debug = require('debug')('css-color-function:parse');

/**
 * Expose `parse`.
 */

module.exports = parse;

/**
 * Parse a CSS color function string.
 *
 * @param {String} string
 * @return {Array}
 */

function parse (string) {
  if ('string' != typeof string) string = string.toString();
  debug('string %s', string);

  /**
   * Match the current position in the string against a `regexp`, returning the
   * match if one exists.
   *
   * @param {RegExp} regexp
   * @return {Undefined or Array}
   */

  function match (regexp) {
    var m = regexp.exec(string);
    if (!m) return;
    string = string.slice(m[0].length);
    return m.slice(1);
  }

  /**
   * Match whitespace.
   */

  function whitespace () {
    match(/^\s+/);
  }

  /**
   * Match a right parentheses.
   *
   * @return {Array or Undefined}
   */

  function rparen () {
    var m = match(/^\)/);
    if (!m) return;
    debug('rparen');
    return m;
  }

  /**
   * Match a modifier: '+' '-' '*'.
   *
   * @return {Object or Undefined}
   */

  function modifier () {
    var m = match(/^([\+\-\*])/);
    if (!m) return;
    var ret = {};
    ret.type = 'modifier';
    ret.value = m[0];
    debug('modifier %o', ret);
    return ret;
  }

  /**
   * Match a generic number function argument.
   *
   * @return {Object or Undefined}
   */

  function number () {
    var m = match(/^([^\)\s]+)/);
    if (!m) return;
    var ret = {};
    ret.type = 'number';
    ret.value = m[0];
    debug('number %o', ret);
    return ret;
  }

  /**
   * Match a function's arguments.
   *
   * @return {Array}
   */

  function args () {
    var ret = [];
    var el;
    while (el = modifier() || fn() || number()) {
      ret.push(el);
      whitespace();
    }
    debug('args %o', ret);
    return ret;
  }

  /**
   * Match an adjuster function.
   *
   * @return {Object or Undefined}
   */

  function adjuster () {
    var m = match(/^(\w+)\(/);
    if (!m) return;
    whitespace();
    var el;
    var ret = {};
    ret.type = 'function';
    ret.name = m[0];
    ret.arguments = args();
    rparen()
    debug('adjuster %o', ret);
    return ret;
  }

  /**
   * Match a color.
   *
   * @return {Object}
   */

  function color () {
    var ret = {};
    ret.type = 'color';

    var col = match(/([^\)\s]+)/)[0];
    if (col.indexOf('(') != -1) {
      var piece = match(/([^\)]*?\))/)[0];
      col = col + piece;
    }

    ret.value = col;
    whitespace();
    return ret;
  }

  /**
   * Match a color function, capturing the first color argument and any adjuster
   * functions after it.
   *
   * @return {Object or Undefined}
   */

  function fn () {
    if (!string.match(/^color\(/)) return;

    var colorRef = balanced('(', ')', string)
    if (!colorRef) throw new SyntaxError('Missing closing parenthese for \'' + string + '\'');
    if (colorRef.body === '') throw new SyntaxError('color() function cannot be empty');
    string = colorRef.body
    whitespace();

    var ret = {};
    ret.type = 'function';
    ret.name = 'color';
    ret.arguments = [fn() || color()];
    debug('function arguments %o', ret.arguments);

    var el;
    while (el = adjuster()) {
      ret.arguments.push(el);
      whitespace();
    }

    // pass the rest of the string in case of recursive color()
    string = colorRef.post
    whitespace();
    debug('function %o', ret);

    return ret;
  }

  /**
   * Return the parsed color function.
   */

  return fn();
}