/**
 * External scanner for Storybook Tree-sitter grammar
 *
 * This scanner handles prose blocks (---tag content ---) which require
 * stateful scanning to properly match opening and closing delimiters.
 */

#include <tree_sitter/parser.h>
#include <wctype.h>
#include <string.h>

enum TokenType {
  PROSE_BLOCK_CONTENT,
  PROSE_BLOCK_END,
};

typedef struct {
  bool in_prose_block;
} Scanner;

void *tree_sitter_storybook_external_scanner_create() {
  Scanner *scanner = (Scanner *)malloc(sizeof(Scanner));
  scanner->in_prose_block = false;
  return scanner;
}

void tree_sitter_storybook_external_scanner_destroy(void *payload) {
  Scanner *scanner = (Scanner *)payload;
  free(scanner);
}

unsigned tree_sitter_storybook_external_scanner_serialize(
  void *payload,
  char *buffer
) {
  Scanner *scanner = (Scanner *)payload;
  buffer[0] = scanner->in_prose_block ? 1 : 0;
  return 1;
}

void tree_sitter_storybook_external_scanner_deserialize(
  void *payload,
  const char *buffer,
  unsigned length
) {
  Scanner *scanner = (Scanner *)payload;
  scanner->in_prose_block = (length > 0 && buffer[0] == 1);
}

/**
 * Check if we're at the start of a line (only whitespace before)
 */
static bool at_line_start(TSLexer *lexer) {
  // Look back to see if there's only whitespace since last newline
  // For simplicity, we'll assume --- at start of content is line-start
  return true;  // Tree-sitter will handle this check via grammar
}

/**
 * Scan prose block content
 *
 * Returns true if we successfully scanned content, false otherwise
 */
static bool scan_prose_content(Scanner *scanner, TSLexer *lexer) {
  bool has_content = false;

  // Consume characters until we see --- at line start
  for (;;) {
    // Check for end-of-file
    if (lexer->eof(lexer)) {
      // If we have content, return it; otherwise error
      if (has_content) {
        lexer->result_symbol = PROSE_BLOCK_CONTENT;
        scanner->in_prose_block = false;
        return true;
      }
      return false;
    }

    // Check for closing ---
    if (lexer->lookahead == '-') {
      // Look ahead to see if it's ---
      lexer->mark_end(lexer);
      lexer->advance(lexer, false);

      if (lexer->lookahead == '-') {
        lexer->advance(lexer, false);
        if (lexer->lookahead == '-') {
          // Found closing ---, stop here
          // Mark end before the ---
          scanner->in_prose_block = false;
          if (has_content) {
            lexer->result_symbol = PROSE_BLOCK_CONTENT;
            return true;
          }
          // No content, scan the closing marker
          lexer->result_symbol = PROSE_BLOCK_END;
          lexer->advance(lexer, false);  // Consume final -
          return true;
        }
      }
      // Not a closing marker, continue with content
      has_content = true;
      continue;
    }

    // Regular content character
    lexer->advance(lexer, false);
    has_content = true;
  }
}

/**
 * Scan for closing prose block marker (---)
 */
static bool scan_prose_end(Scanner *scanner, TSLexer *lexer) {
  // We should be at ---
  if (lexer->lookahead == '-') {
    lexer->advance(lexer, false);
    if (lexer->lookahead == '-') {
      lexer->advance(lexer, false);
      if (lexer->lookahead == '-') {
        lexer->advance(lexer, false);
        lexer->result_symbol = PROSE_BLOCK_END;
        scanner->in_prose_block = false;
        return true;
      }
    }
  }
  return false;
}

bool tree_sitter_storybook_external_scanner_scan(
  void *payload,
  TSLexer *lexer,
  const bool *valid_symbols
) {
  Scanner *scanner = (Scanner *)payload;

  // Skip whitespace at the start
  while (iswspace(lexer->lookahead)) {
    lexer->advance(lexer, true);
  }

  // If we're looking for prose content
  if (valid_symbols[PROSE_BLOCK_CONTENT]) {
    scanner->in_prose_block = true;
    return scan_prose_content(scanner, lexer);
  }

  // If we're looking for prose end
  if (valid_symbols[PROSE_BLOCK_END]) {
    return scan_prose_end(scanner, lexer);
  }

  return false;
}
