#' Plot benchmarking adjustments
#'
#'
#' @description
#'
#' \if{html,text}{(\emph{version française: 
#' \url{https://StatCan.github.io/gensol-gseries/fr/reference/plot_benchAdj.html}})}
#' 
#' Plot benchmarking adjustments for a single series in the current (active) graphics device. Up to three types of
#' adjustments can be overlayed in the same plot:
#' - Adjustments generated by function [benchmarking()]
#' - Adjustments generated by function [stock_benchmarking()]
#' - Cubic spline associated to adjustments generated by function [stock_benchmarking()]
#'
#' These plots can be useful to assess the quality of the benchmarking results and compare the adjustments
#' generated by both benchmarking functions ([benchmarking()] and [stock_benchmarking()]) for stock series.
#'
#'
#' @param PB_graphTable (optional)
#'
#' Data frame (object of class "data.frame") corresponding to the [benchmarking()] (PB for "Proc Benchmarking"
#' approach) function output `graphTable` data frame. Specify `NULL` not to include the [benchmarking()] adjustments
#' in the plot.
#'
#' **Default value** is `PB_graphTable = NULL`.
#'
#' @param SB_graphTable (optional)
#'
#' Data frame (object of class "data.frame") corresponding to the [stock_benchmarking()] (SB) function output
#' `graphTable` data frame. Specify `NULL` not to include the [stock_benchmarking()] adjustments in the plot.
#'
#' **Default value** is `SB_graphTable = NULL`.
#'
#' @param SB_splineKnots (optional)
#'
#' Data frame (object of class "data.frame") corresponding to the [stock_benchmarking()] (SB) function output
#' `splineKnots` data frame. Specify `NULL` not to include the [stock_benchmarking()] cubic spline in the plot.
#'
#' **Default value** is `SB_splineKnots = NULL`.
#'
#' @param legendPos (optional)
#'
#' String (keyword) specifying the location of the legend in the plot. See the description of argument `x` in
#' the documentation of `graphics::legend()` for the list of valid keywords. Specify `NULL` not to include a
#' legend in the plot.
#'
#' **Default value** is `legendPos = "bottomright"`.
#'
#'
#' @details
#' `graphTable` data frame (arguments `PB_graphTable` and `SB_graphTable`) variables used in the plot:
#' - `t` for the x-axis values (*t*)
#' - `benchmarkedSubAnnualRatio` for the *Stock Bench. (SB)* and *Proc Bench. (PB)* lines
#' - `bias` for the *Bias* line (when \eqn{\rho < 1})
#' 
#' `splineKnots` data frame (argument `SB_splineKnots`) variables used in the plot:
#' - `x` for the x-axis values (*t*)
#' - `y` for the *Cubic spline* line and the *Extra knot* and *Original knot* points
#' - `extraKnot` for the type of knot (*Extra knot* vs. *Original knot*)
#' 
#' See section **Value** of [benchmarking()] and [stock_benchmarking()] for more details on these data frames.
#' 
#'
#' @returns
#' This function returns nothing (`invisible(NULL)`).
#'
#'
#' @seealso [plot_graphTable()] [bench_graphs] [benchmarking()] [stock_benchmarking()]
#'
#'
#' @example misc/function_examples/plot_benchAdj-ex.R
#'
#'
#' @export
plot_benchAdj <- function(PB_graphTable = NULL,
                          SB_graphTable = NULL,
                          SB_splineKnots = NULL,
                          legendPos = "bottomright") {

  # Initialize info
  lab_list <- NULL
  col_list <- NULL
  lty_list <- NULL
  lwd_list <- NULL
  pch_list <- NULL
  pt.cex_list <- NULL
  min_x <- .Machine$double.xmax
  max_x <- .Machine$double.xmin
  min_y <- .Machine$double.xmax
  max_y <- .Machine$double.xmin
  bias <- NULL
  BI_string <- ""

  # Process `stock_benchmarking()` `splineKnots` data frame info
  if (!is.null(SB_splineKnots)) {
    
    # Accept `SB_splineKnots = NA` as `SB_splineKnots = NULL`
    tmp <- (unlist(SB_splineKnots))[1]
    if (identical(SB_splineKnots, tmp) && is.na(tmp)) {
      SB_splineKnots <- NULL
    } else {
      
      if (!is.data.frame(SB_splineKnots)) {
        stop("Argument 'SB_splineKnots' is not a 'data.frame' object.\n\n", call. = FALSE)
      }
      SB_splineKnots <- as.data.frame(SB_splineKnots)
      var_list <- names(SB_splineKnots)
      if ("extraKnot" %in% var_list) {
        extraKnot_flag <- TRUE
        lab_list <- c(lab_list, "Cubic spline", "Extra knot", "Original knot")
        col_list <- c(col_list, "black", "black", "green3")
        lty_list <- c(lty_list, 1, NA, NA)
        lwd_list <- c(lwd_list, 1, NA, NA)
        pch_list <- c(pch_list, NA, 1, 19)
        pt.cex_list <- c(pt.cex_list, NA, 1.5, 1.5)
      } else {
        extraKnot_flag <- FALSE
        lab_list <- c(lab_list, "Cubic spline")
        col_list <- c(col_list, "black")
        lty_list <- c(lty_list, 1)
        lwd_list <- c(lwd_list, 1)
        pch_list <- c(pch_list, 1)
        pt.cex_list <- c(pt.cex_list, 1.5)
      }
      interpol <- stats::spline(SB_splineKnots$x, SB_splineKnots$y, method = "natural")
      min_x <- min(min_x, interpol$x, na.rm = TRUE)
      max_x <- max(max_x, interpol$x, na.rm = TRUE)
      min_y <- min(min_y, interpol$y, na.rm = TRUE)
      max_y <- max(max_y, interpol$y, na.rm = TRUE)
    }
  }

  # Process `stock_benchmarking()` `graphTable` data frame info
  if (!is.null(SB_graphTable)) {
    
    # Accept `SB_graphTable = NA` as `SB_graphTable = NULL`
    tmp <- (unlist(SB_graphTable))[1]
    if (identical(SB_graphTable, tmp) && is.na(tmp)) {
      SB_graphTable <- NULL
    } else {
      
      if (!is.data.frame(SB_graphTable)) {
        stop("Argument 'SB_graphTable' is not a 'data.frame' object.\n\n", call. = FALSE)
      }
      SB_graphTable <- as.data.frame(SB_graphTable)
      lab_list <- c(lab_list, "Stock Bench. (SB)")
      col_list <- c(col_list, "red")
      lty_list <- c(lty_list, 1)
      lwd_list <- c(lwd_list, 1)
      pch_list <- c(pch_list, 1)
      pt.cex_list <- c(pt.cex_list, 1)
      min_x <- min(min_x, SB_graphTable$t, na.rm = TRUE)
      max_x <- max(max_x, SB_graphTable$t, na.rm = TRUE)
      min_y <- min(min_y, SB_graphTable$benchmarkedSubAnnualRatio, na.rm = TRUE)
      max_y <- max(max_y, SB_graphTable$benchmarkedSubAnnualRatio, na.rm = TRUE)
      var_list <- names(SB_graphTable)
      if ("bias" %in% var_list) {
        
        # Display the bias line only for non-Denton benchmarking (`rho < 1`)
        if ("rho" %in% var_list) {
          if (abs(SB_graphTable$rho[1] - 1) > gs.tolerance) {
            bias <- SB_graphTable$bias[1]
          }
        } else {
          bias <- SB_graphTable$bias[1]
        }
      }
      if ("lambda" %in% var_list) {
        if (SB_graphTable$lambda[1] == 0) {
          BI_string <- "(BI differences)"
        } else {
          BI_string <- "(BI ratios)"
        }
      }
    }
    
  }

  # Process `benchmarking()` `graphTable` data frame info
  if (!is.null(PB_graphTable)) {
    
    # Accept `PB_graphTable = NA` as `PB_graphTable = NULL`
    tmp <- (unlist(PB_graphTable))[1]
    if (identical(PB_graphTable, tmp) && is.na(tmp)) {
      PB_graphTable <- NULL
    } else {
      
      if (!is.data.frame(PB_graphTable)) {
        stop("Argument 'PB_graphTable' is not a 'data.frame' object.\n\n", call. = FALSE)
      }
      PB_graphTable <- as.data.frame(PB_graphTable)
      PB_graphTable <- PB_graphTable[!duplicated(PB_graphTable[c("t", "benchmarkedSubAnnualRatio")]), ]
      lab_list <- c(lab_list, "Proc Bench. (PB)")
      col_list <- c(col_list, "blue")
      lty_list <- c(lty_list, 1)
      lwd_list <- c(lwd_list, 1)
      pch_list <- c(pch_list, 1)
      pt.cex_list <- c(pt.cex_list, 1)
      min_x <- min(min_x, PB_graphTable$t, na.rm = TRUE)
      max_x <- max(max_x, PB_graphTable$t, na.rm = TRUE)
      min_y <- min(min_y, PB_graphTable$benchmarkedSubAnnualRatio, na.rm = TRUE)
      max_y <- max(max_y, PB_graphTable$benchmarkedSubAnnualRatio, na.rm = TRUE)
      var_list <- names(PB_graphTable)
      if ("bias" %in% var_list) {
        
        # Display the bias line only for non-Denton benchmarking (`rho < 1`)
        if ("rho" %in% var_list) {
          if (abs(PB_graphTable$rho[1] - 1) > gs.tolerance) {
            bias <- PB_graphTable$bias[1]
          }
        } else {
          bias <- PB_graphTable$bias[1]
        }
      }
      if ("lambda" %in% var_list) {
        if (PB_graphTable$lambda[1] == 0) {
          BI_string <- "(BI differences)"
        } else {
          BI_string <- "(BI ratios)"
        }
      }
    }
  }

  if (!is.null(lab_list)) {

    # Impose a minimum range for the 'x' and y' axis values
    # (to avoid "new" warning messages introduced after R-4.2.3)
    if (max_x - min_x < gs.tolerance) {
      # E.g., single adjustment value
      min_x <- min_x - gs.tolerance / 2
      max_x <- max_x + gs.tolerance / 2
    }
    if (max_y - min_y < gs.tolerance) {
      # E.g., identical/repeated adjustment values
      min_y <- min_y - gs.tolerance / 2
      max_y <- max_y + gs.tolerance / 2
    }

    # Initialize the plot
    plot(min_x, min_y, type = "n", xlim = c(min_x, max_x), ylim = c(min_y, max_y),
         xlab = "t", ylab = paste("Adjustments", BI_string))
    graphics::title(paste("Benchmarking Adjustments", BI_string))

    # Plot the cubic spline with knots
    if (!is.null(SB_splineKnots)) {
      graphics::lines(interpol, type = "l")
      graphics::points(SB_splineKnots$x, SB_splineKnots$y, cex = 1.5)
      if (extraKnot_flag) {
        graphics::points(SB_splineKnots$x[SB_splineKnots$extraKnot == FALSE],
                         SB_splineKnots$y[SB_splineKnots$extraKnot == FALSE],
                         pch = 19, cex = 1.5, col = "green3")
      }
    }

    # Plot the stock_benchmarking() adjustments
    if (!is.null(SB_graphTable)) {
      graphics::lines(SB_graphTable$t, SB_graphTable$benchmarkedSubAnnualRatio,
                      type = "l", col = "red", lwd = 2)
      graphics::points(SB_graphTable$t, SB_graphTable$benchmarkedSubAnnualRatio,
                       col = "red")
    }

    # Plot the benchmarking() adjustments
    if (!is.null(PB_graphTable)) {
      graphics::lines(PB_graphTable$t, PB_graphTable$benchmarkedSubAnnualRatio,
                      type = "l", col = "blue", lwd = 2)
      graphics::points(PB_graphTable$t, PB_graphTable$benchmarkedSubAnnualRatio,
                       col = "blue")
    }

    # Plot the bias line
    if (!is.null(bias)) {
      bias_line <- data.frame(x = seq(min_x, max_x),
                              y = rep(bias, max_x - min_x + 1))
      graphics::lines(bias_line, col = "green4", lty = 2)
      lab_list <- c(lab_list, "Bias")
      col_list <- c(col_list, "green4")
      lty_list <- c(lty_list, 2)
      lwd_list <- c(lwd_list, 1)
      pch_list <- c(pch_list, NA)
      pt.cex_list <- c(pt.cex_list, NA)
    }

    # Add the legend
    if (!is.null(legendPos)) {
      graphics::legend(x = legendPos,
                       bty = "n",
                       inset = c(0.025, 0.025),
                       cex = 0.8,
                       legend = lab_list,
                       col = col_list,
                       lty = lty_list,
                       lwd = lwd_list,
                       pch = pch_list,
                       pt.cex = pt.cex_list)
    }
  }

  invisible(NULL)
}
