#' @title
#' Exact separation matrices computation.
#'
#' @description
#' Computes exact separation matrices by evaluating the average separation over all the linear extensions of the input poset.
#' The linear extensions are generated according to the algorithm given in Habib M, Medina R, Nourine L and Steiner G (2001).
#'
#' @param poset Object of S4 class `POSet` representing the poset whose separation matrix is computed.
#' Argument `poset` must be created by using any function contained in the package aimed at building object of S4 class `POSet`
#' (e.g. [POSet()], [LinearPOSet()], [ProductPOSet()], ...) .
#'
#' @param output_every_sec Integer specifying a time interval (in seconds).
#' By specifying this argument, during the execution of `BubleyDyerSeparation`, a message reporting the number of linear extensions
#' progressively generated is printed on the R-Console, every `output_every_sec` seconds.
#'
#' @param type type of separation to be computed. Possible choices are:
#' "symmetric", "asymmetricLower", "asymmetricUpper", "vertical", "horizontal".
#'
#' @param ... additional types of Separations to be computed. Possible choices are:
#' "symmetric", "asymmetricLower", "asymmetricUpper", "vertical", "horizontal".
#'
#' @details The symmetric separation associated to two elements \eqn{a} and \eqn{b} of the input poset, is the average
#' absolute difference between the positions of \eqn{a} and \eqn{b} observed over all linear extensions (whose elements are arranged in ascending order):
#'
#' \eqn{Sep_{ab}=\frac{1}{n}\sum_{i^1}^{n}|Pos_{l_i}(a)-Pos_{l_i}(b)|},
#'
#' where \eqn{n} is the numbers of linear extensions of the input poset;
#' \eqn{l_i} represents a single linear extension and \eqn{Pos_{l_i}(\cdot)} stands for the position of element \eqn{\cdot}
#' into the sequence of poset elements arranged in increasing order according to \eqn{l_i}.
#'
#' Asymmetric lower and upper separations are defined as:
#' \eqn{Sep_{a < b}=\frac{1}{n}\sum_{i^1}^{n}(Pos_{l_i}(b)-Pos_{l_i}(a))\mathbb{1}(a <_{l_i} b)},
#' \eqn{Sep_{b < a}=\frac{1}{n}\sum_{i^1}^{n}(Pos_{l_i}(a)-Pos_{l_i}(b))\mathbb{1}(b <_{l_i} a)},
#' where \eqn{a\leq_{l_i} b} means that \eqn{a} is lower or equal to \eqn{b} in the linear order defined by linear
#' extension \eqn{l_i} and \eqn{\mathbb{1}} is the indicator function. Note that \eqn{Sep_{ab}=Sep_{a < b}+Sep_{a < b}}.
#'
#' Vertical and horizontal separations (\eqn{vSep} and \eqn{hSep}, respectively) are defined as
#'
#' \eqn{vSep_{ab}=|Sep_{a < b}-Sep_{b < a}|} and #' \eqn{hSep_{ab}=Sep_{ab}-vSep_{ab}|}.
#'
#' For a detailed explanation on why \eqn{vSep} and \eqn{hSep} can be interpreted as vertical and horizontal components
#' of the separation between poset elements, see Fattore et. al (2024).
#'
#' @return A list containing: 1) the required type of approximated separation matrices, according to the parameter `type` used
#' to build the `generator` (see function `BuildBubleyDyerSeparationGenerator`); 2) the number of generated linear extensions.
#'
#' @references Habib M, Medina R, Nourine L and Steiner G (2001). Efficient algorithms on distributive lattices.
#' Discrete Applied Mathematics, 110, 169-187. https://doi.org/10.1016/S0166-218X(00)00258-4.
#'
#' Fattore, M., De Capitani, L., Avellone, A., and Suardi, A. (2024).
#' A fuzzy posetic toolbox for multi-criteria evaluation on ordinal data systems.
#' Annals of Operations Research, https://doi.org/10.1007/s10479-024-06352-3.
#'
#'
#' @examples
#' el <- c("a", "b", "c", "d")
#'
#' dom <- matrix(c(
#'   "a", "b",
#'   "c", "b",
#'   "b", "d"
#' ), ncol = 2, byrow = TRUE)
#'
#' pos <- POSet(elements = el, dom = dom)
#'
#'SEP_matrices <- ExactSeparation(pos, output_every_sec=5, "symmetric","asymmetricUpper", "vertical")
#'
#' @name ExactSeparation
#' @export ExactSeparation
ExactSeparation <- function(poset, output_every_sec=NULL, type, ...) {
  SeparationTypes <- c("symmetric", "asymmetricLower", "asymmetricUpper", "vertical", "horizontal")
  SeparationTypesC <- c("symmetric", "asymmetricLower", "asymmetricUpper")
  if (!methods::is(poset, "POSet")) {
    stop("poset must be of class POSet")
  }
  if (!is.null(output_every_sec) && (output_every_sec < 0 || output_every_sec != round(output_every_sec))) {
    stop("output_every_sec must be a positive integer")
  }
  if (!is.null(output_every_sec)) {
    output_every_sec <- as.integer(output_every_sec)
  }

  if (!(type %in% SeparationTypes)) {
    stop("type TO_DO")
  }
  parameter_list = list(type)
  for(t in list(...)) {
    if (!(t %in% SeparationTypes)) {
      stop(".... TO_DO")
    }
    parameter_list[[length(parameter_list)+1]] = t
  }

  functions_list <- parameter_list
  if (SeparationTypes[4] %in% parameter_list) {
    functions_list[[length(functions_list)+1]] = SeparationTypes[2]
    functions_list[[length(functions_list)+1]] = SeparationTypes[3]
  }
  if (SeparationTypes[5] %in% parameter_list) {
    functions_list[[length(functions_list)+1]] = SeparationTypes[1]
    functions_list[[length(functions_list)+1]] = SeparationTypes[2]
    functions_list[[length(functions_list)+1]] = SeparationTypes[3]
  }
  functions_list <- intersect(functions_list, SeparationTypesC)

  tryCatch({
    result <- .Call("_ExactSeparation", poset@ptr, output_every_sec, functions_list)
    if ("vertical" %in% parameter_list) {
      vertical <- abs(result[["asymmetricLower"]] - result[["asymmetricUpper"]])
      result[["vertical"]] = vertical
    }
    if ("horizontal" %in% parameter_list) {
      horizzotal <- result[["symmetric"]] - (abs(result[["asymmetricLower"]] - result[["asymmetricUpper"]]))
      result[["horizontal"]] = horizzotal
    }
    if ("symmetric" %in% parameter_list) {
      horizzotal <- result[["symmetric"]] - (abs(result[["asymmetricLower"]] - result[["asymmetricUpper"]]))
      result[["horizontal"]] = horizzotal
    }
    result <- result[unlist(parameter_list)]
    return (result)
  }, error = function(err) {
    err_split <- strsplit(err[[1]], split = ":")
    stop(err_split[[1]][length(err_split[[1]])])
  }) # END tryCatch
}
