#' plotSingleLZ
#'
#' @description
#'
#' Plot the result of specific local Z-Score tests from a [multiLocalZScore][multiLocalZScore-class] object in the
#' form of line plot profiles.
#'
#' @details
#'
#' This function generates a line plot with the local Z-score profiles of
#' selected region sets from a [multiLocalZScore][multiLocalZScore-class] object. This type of plot
#' complements the local Z-score matrix (generated by [plotLocalZScoreMatrix],
#' since it allows to visualize in detail the local Z-score profile of just the
#' region sets of interest.
#'
#' This plot is well suited for a single or a few region sets, but
#' will get busy if attempting to plot many different profiles. For the latter,
#' the full matrix generated by [plotLocalZScoreMatrix] is usually a better
#' visualization option.
#'
#' @usage plotSingleLZ(mLZ, RS, xlab = "", normZS = TRUE, ylim = NULL, main = NA,
#'  colPal = NULL, labValues = TRUE, labSize = 2.5, labMax = FALSE, smoothing = FALSE, ...)
#'
#' @param mLZ an object of class [multiLocalZScore][multiLocalZScore-class].
#' @param RS character, vector of region set names for which to plot the local Z-score results.
#' @param xlab character, label for the x axis. (default = NA)
#' @param main character, title for the plot. If NA, the name of the query region set in the multiLocalZScore object will be used. (default = NA)
#' @param normZS logical, indicates whether the normalized Z-score values should be plotted. If FALSE, the raw Z-score is used. (default = TRUE)
#' @param ylim numeric, vector with two elements: minimum and maximum Y values of the plot. If NULL, the plot limits are set by default so all data points can be plotted. (default = NULL)
#' @param labValues logical, if TRUE each local Z-score profile is labelled at position 0 with the name of the region set and its Z-score value at the central position. (default = TRUE)
#' @param labSize numerical, size of the labels from labValues in the plot. (default = 2.5)
#' @param labMax logical, if TRUE the labels are placed at the maximum value of each local Z-score profile instead of the center. (default = FALSE)
#' @param colPal character, colors to use as palette for the plot. If NULL, default colors will be used. (default = NULL)
#' @param smoothing logical, if TRUE the [smooth.spline()] function will be applied to the localZ-score profile. (default = FALSE)
#' @param ...  further arguments to be passed to other methods.
#'
#' @return Returns a ggplot object.
#'
#' @seealso [multiLocalZscore()], [makeLZMatrix()]
#'
#' @examples
#'
#' data("cw_Alien")
#'
#' plotSingleLZ(mLZ_regA_ReG, RS = c("regD", "regD_02", "regA", "regAB_04"),
#' labMax = TRUE, smoothing = TRUE)
#'
#' @export plotSingleLZ
#' @import ggplot2
#' @importFrom ggrepel geom_label_repel
#' @importFrom methods hasArg
#' @importFrom RColorBrewer brewer.pal
#' @importFrom methods is
#' @importFrom stats smooth.spline
#' @importFrom stats aggregate


plotSingleLZ <-
  function(mLZ,
           RS,
           xlab = "",
           normZS = TRUE,
           ylim = NULL,
           main = NA,
           colPal = NULL,
           labValues = TRUE,
           labSize = 2.5,
           labMax = FALSE,
           smoothing = FALSE,
           ...) {

    stopifnot("mLZ is missing" = methods::hasArg(mLZ))
    stopifnot("mLZ needs to be a multiLocalZScore object" = methods::is(mLZ, "multiLocalZScore"))
    stopifnot("RS is missing" = methods::hasArg(RS))
    stopifnot("One or more elements in RS do not match region set names in mLZ" = all(RS %in% names(mlzsMultiLocalZscores(mLZ)$shifed_ZSs)))

    RS <- as.list(RS)
    df <- do.call("rbind", lapply(X = RS, FUN = DFfromLZ, mLZ = mLZ))

    if (is.na(main)) {
      main <- mlzsParam(mLZ)$A
    }
    evfun <- mlzsParam(mLZ)$evFUN
    ranfun <- mlzsParam(mLZ)$ranFUN

    pal <- plotPal(colPal) #palette

    if (mlzsParam(mLZ)$evFUN == "numOverlaps") {
      mlzsParam(mLZ)$evFUN <- "N. of overlaps"
    }

    if (normZS) { # Raw or norm ZS
      df$score <- df$normLocalZscore
      ylabel <- "Normalized Z-score"
    } else {
      df$score <- df$lzscore
      ylabel <- "Z-score"
    }

    if (smoothing == TRUE){
      smth <- stats::smooth.spline(df$score)
      df$score <- smth$y
    }

    # Plot
    p <- ggplot2::ggplot(df, ggplot2::aes_string(x = "shift", y = "score", group = "name", fill = "name", color = "name")) +
      ggplot2::geom_hline(yintercept = 0, color = "#515E63", size = 0.6) +
      ggplot2::geom_vline(xintercept = 0, color = "#515E63", size = 0.4, linetype = "dotted") +
      ggplot2::geom_density(alpha = 0.3, stat = "identity") +
      ggplot2::scale_color_manual(values = pal(length(RS))) +
      ggplot2::scale_fill_manual(values = pal(length(RS))) +
      ggplot2::labs(
        title = main,
        subtitle = paste("ranFUN: ", ranfun, "\nevFUN: ", evfun),
        y = ylabel,
        x = "bp"
      ) +
      ggplot2::theme(legend.title = ggplot2::element_blank())

    # Labels
    if (labValues) {
      if (labMax) {
        df_label <- merge(stats::aggregate(score ~ name, data = df, FUN = max), df)
        df_label <- df_label[order(df_label$shift), , drop = FALSE]
        df_label <- df_label[!duplicated(df_label$name), , drop = FALSE]
      } else {
        df_label <- df[df$shift == 0, , drop = FALSE]
      }
      df_label$text <- paste(df_label$name, "\nZS: ", round(df_label$score, digits = 2), sep = "")
      p <- p +
        ggplot2::coord_cartesian(clip = "off") +
        ggrepel::geom_label_repel(
          data = df_label, inherit.aes = FALSE,
          ggplot2::aes_string(label = "text", x = "shift", y = "score", color = "name"),
          fill = "#FDFAF6", size = labSize,
          xlim = c(0.2 * max(df$shift), NA),
          show.legend = FALSE,
          min.segment.length = 0
        )
    }

    # Ylims
    if (!is.null(ylim)) {
      p <- p + ggplot2::coord_cartesian(ylim = ylim)
    }
    p <- p + mendel_theme()
    return(p)
  }
