# Main wrapper functions for GPForth and GPFoblq with random start functionality.
# Overhauled as part of 2026.4-1

.GPA_RS_engine <- function(A, Tmat, normalize = FALSE, eps = 1e-5, maxit = 1000,
                            method = NULL, methodArgs = NULL, randomStarts = 0,
                            orthogonal = TRUE, L = NULL, ...) {
  # Internal engine implementing random-start gradient projection rotation.
  #
  # Args:
  #   A            : Unrotated factor loading matrix (p x k)
  #   Tmat         : Initial rotation/transformation matrix
  #   normalize    : Kaiser normalization - logical or weight matrix
  #   eps          : Convergence tolerance (default 1e-5)
  #   maxit        : Maximum iterations (default 1000)
  #   method       : Rotation criterion name
  #   methodArgs   : Additional arguments passed to the vgQ criterion function
  #   randomStarts : Number of random starts (0 = use Tmat as-is)
  #   orthogonal   : TRUE for orthogonal (GPForth), FALSE for oblique (GPFoblq)
  #   L            : Deprecated alias for A
  #
  # Returns:
  #   A GPArotation object from the best start, with optional randStartChar diagnostics

  # --- Deprecation ---
  if (!is.null(L)) {
    warning("Argument 'L' is deprecated; using 'A' instead.", call. = FALSE)
    A <- L
  }

  # --- Validation ---
  if (ncol(A) <= 1)
    stop("Rotation does not make sense for single-factor models.")

  # --- Setup ---
  engine        <- if (orthogonal) GPForth else GPFoblq
  actual_starts <- max(1, randomStarts)
  best_res      <- NULL
  Qvalues       <- numeric(actual_starts)
  Qconverged    <- logical(actual_starts)

  # --- Execution loop ---
  for (i in 1:actual_starts) {
    current_Tmat <- if (randomStarts > 0) Random.Start(ncol(A)) else Tmat

    res <- engine(A, Tmat = current_Tmat, normalize = normalize, eps = eps,
                  maxit = maxit, method = method, methodArgs = methodArgs)

    # Use indexing instead of tail() to avoid namespace warnings
    Qvalues[i]    <- res$Table[nrow(res$Table), 2]
    Qconverged[i] <- res$convergence

    if (is.null(best_res) || Qvalues[i] < best_res$Table[nrow(best_res$Table), 2])
      best_res <- res
  }

  # --- Random start diagnostics ---
  if (randomStarts > 1) {
    Q_round    <- round(Qvalues / eps) * eps
    Qmin_round <- min(Q_round)

    best_res$randStartChar <- c(
      randomStarts = randomStarts,
      Converged    = sum(Qconverged),
      atMinimum    = sum(Q_round == Qmin_round),
      localMins    = length(unique(Q_round))
    )
  }

  # --- Finalize for S3 print/summary ---
  if (!orthogonal)
    dimnames(best_res$Phi) <- list(colnames(A), colnames(A))

  best_res
}

GPFRSorth <- function(A, Tmat = diag(ncol(A)), normalize = FALSE, eps = 1e-5,
                      maxit = 1000, method = "varimax", methodArgs = NULL,
                      randomStarts = 0, ...) {
  # Orthogonal rotation with optional random starts. Wrapper for .GPA_RS_engine.
  #
  # Args:
  #   A            : Unrotated factor loading matrix (p x k)
  #   Tmat         : Initial rotation matrix (default: identity)
  #   normalize    : Kaiser normalization - logical or weight matrix
  #   eps          : Convergence tolerance (default 1e-5)
  #   maxit        : Maximum iterations (default 1000)
  #   method       : Rotation criterion (default "varimax")
  #   methodArgs   : Additional arguments passed to the vgQ criterion function
  #   randomStarts : Number of random starts (0 = use Tmat as-is)

  .GPA_RS_engine(A = A, Tmat = Tmat, normalize = normalize, eps = eps,
                 maxit = maxit, method = method, methodArgs = methodArgs,
                 randomStarts = randomStarts, orthogonal = TRUE, ...)
}

GPFRSoblq <- function(A, Tmat = diag(ncol(A)), normalize = FALSE, eps = 1e-5,
                      maxit = 1000, method = "quartimin", methodArgs = NULL,
                      randomStarts = 0, ...) {
  # Oblique rotation with optional random starts. Wrapper for .GPA_RS_engine.
  #
  # Args:
  #   A            : Unrotated factor loading matrix (p x k)
  #   Tmat         : Initial transformation matrix (default: identity)
  #   normalize    : Kaiser normalization - logical or weight matrix
  #   eps          : Convergence tolerance (default 1e-5)
  #   maxit        : Maximum iterations (default 1000)
  #   method       : Rotation criterion (default "quartimin")
  #   methodArgs   : Additional arguments passed to the vgQ criterion function
  #   randomStarts : Number of random starts (0 = use Tmat as-is)

  .GPA_RS_engine(A = A, Tmat = Tmat, normalize = normalize, eps = eps,
                 maxit = maxit, method = method, methodArgs = methodArgs,
                 randomStarts = randomStarts, orthogonal = FALSE, ...)
}