#' @title Seminal change point
#' @description
#' Window-based change-point detection that compares two local linear models
#' fitted on each sliding window: one using the full window and another using the
#' same window split around its central observation. The difference between the
#' two residual summaries is used as the change score.
#'
#' The method is called "seminal" because the paper defines a seminal point for
#' each window family, i.e. the central observation used to split the local
#' regression into two sides. This makes the detector a local family method based
#' on windows rather than a global segmentation algorithm.
#'
#' @param sw_size Sliding window size.
#'@return `hcp_scp` object
#'@examples
#'library(daltoolbox)
#'
#' # Load change-point example data
#' data(examples_changepoints)
#'
#' # Use a simple example
#' dataset <- examples_changepoints$simple
#' head(dataset)
#'
#' # Configure the seminal change-point detector
#' model <- hcp_scp()
#'
#' # Fit the model
#' model <- fit(model, dataset$serie)
#'
#' # Run detection
#' detection <- detect(model, dataset$serie)
#'
#' # Show detected change points
#' print(detection[(detection$event),])
#'
#' @references
#' - The seminal change-point paper referenced in
#'   `Event Detection from Time Series Data`.
#' - Ogasawara, E., Salles, R., Porto, F., Pacitti, E. Event Detection in Time Series. 1st ed.
#'   Cham: Springer Nature Switzerland, 2025. doi:10.1007/978-3-031-75941-3
#'
#'@export
hcp_scp <- function(sw_size = 30) {
  obj <- harbinger()
  obj$sw_size <- sw_size

  hutils <- harutils()
  obj$har_outliers <- hutils$har_filter_boxplot
  obj$har_outliers_check <- hutils$har_candidate_selection_highgroup

  class(obj) <- append("hcp_scp", class(obj))
  return(obj)
}

#'@importFrom stats lm
#'@importFrom stats na.omit
#'@exportS3Method detect hcp_scp
detect.hcp_scp <- function(obj, serie, ...) {
  # Compare the full-window regression against the split-window regression.
  analyze_window <- function(data, offset) {
  n <- length(data)
  y <- data.frame(t = 1:n, y = data)

  mdl <- stats::lm(y~t, y)
  res <- obj$har_distance(mdl$residuals)
  err <- mean(res)

  y_a <- y[1:(offset-1),]
  mdl_a <- stats::lm(y~t, y_a)
  y_d <- y[(offset+1):n,]
  mdl_d <- stats::lm(y~t, y_d)

  res_ad <- obj$har_distance(c(mdl_a$residuals,mdl_d$residuals))
  err_ad <- mean(res_ad)

  #return 1-error on whole window; 2-error on window halves; 3-error difference
  return(data.frame(mdl=err, mdl_ad=err_ad, mdl_dif=err-err_ad))
}

  if(is.null(serie)) stop("No data was provided for computation", call. = FALSE)

  # Normalize indexing and omit NAs.
  obj <- obj$har_store_refs(obj, serie)

  sx <- tspredit::ts_data(obj$serie, obj$sw_size)
  obj$offset <- round(obj$sw_size/2)

  # Analyze all data windows.
  errors <- do.call(rbind,apply(sx, 1, analyze_window, obj$offset))
  values <- sx[, obj$offset + 1]

  res <- errors$mdl_dif

  change_point <- obj$har_outliers(res)
  change_point <- obj$har_outliers_check(change_point, res, values)

  threshold <- attr(change_point, "threshold")
  res <- c(rep(0, obj$offset), res, rep(0, obj$sw_size - obj$offset - 1))
  change_point <- c(rep(FALSE, obj$offset), change_point, rep(FALSE, obj$sw_size - obj$offset - 1))
  attr(change_point, "threshold") <- threshold

  # Restore change points to original indexing.
  detection <- obj$har_restore_refs(obj, change_point = change_point, res = res)

  return(detection)
}
