
# --- determine_group_type: determine the result type of a bracket group ---
# The bracket structure is already set by add_brackets, so this function
# only needs to look at the elements inside a single group:
#   - If an operator is present, return its result type.
#   - Otherwise, apply COMP_RULES to determine the composition type.
determine_group_type <- function(inner, type) {
  SEN_OPEN <- "\u00ab"
  VT_OPEN  <- "\u2039"
  chars <- strsplit(inner, "")[[1]]
  types <- character(0)
  op_result <- NULL
  i <- 1L

  while (i <= length(chars)) {
    ch <- chars[i]

    if (ch == "#") {
      rt <- read_tag(chars, i)
      i <- rt$next_i
      parsed <- parse_type(type[rt$tag_num])
      # If this element is an operator, remember result but continue
      # to collect argument types for type inheritance
      if (parsed$is_operator) {
        op_result <- parsed$result
      } else {
        types <- c(types, parsed$word_type)
      }

    } else if (ch %in% c("(", "<", "[", "{") || ch == SEN_OPEN || ch == VT_OPEN) {
      bracket_type <- switch(ch,
        "(" = "S", "<" = "V", "\u2039" = "Vt", "[" = "A", "{" = "SEN", "SEN")
      close <- switch(ch,
        "(" = ")", "<" = ">", "\u2039" = "\u203a", "[" = "]", "{" = "}", "\u00bb")
      depth <- 1L
      i <- i + 1L
      while (i <= length(chars) && depth > 0) {
        if (chars[i] == ch) depth <- depth + 1L
        if (chars[i] == close) depth <- depth - 1L
        i <- i + 1L
      }
      types <- c(types, bracket_type)

    } else {
      i <- i + 1L
    }
  }

  # Operator with type inheritance
  if (!is.null(op_result)) {
    if (!grepl("[a-z]", op_result)) {
      for (t in types) {
        if (has_base_type(t, op_result) && t != op_result) return(t)
      }
    }
    return(op_result)
  }

  if (length(types) == 0) return("S")
  if (length(types) == 1) return(types[1])

  # No operator: apply composition rules (from COMP_RULES)
  if (length(types) == 2) {
    for (cr in COMP_RULES) {
      if (type_matches(types[1], cr$t1) && type_matches(types[2], cr$t2)) return(cr$result)
    }
  }

  # Fallback
  "SEN"
}


grammatical_structure <- function(s, type, expr = NULL) {
  SEN_OPEN  <- "\u00ab"
  SEN_CLOSE <- "\u00bb"
  VT_OPEN   <- "\u2039"
  VT_CLOSE  <- "\u203a"

  # Step 1: Build structure string by replacing expr[k] with #k
  if (!is.null(expr)) {
    for (k in seq_along(expr)) {
      s <- sub(expr[k], paste0("#", k), s, fixed = TRUE)
    }
  }

  s <- str_replace_all(s, "-","")
  s <- str_replace_all(s, fixed("."),"")
  s <- str_replace_all(s, " ","")

  # Step 2: Add brackets (grouping by operator binding and composition rules)
  res <- add_brackets(s, type)
  if (identical(res$type, "ERROR")) return(res$string)
  s <- res$string

  # Step 3: Replace {} with typed brackets
  while (grepl("\\{[^{}]+\\}", s)) {
    m <- regexpr("\\{[^{}]+\\}", s)
    match_str <- regmatches(s, m)
    inner <- substring(match_str, 2, nchar(match_str) - 1)
    group_type <- determine_group_type(inner, type)
    group_base <- base_type(group_type)
    brackets <- if (group_type == "Vt") {
      c(VT_OPEN, VT_CLOSE)
    } else {
      switch(group_base,
        "S" = c("(", ")"),
        "V" = c("<", ">"),
        "A" = c("[", "]"),
        c(SEN_OPEN, SEN_CLOSE))
    }
    regmatches(s, m) <- paste0(brackets[1], inner, brackets[2])
  }

  s <- gsub(SEN_OPEN, "{", s, fixed = TRUE)
  s <- gsub(SEN_CLOSE, "}", s, fixed = TRUE)
  s <- gsub(VT_OPEN, "<", s, fixed = TRUE)
  s <- gsub(VT_CLOSE, ">", s, fixed = TRUE)

  # Step 4: Replace #N tags with expressions
  if (!is.null(expr)) {
    for (k in seq_along(expr)) {
      s <- sub(paste0("#", k), expr[k], s, fixed = TRUE)
    }
  }

  class(s) <- "grammatical_structure"
  s
}


