########################################################################################################################################
########################################################################################################################################
# LEGet
########################################################################################################################################
########################################################################################################################################
#' @title
#' Generates linear extensions of a given poset, by using a linear extension generator
#'
#' @description
#' Generates the linear extensions of a poset, by using a linear extension generator
#' built by functions `LEGenerator` and `LEBubleyDyer`.
#'
#' @param generator The linear extension generator built by function [LEGenerator()] or [LEBubleyDyer()].
#' If `LEGenerator` is used, `n` linear extensions of the poset are generated,
#' according to a deterministic generation order, consistent with the algorithm given in Habib M, Medina R, Nourine L and Steiner G (2001).
#' If `LEBubleyDyer` is used, the proper number of linear extensions is randomly
#' sampled by using the Bubley and Dyer (1999) procedure, based on parameter `n` and `error`.
#'
#' @param from_start Logical value indicating whether the linear extensions generator should be reset or not.
#' If `from_start=FALSE`, linear extensions generation starts from the last linear extension generated
#' in the previous `LEGet` call.
#' If `from_start=TRUE`, previous `LEGet` calls do not impact on the new linear extensions generation.
#'
#' In  more details, when `generator` is created via [LEGenerator()], linear extensions are built after a deterministic
#' rule that fixes the generation order. In this case, if `from_start=TRUE` the generation starts from the first
#' linear extension in the generation order; if `from_start=FALSE`, the first linear extension generated is the successor,
#' in the prefixed generation order, to the last one generated by the previous `LEGet` call. This allows
#' the user to generate the set of all linear extensions of a poset in subsequent `LEGet` calls, so as to keep control
#' on computational
#' times and memory space.
#'
#' When `generator` is created via [LEBubleyDyer()], linear extensions are generated according
#' to the Bubley-Dyer sampling procedure that, starting from a randomly generated linear extension,
#' produces a sequence of linear orders in which the \eqn{k}-th one is obtained by a proper
#' random modification of the \eqn{(k-1)}-th one.
#' In this case, if `from_start=TRUE`, the first linear extension is chosen at random; if `from_start=FALSE`,
#' it is obtained by randomly modifying the last one, generated in the previous `LEGet` call.
#'
#' @param n number of linear extensions to be generated.
#'
#' @param error A real number in the interval \eqn{(0,1)} representing the "distance" from uniformity
#' in the sampling distribution of linear extensions.
#' This parameter is used only when `generator` is of class  `BubleyDyerGenerator`. It determines the number
#' of linear extensions to be genrated, in order to achieve the desired "distance" from uniformity,
#' in the sampling distribution of linear extensions.
#' According to Bubley and Dyer (1999), if `error=`\eqn{\epsilon} and \eqn{E} is the
#' number of elements in the poset, then the number \eqn{n_\epsilon} of linear
#' extensions to be sampled is given by
#'
#' \eqn{n_\epsilon=E^4(\ln(E))^2+E^3\ln(E)\ln(\epsilon^{-1})}.
#'
#' If both arguments `n` and `error` are specified by the user, the number of linear extensions actually generated
#' is `n`.
#'
#' @param output_every_sec integer specifying a time interval (in seconds).
#' By specifying this argument, during the execution of `LEGet`, the number of linear extensions
#' actually generated is printed on the R-Console, every `output_every_sec`seconds.
#'
#' @return
#' A matrix whose columns reports the generated linear extensions
#'
#' @references Bubley, R., Dyer, M. (1999). Faster random generation of linear extensions.
#' Discrete Mathematics, 201, 81-88. https://doi.org/10.1016/S0012-365X(98)00333-1
#'
#' 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.
#'
#' @examples
#' el <- c("a", "b", "c", "d", "e")
#'
#' dom <- matrix(c(
#'   "a", "b",
#'   "c", "b",
#'   "c", "d"
#' ), ncol = 2, byrow = TRUE)
#'
#' pos <- POSet(elements = el, dom = dom)
#'
#' LEgen <- LEGenerator(pos)
#' LEmatrix <- LEGet(LEgen)
#'
#' LEgen <- LEGenerator(pos)
#' LEmatrix_first <- LEGet(LEgen, n=10)
#' LEmatrix_second <- LEGet(LEgen, from_start=FALSE)
#' LEmatrix <- cbind(LEmatrix_first,LEmatrix_second)
#'
#' #Randomly generate n=30 linear extensions from the poset
#' LEgen <- LEBubleyDyer(pos)
#' LEmatrix <- LEGet(LEgen, n=30)
#'
#' #Randomly generate n=60 linear extensions from the poset in two steps
#' LEgen <- LEBubleyDyer(pos)
#' LEmatrix_first <- LEGet(LEgen, n=30)
#' LEmatrix_second <- LEGet(LEgen, n=30, from_start=FALSE)
#' LEmatrix <- cbind(LEmatrix_first,LEmatrix_second)
#'
#'#Generates linear extensions from the poset with precision=1
#' LEgen <- LEBubleyDyer(pos)
#' LEmatrix <- LEGet(LEgen, error=0.002)
#'
#' @name LEGet
#' @export LEGet
LEGet <- function(generator, from_start=TRUE, n=NULL, error=NULL, output_every_sec=NULL) {
  if (!methods::is(generator, "LEGenerator")) {
    stop("generator must be of class LEGenerator")
  }
  if (is.null(from_start) || !is.logical(from_start)) {
    stop("from_start must be a logical value (TRUE/FALSE)")
  }
  if (!is.null(n) && (n < 0 || n != round(n))) {
    stop("n must be a positive integer")
  }
  if (!methods::is(generator, "BubleyDyerGenerator") && !is.null(error)) {
    warning("parameter error is not used by the current le-generator")
  }
  if (!is.null(error) && (!is.numeric(error) || length(error) != 1 || (error <= 0.0) || (error >= 1.0))) {
    stop("error must be in (0, 1)")
  }
  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(n)) {
    n <- as.integer(n)
  }

  if (!is.null(output_every_sec)) {
    output_every_sec <- as.integer(output_every_sec)
  }
  tryCatch({
    if (methods::is(generator, "BubleyDyerGenerator")) {
      if (is.null(n) && is.null(error)) {
        stop("parameter n or parameter error must be provided.")
      }
      ptr <- .Call("_LEGBubleyDyerGet", generator@ptr, from_start, n, error, output_every_sec)
    } else {
      ptr <- .Call("_LEGGet", generator@ptr, from_start, n, output_every_sec)
    }
    return (ptr)
  }, error = function(err) {
    err_split <- strsplit(err[[1]], split = ":")
    stop(err_split[[1]][length(err_split[[1]])])
  }) # END tryCatch
}
