
# =============================================================================
# eval_operator: evaluate an operator or composition
# =============================================================================


# --- Helper: extract {specific} from "general {specific}" if present ---
extract_specific <- function(trans) {
  m <- regmatches(trans, regexpr("(?<=\\{)[^}]+(?=\\})", trans, perl = TRUE))
  if (length(m) > 0 && nchar(m) > 0) m else trans
}

# --- Helper: capitalize first letter and ensure trailing period ---
make_sentence <- function(s) {
  s <- sub("^(\\s*)(\\w)", "\\1\\U\\2", s, perl = TRUE)
  if (!endsWith(trimws(s), ".")) s <- paste0(s, ".")
  s
}

# --- Helper: remove <annotations> from translations ---
strip_annotations <- function(trans) {
  trans <- gsub("<[^>]*>", "", trans)
  trans <- gsub("\\s+", " ", trimws(trans))
  trans
}


# --- eval_operator: fully evaluate an operator or composition ---
#
# Contains the complete translation logic: argument processing (article
# insertion, verb-prefix stripping), placeholder substitution, and
# composition rules.
#
# @param operator Type string of the operator (e.g., "Sx->V", "SSx->V"),
#   or NULL for composition (no operator present).
# @param rule_translation Translation string with placeholders
#   (e.g., "to utilize S"), or NULL for composition.
# @param args List of c(name, translation, original_type) vectors:
#   - name: placeholder name derived from the base type (e.g., "S", "S1",
#     "V", "SEN2"). The base type is reconstructed by stripping the
#     trailing index.
#   - translation: resolved translation of the argument
#   - original_type: the full (possibly extended) type (e.g. "So", "Vt")
# @param seps Character vector of separators between elements
# @param trailing_sep Text after the last element
# @return c(result_type, result_translation) or error message (length 1)
eval_operator <- function(operator, rule_translation, args = list(), seps = "", trailing_sep = "") {

  # Apply extract_specific and strip_annotations to all translations upfront
  if (!is.null(rule_translation)) {
    rule_translation <- extract_specific(rule_translation)
    rule_translation <- strip_annotations(rule_translation)
  }
  for (i in seq_along(args)) {
    args[[i]][2] <- extract_specific(args[[i]][2])
    args[[i]][2] <- strip_annotations(args[[i]][2])
  }

  if (!is.null(operator)) {
    # =================================================================
    # Operator evaluation: substitute arguments into rule_translation
    # =================================================================
    parsed <- parse_type(operator)
    result_type <- get_result_type(parsed)

    trans <- rule_translation
    for (arg in args) {
      name      <- arg[1]                         # placeholder name (S, S1, V, ...)
      arg_type  <- sub("[0-9]+$", "", name)        # type reconstructed from name
      arg_trans <- arg[2]

      # Strip "to " from verb-type arguments
      if (!is.na(arg_type) && arg_type == "V")
        arg_trans <- sub("^to ", "", arg_trans)

      # Add "the " for S-type arguments when the operator changes
      # the type (e.g., Sx->V gets "the", but xS->S does not).
      #if (!is.na(arg_type) && arg_type == "S" && !identical("S", result_type))
      #  arg_trans <- paste0("the ", arg_trans)

      # Replace the placeholder (whole-word match)
      trans <- sub(paste0("\\b", name, "\\b"), arg_trans, trans)
    }

    # Type inheritance: if result is a base type and the first argument
    # with that base type carries an extended type, inherit the extension.
    if (!grepl("[a-z]", result_type)) {
      for (arg in args) {
        orig_type <- if (length(arg) >= 3) arg[3] else sub("[0-9]+$", "", arg[1])
        if (has_base_type(orig_type, result_type) && orig_type != result_type) {
          result_type <- orig_type
          break
        }
      }
    }

    # Avoid double period
    if (endsWith(trans, ".") && startsWith(trailing_sep, "."))
      trailing_sep <- substring(trailing_sep, 2)

    trans <- paste0(trans, trailing_sep)
    if (has_base_type(result_type, "SEN")) trans <- make_sentence(trans)
    return(c(result_type, trans))
  }

  # =================================================================
  # No operator: base case or composition
  # =================================================================
  seps <- gsub("-", " ", seps, fixed = TRUE)
  trailing_sep <- gsub("-", " ", trailing_sep, fixed = TRUE)

  # Helper: remove any space immediately before a comma
  fix_comma <- function(s) gsub(" ,", ",", s, fixed = TRUE)

  # Composition: exactly two elements required
  if (length(args) != 2) {
    remaining <- paste(sapply(args, function(a) sub("[0-9]+$", "", a[1])),
                       collapse = " + ")
    return(paste0("Cannot compose: ", remaining))
  }

  t1 <- sub("[0-9]+$", "", args[[1]][1]);  tr1 <- args[[1]][2]
  t2 <- sub("[0-9]+$", "", args[[2]][1]);  tr2 <- args[[2]][2]
  sep <- if (length(seps) > 0) seps[1] else " "

  # Full (extended) types from build_args third element
  full_t1 <- if (length(args[[1]]) >= 3) args[[1]][3] else t1
  full_t2 <- if (length(args[[2]]) >= 3) args[[2]][3] else t2

  # S + A -> S: simple juxtaposition
  if (t1 == "S" && t2 == "A")
    return(c("S", fix_comma(paste0(tr1, " ", tr2, trailing_sep))))

  # S + S -> S: genitive/instrumental ("of/with")
  if (t1 == "S" && t2 == "S" && !grepl(",", sep, fixed = TRUE))
    return(c("S", fix_comma(paste0(tr1, " of/with ", tr2, trailing_sep))))

  # S, S -> S: comma-separated list
  if (t1 == "S" && t2 == "S" && grepl(",", sep, fixed = TRUE))
    return(c("S", fix_comma(paste0(tr1, sep, tr2, trailing_sep))))

  # S + Vt -> Vi: object + transitive verb (swap order)
  if (has_base_type(full_t1, "S") && full_t2 == "Vt") {
    v_trans <- sub("^to ", "", tr2)
    if (grepl("\\bS\\b", v_trans)) {
      v_trans <- sub("\\bS\\b", tr1, v_trans)
      return(c("Vi", fix_comma(paste0(v_trans, trailing_sep))))
    }
    return(c("Vi", fix_comma(paste0(v_trans, " ", tr1, trailing_sep))))
  }

  # S + Vi -> SEN: subject + intransitive verb (strip "to ", keep order)
  if (has_base_type(full_t1, "S") && full_t2 == "Vi") {
    v_trans <- sub("^to ", "", tr2)
    return(c("SEN", make_sentence(fix_comma(paste0(tr1, " ", v_trans, trailing_sep)))))
  }

  # S + V -> SEN: subject-verb (strip "to ")
  if (t1 == "S" && t2 == "V") {
    v_trans <- sub("^to ", "", tr2)
    return(c("SEN", make_sentence(fix_comma(paste0(tr1, " ", v_trans, trailing_sep)))))
  }

  # SEN + SEN -> SEN: sentence concatenation (add period if missing)
  if (t1 == "SEN" && t2 == "SEN") {
    period <- if (endsWith(tr1, ".") || grepl("\\.", sep)) "" else "."
    # Avoid double period when tr1 already ends with "."
    if (endsWith(tr1, ".") && startsWith(sep, "."))
      sep <- substring(sep, 2)
    # Avoid double period when tr2 already ends with "."
    if (endsWith(tr2, ".") && startsWith(trailing_sep, "."))
      trailing_sep <- substring(trailing_sep, 2)
    return(c("SEN", fix_comma(paste0(tr1, period, sep, tr2, trailing_sep))))
  }

  remaining <- paste(sapply(args, function(a) sub("[0-9]+$", "", a[1])),
                     collapse = " + ")
  paste0("Cannot compose: ", remaining)
}
