///|
type Parser

///|
extern "js" fn ts_parser_new(ts : TS) -> Parser =
  #|(ts) => {
  #|  return new ts.Parser();
  #|}

///|
/// Create a new parser.
pub fn Parser::new() -> Parser {
  ts_parser_new(ts)
}

///|
pub fn parser(language : Language) -> Parser raise LanguageError {
  let parser = Parser::new()
  parser.set_language(language)
  return parser
}

///|
extern "js" fn ts_parser_language(parser : Parser) -> Language =
  #|(parser) => {
  #|  return parser.language;
  #|}

///|
/// Get the parser's current language.
pub fn Parser::language(self : Parser) -> Language? {
  ts_parser_language(self).to_option()
}

///|
extern "js" fn ts_parser_set_language(
  parser : Parser,
  language : Language,
) -> Bool =
  #|(parser, language) => {
  #|  try {
  #|    parser.setLanguage(language);
  #|    return true;
  #|  } catch (error) {
  #|    console.error("Error setting parser language:", error);
  #|    return false;
  #|  }
  #|}

///|
suberror LanguageError {
  VersionMismatch(
    language_abi_version~ : Int,
    library_version~ : Int,
    min_compatible_version~ : Int
  )
}

///|
/// Set the language that the parser should use for parsing.
///
/// Returns a boolean indicating whether or not the language was successfully
/// assigned. True means assignment succeeded. False means there was a version
/// mismatch: the language was generated with an incompatible version of the
/// Tree-sitter CLI. Check the language's ABI version using Language::abi_version
/// and compare it to this library's `LANGUAGE_VERSION` and
/// `MIN_COMPATIBLE_LANGUAGE_VERSION` constants.
pub fn Parser::set_language(
  self : Parser,
  language : Language,
) -> Unit raise LanguageError {
  let succeed = ts_parser_set_language(self, language)
  if not(succeed) {
    raise VersionMismatch(
      language_abi_version=language.abi_version(),
      library_version=LANGUAGE_VERSION,
      min_compatible_version=MIN_COMPATIBLE_LANGUAGE_VERSION,
    )
  }
}

///|
/// The latest ABI version that is supported by the current version of the
/// library. When Languages are generated by the Tree-sitter CLI, they are
/// assigned an ABI version number that corresponds to the current CLI version.
/// The Tree-sitter library is generally backwards-compatible with languages
/// generated using older CLI versions, but is not forwards-compatible.
pub const LANGUAGE_VERSION = 15

///|
///
/// The earliest ABI version that is supported by the current version of the
/// library.
pub const MIN_COMPATIBLE_LANGUAGE_VERSION = 13

///|
extern "js" fn ts_parser_included_ranges(parser : Parser) -> Array[Range] =
  #|(parser) => {
  #|  return parser.getIncludedRanges();
  #|}

///|
/// Get the ranges of text that the parser will include when parsing.
pub fn Parser::included_ranges(self : Parser) -> Array[Range] {
  ts_parser_included_ranges(self)
}

///|
pub suberror ParseError {
  MissingLanguage
  Cancelled
} derive(Show)

///|
fn Parser::raise_parse_error(
  self : Parser,
  tree : Tree?,
) -> Tree raise ParseError {
  match tree {
    None =>
      match self.language() {
        None => raise MissingLanguage
        Some(_) => raise Cancelled
      }
    Some(tree) => return tree
  }
}

///|
extern "js" fn ts_parser_parse_string(
  parser : Parser,
  string : String,
  old_tree : Tree,
) -> Tree =
  #|(parser, string, oldTree) => {
  #|  return parser.parse(string, oldTree);
  #|}

///|
/// Use the parser to parse some source code stored in one contiguous string buffer.
/// The first two parameters are the same as in the `parse` function. The final
/// parameter is the string to parse.
pub fn Parser::parse_string(
  self : Parser,
  old_tree? : Tree,
  string : StringView,
) -> Tree raise ParseError {
  let old_ts_tree = match old_tree {
    None => ts_tree_null()
    Some(tree) => tree
  }
  let tree = ts_parser_parse_string(self, string.to_string(), old_ts_tree).to_option()
  self.raise_parse_error(tree)
}

///|
extern "js" fn ts_parser_reset(parser : Parser) =
  #|(parser) => {
  #|  return parser.reset();
  #|}

///|
/// Instruct the parser to start the next parse from the beginning.
///
/// If the parser previously failed because of a timeout or a cancellation, then
/// by default, it will resume where it left off on the next call to
/// `parse` or other parsing functions. If you don't want to resume,
/// and instead intend to use this parser to parse some other document, you must
/// call `reset` first.
pub fn Parser::reset(self : Parser) -> Unit {
  ts_parser_reset(self)
}

///|
pub enum LogType {
  Parse
  Lex
}

///|
struct Logger((String, Bool) -> Unit)

///|
pub fn Logger::new(log : (LogType, StringView) -> Unit) -> Logger {
  fn(message, isLex) {
    let logType = if isLex { LogType::Lex } else { LogType::Parse }
    log(logType, message)
  }
}

///|
extern "js" fn ts_parser_set_logger(
  parser : Parser,
  logger : (String, Bool) -> Unit,
) =
  #|(parser, logger) => {
  #|  return parser.setLogger(logger);
  #|}

///|
/// Set the logger that a parser should use during parsing.
///
/// The parser does not take ownership over the logger payload. If a logger was
/// previously assigned, the caller is responsible for releasing any memory
/// owned by the previous logger.
pub fn Parser::set_logger(self : Parser, logger : Logger) -> Unit {
  ts_parser_set_logger(self, logger.0)
}

///|
extern "js" fn ts_parser_logger(parser : Parser) -> (String, Bool) -> Unit =
  #|(parser) => {
  #|  return parser.getLogger();
  #|}

///|
/// Get the parser's current logger.
pub fn Parser::logger(self : Parser) -> Logger {
  ts_parser_logger(self)
}