text: var Hack = require('./hack');
  
  var Marker = require('../tokenizer/marker');
  var Token = require('../tokenizer/token');
  
  var Match = {
    ASTERISK: '*',
    BACKSLASH: '\\',
    BANG: '!',
    BANG_SUFFIX_PATTERN: /!\w+$/,
    IMPORTANT_TOKEN: '!important',
    IMPORTANT_TOKEN_PATTERN: new RegExp('!important$', 'i'),
    IMPORTANT_WORD: 'important',
    IMPORTANT_WORD_PATTERN: new RegExp('important$', 'i'),
    SUFFIX_BANG_PATTERN: /!$/,
    UNDERSCORE: '_',
    VARIABLE_REFERENCE_PATTERN: /var\(--.+\)$/
  };
  
  function wrapAll(properties, includeVariable) {
    var wrapped = [];
    var single;
    var property;
    var i;
  
    for (i = properties.length - 1; i >= 0; i--) {
      property = properties[i];
  
      if (property[0] != Token.PROPERTY) {
        continue;
      }
  
      if (!includeVariable && someVariableReferences(property)) {
        continue;
      }
  
      single = wrapSingle(property);
      single.all = properties;
      single.position = i;
      wrapped.unshift(single);
    }
  
    return wrapped;
  }
  
  function someVariableReferences(property) {
    var i, l;
    var value;
  
    // skipping `property` and property name tokens
    for (i = 2, l = property.length; i < l; i++) {
      value = property[i];
  
      if (value[0] != Token.PROPERTY_VALUE) {
        continue;
      }
  
      if (isVariableReference(value[1])) {
        return true;
      }
    }
  
    return false;
  }
  
  function isVariableReference(value) {
    return Match.VARIABLE_REFERENCE_PATTERN.test(value);
  }
  
  function isMultiplex(property) {
    var value;
    var i, l;
  
    for (i = 3, l = property.length; i < l; i++) {
      value = property[i];
  
      if (value[0] == Token.PROPERTY_VALUE && (value[1] == Marker.COMMA || value[1] == Marker.FORWARD_SLASH)) {
        return true;
      }
    }
  
    return false;
  }
  
  function hackFrom(property) {
    var match = false;
    var name = property[1][1];
    var lastValue = property[property.length - 1];
  
    if (name[0] == Match.UNDERSCORE) {
      match = [Hack.UNDERSCORE];
    } else if (name[0] == Match.ASTERISK) {
      match = [Hack.ASTERISK];
    } else if (lastValue[1][0] == Match.BANG && !lastValue[1].match(Match.IMPORTANT_WORD_PATTERN)) {
      match = [Hack.BANG];
    } else if (lastValue[1].indexOf(Match.BANG) > 0 && !lastValue[1].match(Match.IMPORTANT_WORD_PATTERN) && Match.BANG_SUFFIX_PATTERN.test(lastValue[1])) {
      match = [Hack.BANG];
    } else if (lastValue[1].indexOf(Match.BACKSLASH) > 0 && lastValue[1].indexOf(Match.BACKSLASH) == lastValue[1].length - Match.BACKSLASH.length - 1) {
      match = [Hack.BACKSLASH, lastValue[1].substring(lastValue[1].indexOf(Match.BACKSLASH) + 1)];
    } else if (lastValue[1].indexOf(Match.BACKSLASH) === 0 && lastValue[1].length == 2) {
      match = [Hack.BACKSLASH, lastValue[1].substring(1)];
    }
  
    return match;
  }
  
  function isImportant(property) {
    if (property.length < 3)
      return false;
  
    var lastValue = property[property.length - 1];
    if (Match.IMPORTANT_TOKEN_PATTERN.test(lastValue[1])) {
      return true;
    } else if (Match.IMPORTANT_WORD_PATTERN.test(lastValue[1]) && Match.SUFFIX_BANG_PATTERN.test(property[property.length - 2][1])) {
      return true;
    }
  
    return false;
  }
  
  function stripImportant(property) {
    var lastValue = property[property.length - 1];
    var oneButLastValue = property[property.length - 2];
  
    if (Match.IMPORTANT_TOKEN_PATTERN.test(lastValue[1])) {
      lastValue[1] = lastValue[1].replace(Match.IMPORTANT_TOKEN_PATTERN, '');
    } else {
      lastValue[1] = lastValue[1].replace(Match.IMPORTANT_WORD_PATTERN, '');
      oneButLastValue[1] = oneButLastValue[1].replace(Match.SUFFIX_BANG_PATTERN, '');
    }
  
    if (lastValue[1].length === 0) {
      property.pop();
    }
  
    if (oneButLastValue[1].length === 0) {
      property.pop();
    }
  }
  
  function stripPrefixHack(property) {
    property[1][1] = property[1][1].substring(1);
  }
  
  function stripSuffixHack(property, hackFrom) {
    var lastValue = property[property.length - 1];
    lastValue[1] = lastValue[1]
      .substring(0, lastValue[1].indexOf(hackFrom[0] == Hack.BACKSLASH ? Match.BACKSLASH : Match.BANG))
      .trim();
  
    if (lastValue[1].length === 0) {
      property.pop();
    }
  }
  
  function wrapSingle(property) {
    var importantProperty = isImportant(property);
    if (importantProperty) {
      stripImportant(property);
    }
  
    var whichHack = hackFrom(property);
    if (whichHack[0] == Hack.ASTERISK || whichHack[0] == Hack.UNDERSCORE) {
      stripPrefixHack(property);
    } else if (whichHack[0] == Hack.BACKSLASH || whichHack[0] == Hack.BANG) {
      stripSuffixHack(property, whichHack);
    }
  
    return {
      block: property[2] && property[2][0] == Token.PROPERTY_BLOCK,
      components: [],
      dirty: false,
      hack: whichHack,
      important: importantProperty,
      name: property[1][1],
      multiplex: property.length > 3 ? isMultiplex(property) : false,
      position: 0,
      shorthand: false,
      unused: false,
      value: property.slice(2)
    };
  }
  
  module.exports = {
    all: wrapAll,
    single: wrapSingle
  };
  