///|
fn require_color_argument(
  args : Array[ParseNode],
  func_name : String,
) -> String raise ParseFailure {
  require_color_argument_at(args, 0, func_name)
}

///|
fn require_color_argument_at(
  args : Array[ParseNode],
  index : Int,
  func_name : String,
) -> String raise ParseFailure {
  guard require_function_arg(args, index, func_name) is ColorToken(color~, ..) else {
    raise InternalInvariant(message="Expected color argument for " + func_name)
  }
  color
}

///|
fn textcolor_spec() -> FunctionSpec {
  FunctionSpec::make(
    ["\\textcolor"],
    2,
    arg_types=[ColorArg, OriginalArg],
    allowed_in_text=true,
    handler=textcolor_handler,
  )
}

///|
fn textcolor_handler(
  context : FunctionContext,
  args : Array[ParseNode],
  _opt_args : Array[ParseNode?],
) -> ParseNode raise ParseFailure {
  Color(
    mode=context.mode,
    color=require_color_argument(args, context.func_name),
    body=ord_argument(require_function_arg(args, 1, context.func_name)),
  )
}

///|
fn color_spec() -> FunctionSpec {
  FunctionSpec::make(
    ["\\color"],
    1,
    arg_types=[ColorArg],
    allowed_in_text=true,
    handler=color_handler,
  )
}

///|
fn color_handler(
  context : FunctionContext,
  args : Array[ParseNode],
  _opt_args : Array[ParseNode?],
) -> ParseNode raise ParseFailure {
  let color = require_color_argument(args, context.func_name)
  (context.set_macro)("\\current@color", Some(MacroDefinition::text(color)))
  Color(
    mode=context.mode,
    color~,
    body=(context.parse_expression)(true, context.break_on_token_text),
  )
}