#' @exportS3Method print grammatical_structure
print.grammatical_structure <- function(x, ...) {

  s <- unclass(x)

  # --- Vivid color styles ---
  col_green <- cli::make_ansi_style("#00CC44")
  col_blue  <- cli::make_ansi_style("#338AFF")
  col_red   <- cli::make_ansi_style("#EE3333")
  col_tan   <- cli::make_ansi_style("#C8A840")
  col_gray  <- cli::make_ansi_style("#AAAAAA")

  apply_color <- function(text, color) {
    switch(color,
      "green" = col_green(text),
      "blue"  = col_blue(text),
      "red"   = col_red(text),
      "tan"   = col_tan(text),
      "gray"  = col_gray(text),
      text
    )
  }

  # --- Bracket definitions ---
  open_brackets  <- c("(", "[", "<", "{")
  close_brackets <- c(")", "]", ">", "}")
  bracket_colors <- c("green", "blue", "red", "tan")

  chars <- strsplit(s, "")[[1]]
  n <- length(chars)

  # === Pass 1: Match bracket pairs and check for bare symbols ===
  pair_id    <- integer(n)
  pair_match <- integer(n)
  stack_pos  <- integer(0)
  pair_count <- 0L

  for (i in seq_len(n)) {
    if (chars[i] %in% open_brackets) {
      stack_pos <- c(stack_pos, i)
    } else if (chars[i] %in% close_brackets) {
      if (length(stack_pos) > 0) {
        open_pos <- stack_pos[length(stack_pos)]
        stack_pos <- stack_pos[-length(stack_pos)]
        pair_count <- pair_count + 1L
        pair_id[open_pos] <- pair_count
        pair_id[i] <- pair_count
        pair_match[open_pos] <- i
        pair_match[i] <- open_pos
      }
    }
  }

  # For each pair check whether it contains a bare (non-bracket) symbol
  # at nesting depth 0 relative to that pair.
  pair_has_bare <- logical(pair_count)

  for (i in seq_len(n)) {
    if (chars[i] %in% open_brackets && pair_id[i] > 0) {
      pid <- pair_id[i]
      if (pair_has_bare[pid]) next
      close_pos <- pair_match[i]
      if (close_pos - i < 2) next
      depth <- 0L
      for (j in (i + 1L):(close_pos - 1L)) {
        if (chars[j] %in% open_brackets) {
          depth <- depth + 1L
        } else if (chars[j] %in% close_brackets) {
          depth <- depth - 1L
        } else if (depth == 0L) {
          pair_has_bare[pid] <- TRUE
          break
        }
      }
    }
  }

  # === Pass 2: Color each character ===
  color_stack <- character(0)
  parts <- character(n)

  for (i in seq_len(n)) {
    ch <- chars[i]

    if (ch %in% open_brackets) {
      pid <- pair_id[i]
      if (pid > 0 && pair_has_bare[pid]) {
        col <- bracket_colors[match(ch, open_brackets)]
      } else {
        col <- "gray"
      }
      color_stack <- c(color_stack, col)
      parts[i] <- apply_color(ch, col)

    } else if (ch %in% close_brackets) {
      col <- if (length(color_stack) > 0) color_stack[length(color_stack)] else ""
      parts[i] <- apply_color(ch, col)
      if (length(color_stack) > 0) {
        color_stack <- color_stack[-length(color_stack)]
      }

    } else {
      col <- if (length(color_stack) > 0) color_stack[length(color_stack)] else ""
      parts[i] <- apply_color(ch, col)
    }
  }

  cat(paste0(parts, collapse = ""), "\n")
  invisible(x)
}
