var zip = require('lodash/zip');
var trimEnd = require('lodash/trimEnd');

// Split a long word up to fit within the word wrap limit.  Use either a
// character to split looking back from the word wrap limit, or
// truncate to the word wrap limit.
function splitLongWord(word, options) {
  var wrapCharacters = options.longWordSplit.wrapCharacters || [];
  var forceWrapOnLimit = options.longWordSplit.forceWrapOnLimit || false;
  var max = options.wordwrap;

  var fuseWord = [];
  var idx = 0;
  while (word.length > max) {
    var firstLine = word.substr(0, max);
    var remainingChars = word.substr(max);

    var splitIndex = firstLine.lastIndexOf(wrapCharacters[idx]);

    if (splitIndex > -1) {
      // We've found a character to split on, store before the split then check if we
      // need to split again
      word = firstLine.substr(splitIndex + 1) + remainingChars;
      fuseWord.push(firstLine.substr(0, splitIndex + 1));
    } else {
      idx++;
      if (idx >= wrapCharacters.length) {
        // Cannot split on character, so either split at 'max' or preserve length
        if (forceWrapOnLimit) {
          fuseWord.push(firstLine);
          word = remainingChars;
          if (word.length > max) {
            continue;
          }
        } else {
          word = firstLine + remainingChars;
          if (!options.preserveNewlines) {
            word += '\n';
          }
        }
        break;
      } else {
        word = firstLine + remainingChars;
      }
    }
  }
  fuseWord.push(word);

  return fuseWord.join('\n');
}

exports.wordwrap = function wordwrap(text, options) {
  var max = options.wordwrap;
  var preserveNewlines = options.preserveNewlines;
  var length = options.lineCharCount;

  // Preserve leading space
  var result = text.startsWith(' ') ? ' ' : '';
  length += result.length;
  var buffer = [];
  // Split the text into words, decide to preserve new lines or not.
  var words = preserveNewlines
    ? text.replace(/\n/g, '\n ').split(/\ +/)
    : text.trim().split(/\s+/);

  // Determine where to end line word by word.
  words.forEach(function(word) {
    // Add buffer to result if we can't fit any more words in the buffer.
    if ((max || max === 0) && length > 0 && ((length + word.length > max) || (length + word.indexOf('\n') > max))) {
      // Concat buffer and add it to the result
      result += buffer.join(' ') + '\n';
      // Reset buffer and length
      buffer.length = length = 0;
    }

    // Check if the current word is long enough to be wrapped
    if ((max || max === 0) && (options.longWordSplit) && (word.length > max)) {
      word = splitLongWord(word, options);
    }

    buffer.push(word);

    // If the word contains a newline then restart the count and add the buffer to the result
    if (word.indexOf('\n') !== -1) {
      result += buffer.join(' ');

      // Reset the buffer, let the length include any characters after the last newline
      buffer.length = 0;
      length = word.length - (word.lastIndexOf('\n') + 1);
      // If there are characters after the newline, add a space and increase the length by 1
      if (length) {
        result += ' ';
        length++;
      }
    } else {
      // Add word length + one whitespace
      length += word.length + 1;
    }
  });
  // Add the rest to the result.
  result += buffer.join(' ');

  // Preserve trailing space
  if (!text.endsWith(' ')) {
    result = trimEnd(result);
  } else if (!result.endsWith(' ')) {
    result = result + ' ';
  }

  return result;
};

exports.arrayZip = function arrayZip(array) {
  return zip.apply(null, array);
};

exports.splitCssSearchTag = function splitCssSearchTag(tagString) {
  function getParams(re, string) {
    var captures = [], found;
    while ((found = re.exec(string)) !== null) {
      captures.push(found[1]);
    }
    return captures;
  }

  var splitTag = {};
  var elementRe = /(^\w*)/g;
  splitTag.element = elementRe.exec(tagString)[1];
  splitTag.classes = getParams( /\.([\d\w-]*)/g, tagString);
  splitTag.ids = getParams( /#([\d\w-]*)/g, tagString);

  return splitTag;
};

exports.replaceAll = function replaceAll(str, find, replace) {
  var reg = new RegExp(find, 'g');

  return str.replace(reg, replace);
};
