/* eslint-disable no-plusplus, no-continue */
const foregroundColors = {
  '30': 'black',
  '31': 'red',
  '32': 'green',
  '33': 'yellow',
  '34': 'blue',
  '35': 'magenta',
  '36': 'cyan',
  '37': 'white',
  '90': 'grey',
};
const backgroundColors = {
  '40': 'black',
  '41': 'red',
  '42': 'green',
  '43': 'yellow',
  '44': 'blue',
  '45': 'magenta',
  '46': 'cyan',
  '47': 'white',
};
const styles = {
  '1': 'bold',
  '3': 'italic',
  '4': 'underline',
};
const eraseChar = (matchingText, result) => {
  if (matchingText.length) {
    return [matchingText.substr(0, matchingText.length - 1), result];
  } else if (result.length) {
    const index = result.length - 1;
    const { text } = result[index];
    const newResult =
      text.length === 1
        ? result.slice(0, result.length - 1)
        : result.map((item, i) =>
            index === i
              ? { ...item, text: text.substr(0, text.length - 1) }
              : item
          );

    return [matchingText, newResult];
  }

  return [matchingText, result];
};

const ansiparse = str => {
  let matchingControl = null;
  let matchingData = null;
  let matchingText = '';
  let ansiState = [];
  let result = [];
  let state = {};

  for (let i = 0; i < str.length; i++) {
    if (matchingControl !== null) {
      if (matchingControl === '\x1b' && str[i] === '[') {
        if (matchingText) {
          state.text = matchingText;
          result.push(state);
          state = {};
          matchingText = '';
        }

        matchingControl = null;
        matchingData = '';
      } else {
        matchingText += matchingControl + str[i];
        matchingControl = null;
      }

      continue;
    } else if (matchingData !== null) {
      if (str[i] === ';') {
        ansiState.push(matchingData);
        matchingData = '';
      } else if (str[i] === 'm') {
        ansiState.push(matchingData);
        matchingData = null;
        matchingText = '';

        for (let a = 0; a < ansiState.length; a++) {
          const ansiCode = ansiState[a];

          if (foregroundColors[ansiCode]) {
            state.foreground = foregroundColors[ansiCode];
          } else if (backgroundColors[ansiCode]) {
            state.background = backgroundColors[ansiCode];
          } else if (ansiCode === 39) {
            delete state.foreground;
          } else if (ansiCode === 49) {
            delete state.background;
          } else if (styles[ansiCode]) {
            state[styles[ansiCode]] = true;
          } else if (ansiCode === 22) {
            state.bold = false;
          } else if (ansiCode === 23) {
            state.italic = false;
          } else if (ansiCode === 24) {
            state.underline = false;
          }
        }

        ansiState = [];
      } else {
        matchingData += str[i];
      }

      continue;
    }

    if (str[i] === '\x1b') {
      matchingControl = str[i];
    } else if (str[i] === '\u0008') {
      [matchingText, result] = eraseChar(matchingText, result);
    } else {
      matchingText += str[i];
    }
  }

  if (matchingText) {
    state.text = matchingText + (matchingControl || '');
    result.push(state);
  }

  return result;
};

export default ansiparse;
