// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import '../ast.dart';
import '../block_parser.dart';
import '../line.dart';
import '../patterns.dart';
import '../util.dart';
import 'block_syntax.dart';

/// Parses preformatted code blocks that are indented four spaces.
class CodeBlockSyntax extends BlockSyntax {
  @override
  RegExp get pattern => indentPattern;

  @override
  bool canEndBlock(BlockParser parser) => false;

  const CodeBlockSyntax();

  @override
  List<Line> parseChildLines(BlockParser parser) {
    final childLines = <Line>[];

    while (!parser.isDone) {
      final isBlankLine = parser.current.isBlankLine;
      if (isBlankLine && _shouldEnd(parser)) {
        break;
      }

      if (!isBlankLine &&
          childLines.isNotEmpty &&
          !pattern.hasMatch(parser.current.content)) {
        break;
      }

      childLines.add(Line(
        parser.current.content.dedent().text,
        tabRemaining: parser.current.tabRemaining,
      ));

      parser.advance();
    }

    return childLines;
  }

  @override
  Node parse(BlockParser parser) {
    final childLines = parseChildLines(parser);

    // The Markdown tests expect a trailing newline.
    childLines.add(Line(''));

    var content = childLines
        .map((e) => e.content.prependSpace(e.tabRemaining ?? 0))
        .join('\n');
    if (parser.document.encodeHtml) {
      content = escapeHtml(content, escapeApos: false);
    }

    return Element('pre', [Element.text('code', content)]);
  }

  bool _shouldEnd(BlockParser parser) {
    var i = 1;
    while (true) {
      final nextLine = parser.peek(i);
      // EOF
      if (nextLine == null) {
        return true;
      }

      // It does not matter how many blank lines between chunks:
      // https://spec.commonmark.org/0.30/#example-111
      if (nextLine.isBlankLine) {
        i++;
        continue;
      }

      return !pattern.hasMatch(nextLine.content);
    }
  }
}
