#'Compute Effective Sample Size With Classical Method
#'
#'Compute the number of effective samples along one dimension of an array. This
#'effective number of independent observations can be used in statistical/inference 
#'tests.\cr
#'The calculation is based on eno function from Caio Coelho from rclim.txt.
#'
#'@param data A numeric array with named dimensions.
#'@param sdate_dim A function indicating the dimension along 
#'  which to compute the effective sample size. Default value is 'sdate'.
#'@param na.action A charater string indicating the action for missing values,  
#'  can be na.pass (missing values are allowed) or na.fail (no missing values 
#'  are allowed). See details in stats::acf(). Default value is 'na.pass'.
#'@param ncores An integer indicating the number of cores to use for parallel 
#'  computation. Default value is NULL.
#'
#'@return An array with the first dimension 'stats', which is the number of 
#'  effective samples, and the following dimensions same as parameter 'data' 
#'  except the sdate_dim dimension.
#'
#'@keywords datagen
#'@author History:\cr
#'0.1  -  2011-05  (V. Guemas, \email{virginie.guemas@ic3.cat})  -  Original code\cr
#'1.0  -  2013-09  (N. Manubens, \email{nicolau.manubens@ic3.cat})  -  Formatting to R CRAN
#'3.0  -  2019-12  (A. Ho, \email{an.ho@bsc.es})  - Adopt multiApply
#'
#'@examples
#'set.seed(1)
#'data <- array(rnorm(800), dim = c(dataset = 1, member = 2, sdate = 4, 
#'                                  ftime = 4, lat = 10, lon = 10))
#'na <- floor(runif(40, min = 1, max = 800))
#'data[na] <- NA
#'res <- Eno(data)
#'
#'@importFrom stats acf na.pass na.fail
#'@import multiApply
#'@export
Eno <- function(data, sdate_dim = 'sdate', na.action = na.pass, ncores = NULL) {

  # Check inputs 
  ## data
  if (is.null(data)) {
    stop("Parameter 'data' cannot be NULL.")
  }
  if (!is.numeric(data)) {
    stop("Parameter 'data' must be a numeric array.")
  }
  if (is.null(dim(data))) {  #is vector
    dim(data) <- c(length(data))
    names(dim(data)) <- sdate_dim
  }
  if(any(is.null(names(dim(data))))| any(nchar(names(dim(data))) == 0)) {
    stop("Parameter 'data' must have dimension names.")
  }
  ## sdate_dim
  if (!is.character(sdate_dim) | length(sdate_dim) > 1) {
    stop("Parameter 'sdate_dim' must be a character string.")
  }
  if (!sdate_dim %in% names(dim(data))) {
    stop("Parameter 'sdate_dim' is not found in 'data' dimension.")
  }
  ## na.action
  if (as.character(substitute(na.action)) != c("na.pass") &&
      as.character(substitute(na.action)) != c("na.fail")) {
      stop("Parameter 'na.action' must be either 'na.pass' or 'na.fail'.")
  }
  if(as.character(substitute(na.action))== c("na.fail") && any(is.na(data))) {
    stop(paste0("Calculation fails because NA is found in paratemter 'data', ",
                "which is not accepted when ",
                "parameter 'na.action' = 'na.fail'."))
  }
  ## ncores
  if (!is.null(ncores)) {
    if (!is.numeric(ncores) | ncores %% 1 != 0 | ncores < 0 |
      length(ncores) > 1) {
      stop("Parameter 'ncores' must be a positive integer.")
    }
  }

  ###############################
  # Calculate Eno

  eno <- Apply(data = list(data),
               target_dims = sdate_dim,
               output_dims = 'stats',
               fun = .Eno,
               na.action = na.action,
               ncores = ncores)$output1

  return(eno)
}

.Eno <- function(x, na.action) {
  n <- length(sort(x))
  if (n > 1) {
    a <- acf(x, lag.max = n - 1, plot = FALSE,
             na.action = na.action)$acf[2:n, 1, 1]
    s <- 0
    for (k in 1:(n - 1)) {
      s <- s + (((n - k) / n) * a[k])
    }
    eno <- min(n / (1 + (2 * s)), n)
  } else {
    eno <- NA
  }
  return(as.array(eno))
}
