// 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';

abstract class BlockSyntax {
  const BlockSyntax();

  /// Gets the regex used to identify the beginning of this block, if any.
  RegExp get pattern;

  bool canEndBlock(BlockParser parser) => true;

  bool canParse(BlockParser parser) {
    return pattern.hasMatch(parser.current.content);
  }

  Node? parse(BlockParser parser);

  List<Line?> parseChildLines(BlockParser parser) {
    // Grab all of the lines that form the block element.
    final childLines = <Line?>[];

    while (!parser.isDone) {
      final match = pattern.firstMatch(parser.current.content);
      if (match == null) break;
      childLines.add(parser.current);
      parser.advance();
    }

    return childLines;
  }

  /// Returns the block which interrupts current syntax parsing if there is one,
  /// otherwise returns `null`.
  ///
  /// Make sure to check if [parser] `isDone` is `false` first.
  BlockSyntax? interruptedBy(BlockParser parser) {
    for (final syntax in parser.blockSyntaxes) {
      if (syntax.canParse(parser) && syntax.canEndBlock(parser)) {
        return syntax;
      }
    }
    return null;
  }

  /// Gets whether or not [parser]'s current line should end the previous block.
  static bool isAtBlockEnd(BlockParser parser) {
    if (parser.isDone) return true;
    return parser.blockSyntaxes
        .any((s) => s.canParse(parser) && s.canEndBlock(parser));
  }

  /// Generates a valid HTML anchor from the inner text of [element].
  static String generateAnchorHash(Element element) =>
      element.children!.first.textContent
          .toLowerCase()
          .trim()
          .replaceAll(RegExp('[^a-z0-9 _-]'), '')
          .replaceAll(RegExp(r'\s'), '-');
}
