text: 'use strict';
  
  /* eslint-disable max-params */
  
  var trim = require('trim');
  var repeat = require('repeat-string');
  var decimal = require('is-decimal');
  var getIndent = require('../util/get-indentation');
  var removeIndent = require('../util/remove-indentation');
  var interrupt = require('../util/interrupt');
  
  module.exports = list;
  
  var C_ASTERISK = '*';
  var C_UNDERSCORE = '_';
  var C_PLUS = '+';
  var C_DASH = '-';
  var C_DOT = '.';
  var C_SPACE = ' ';
  var C_NEWLINE = '\n';
  var C_TAB = '\t';
  var C_PAREN_CLOSE = ')';
  var C_X_LOWER = 'x';
  
  var TAB_SIZE = 4;
  var EXPRESSION_LOOSE_LIST_ITEM = /\n\n(?!\s*$)/;
  var EXPRESSION_TASK_ITEM = /^\[([ \t]|x|X)][ \t]/;
  var EXPRESSION_BULLET = /^([ \t]*)([*+-]|\d+[.)])( {1,4}(?! )| |\t|$|(?=\n))([^\n]*)/;
  var EXPRESSION_PEDANTIC_BULLET = /^([ \t]*)([*+-]|\d+[.)])([ \t]+)/;
  var EXPRESSION_INITIAL_INDENT = /^( {1,4}|\t)?/gm;
  
  /* Map of characters which can be used to mark
   * list-items. */
  var LIST_UNORDERED_MARKERS = {};
  
  LIST_UNORDERED_MARKERS[C_ASTERISK] = true;
  LIST_UNORDERED_MARKERS[C_PLUS] = true;
  LIST_UNORDERED_MARKERS[C_DASH] = true;
  
  /* Map of characters which can be used to mark
   * list-items after a digit. */
  var LIST_ORDERED_MARKERS = {};
  
  LIST_ORDERED_MARKERS[C_DOT] = true;
  
  /* Map of characters which can be used to mark
   * list-items after a digit. */
  var LIST_ORDERED_COMMONMARK_MARKERS = {};
  
  LIST_ORDERED_COMMONMARK_MARKERS[C_DOT] = true;
  LIST_ORDERED_COMMONMARK_MARKERS[C_PAREN_CLOSE] = true;
  
  function list(eat, value, silent) {
    var self = this;
    var commonmark = self.options.commonmark;
    var pedantic = self.options.pedantic;
    var tokenizers = self.blockTokenizers;
    var interuptors = self.interruptList;
    var markers;
    var index = 0;
    var length = value.length;
    var start = null;
    var size = 0;
    var queue;
    var ordered;
    var character;
    var marker;
    var nextIndex;
    var startIndex;
    var prefixed;
    var currentMarker;
    var content;
    var line;
    var prevEmpty;
    var empty;
    var items;
    var allLines;
    var emptyLines;
    var item;
    var enterTop;
    var exitBlockquote;
    var isLoose;
    var node;
    var now;
    var end;
    var indented;
  
    while (index < length) {
      character = value.charAt(index);
  
      if (character === C_TAB) {
        size += TAB_SIZE - (size % TAB_SIZE);
      } else if (character === C_SPACE) {
        size++;
      } else {
        break;
      }
  
      index++;
    }
  
    if (size >= TAB_SIZE) {
      return;
    }
  
    character = value.charAt(index);
  
    markers = commonmark ?
      LIST_ORDERED_COMMONMARK_MARKERS :
      LIST_ORDERED_MARKERS;
  
    if (LIST_UNORDERED_MARKERS[character] === true) {
      marker = character;
      ordered = false;
    } else {
      ordered = true;
      queue = '';
  
      while (index < length) {
        character = value.charAt(index);
  
        if (!decimal(character)) {
          break;
        }
  
        queue += character;
        index++;
      }
  
      character = value.charAt(index);
  
      if (!queue || markers[character] !== true) {
        return;
      }
  
      start = parseInt(queue, 10);
      marker = character;
    }
  
    character = value.charAt(++index);
  
    if (character !== C_SPACE && character !== C_TAB) {
      return;
    }
  
    if (silent) {
      return true;
    }
  
    index = 0;
    items = [];
    allLines = [];
    emptyLines = [];
  
    while (index < length) {
      nextIndex = value.indexOf(C_NEWLINE, index);
      startIndex = index;
      prefixed = false;
      indented = false;
  
      if (nextIndex === -1) {
        nextIndex = length;
      }
  
      end = index + TAB_SIZE;
      size = 0;
  
      while (index < length) {
        character = value.charAt(index);
  
        if (character === C_TAB) {
          size += TAB_SIZE - (size % TAB_SIZE);
        } else if (character === C_SPACE) {
          size++;
        } else {
          break;
        }
  
        index++;
      }
  
      if (size >= TAB_SIZE) {
        indented = true;
      }
  
      if (item && size >= item.indent) {
        indented = true;
      }
  
      character = value.charAt(index);
      currentMarker = null;
  
      if (!indented) {
        if (LIST_UNORDERED_MARKERS[character] === true) {
          currentMarker = character;
          index++;
          size++;
        } else {
          queue = '';
  
          while (index < length) {
            character = value.charAt(index);
  
            if (!decimal(character)) {
              break;
            }
  
            queue += character;
            index++;
          }
  
          character = value.charAt(index);
          index++;
  
          if (queue && markers[character] === true) {
            currentMarker = character;
            size += queue.length + 1;
          }
        }
  
        if (currentMarker) {
          character = value.charAt(index);
  
          if (character === C_TAB) {
            size += TAB_SIZE - (size % TAB_SIZE);
            index++;
          } else if (character === C_SPACE) {
            end = index + TAB_SIZE;
  
            while (index < end) {
              if (value.charAt(index) !== C_SPACE) {
                break;
              }
  
              index++;
              size++;
            }
  
            if (index === end && value.charAt(index) === C_SPACE) {
              index -= TAB_SIZE - 1;
              size -= TAB_SIZE - 1;
            }
          } else if (character !== C_NEWLINE && character !== '') {
            currentMarker = null;
          }
        }
      }
  
      if (currentMarker) {
        if (!pedantic && marker !== currentMarker) {
          break;
        }
  
        prefixed = true;
      } else {
        if (!commonmark && !indented && value.charAt(startIndex) === C_SPACE) {
          indented = true;
        } else if (commonmark && item) {
          indented = size >= item.indent || size > TAB_SIZE;
        }
  
        prefixed = false;
        index = startIndex;
      }
  
      line = value.slice(startIndex, nextIndex);
      content = startIndex === index ? line : value.slice(index, nextIndex);
  
      if (
        currentMarker === C_ASTERISK ||
        currentMarker === C_UNDERSCORE ||
        currentMarker === C_DASH
      ) {
        if (tokenizers.thematicBreak.call(self, eat, line, true)) {
          break;
        }
      }
  
      prevEmpty = empty;
      empty = !trim(content).length;
  
      if (indented && item) {
        item.value = item.value.concat(emptyLines, line);
        allLines = allLines.concat(emptyLines, line);
        emptyLines = [];
      } else if (prefixed) {
        if (emptyLines.length !== 0) {
          item.value.push('');
          item.trail = emptyLines.concat();
        }
  
        item = {
          value: [line],
          indent: size,
          trail: []
        };
  
        items.push(item);
        allLines = allLines.concat(emptyLines, line);
        emptyLines = [];
      } else if (empty) {
        if (prevEmpty) {
          break;
        }
  
        emptyLines.push(line);
      } else {
        if (prevEmpty) {
          break;
        }
  
        if (interrupt(interuptors, tokenizers, self, [eat, line, true])) {
          break;
        }
  
        item.value = item.value.concat(emptyLines, line);
        allLines = allLines.concat(emptyLines, line);
        emptyLines = [];
      }
  
      index = nextIndex + 1;
    }
  
    node = eat(allLines.join(C_NEWLINE)).reset({
      type: 'list',
      ordered: ordered,
      start: start,
      loose: null,
      children: []
    });
  
    enterTop = self.enterList();
    exitBlockquote = self.enterBlock();
    isLoose = false;
    index = -1;
    length = items.length;
  
    while (++index < length) {
      item = items[index].value.join(C_NEWLINE);
      now = eat.now();
  
      item = eat(item)(listItem(self, item, now), node);
  
      if (item.loose) {
        isLoose = true;
      }
  
      item = items[index].trail.join(C_NEWLINE);
  
      if (index !== length - 1) {
        item += C_NEWLINE;
      }
  
      eat(item);
    }
  
    enterTop();
    exitBlockquote();
  
    node.loose = isLoose;
  
    return node;
  }
  
  function listItem(ctx, value, position) {
    var offsets = ctx.offset;
    var fn = ctx.options.pedantic ? pedanticListItem : normalListItem;
    var checked = null;
    var task;
    var indent;
  
    value = fn.apply(null, arguments);
  
    if (ctx.options.gfm) {
      task = value.match(EXPRESSION_TASK_ITEM);
  
      if (task) {
        indent = task[0].length;
        checked = task[1].toLowerCase() === C_X_LOWER;
        offsets[position.line] += indent;
        value = value.slice(indent);
      }
    }
  
    return {
      type: 'listItem',
      loose: EXPRESSION_LOOSE_LIST_ITEM.test(value) ||
        value.charAt(value.length - 1) === C_NEWLINE,
      checked: checked,
      children: ctx.tokenizeBlock(value, position)
    };
  }
  
  /* Create a list-item using overly simple mechanics. */
  function pedanticListItem(ctx, value, position) {
    var offsets = ctx.offset;
    var line = position.line;
  
    /* Remove the list-item’s bullet. */
    value = value.replace(EXPRESSION_PEDANTIC_BULLET, replacer);
  
    /* The initial line was also matched by the below, so
     * we reset the `line`. */
    line = position.line;
  
    return value.replace(EXPRESSION_INITIAL_INDENT, replacer);
  
    /* A simple replacer which removed all matches,
     * and adds their length to `offset`. */
    function replacer($0) {
      offsets[line] = (offsets[line] || 0) + $0.length;
      line++;
  
      return '';
    }
  }
  
  /* Create a list-item using sane mechanics. */
  function normalListItem(ctx, value, position) {
    var offsets = ctx.offset;
    var line = position.line;
    var max;
    var bullet;
    var rest;
    var lines;
    var trimmedLines;
    var index;
    var length;
  
    /* Remove the list-item’s bullet. */
    value = value.replace(EXPRESSION_BULLET, replacer);
  
    lines = value.split(C_NEWLINE);
  
    trimmedLines = removeIndent(value, getIndent(max).indent).split(C_NEWLINE);
  
    /* We replaced the initial bullet with something
     * else above, which was used to trick
     * `removeIndentation` into removing some more
     * characters when possible.  However, that could
     * result in the initial line to be stripped more
     * than it should be. */
    trimmedLines[0] = rest;
  
    offsets[line] = (offsets[line] || 0) + bullet.length;
    line++;
  
    index = 0;
    length = lines.length;
  
    while (++index < length) {
      offsets[line] = (offsets[line] || 0) +
        lines[index].length - trimmedLines[index].length;
      line++;
    }
  
    return trimmedLines.join(C_NEWLINE);
  
    function replacer($0, $1, $2, $3, $4) {
      bullet = $1 + $2 + $3;
      rest = $4;
  
      /* Make sure that the first nine numbered list items
       * can indent with an extra space.  That is, when
       * the bullet did not receive an extra final space. */
      if (Number($2) < 10 && bullet.length % 2 === 1) {
        $2 = C_SPACE + $2;
      }
  
      max = $1 + repeat(C_SPACE, $2.length) + $3;
  
      return max + rest;
    }
  }
  