#'Computes Effective Sample Size With Classical Method
#'
#'Computes the effective number of independent values along the posdim 
#'dimension of a matrix.\cr
#'This effective number of independent observations can be used in 
#'statistical/inference tests.\cr
#'Based on eno function from Caio Coelho from rclim.txt.
#'
#'@param obs A numeric n-dimensional array with or without named dimensions.
#'@param posdim A character or an integer indicating the dimension along 
#'  which to compute the effective sample size.
#'@param na.action Function to be called to handle missing values, can be 
#'  na.pass or na.fail. Default: na.pass
#'@param ncores The number of cores to use for parallel computation. Default: NULL
#'
#'@return An array with the same dimensions as obs except the posdim dimension.
#'
#'@keywords datagen
#'@author History:\cr
#'0.1  -  2011-05  (V. Guemas, \email{virginie.guemas at ic3.cat})  -  Original code\cr
#'1.0  -  2013-09  (N. Manubens, \email{nicolau.manubens at ic3.cat})  -  Formatting to R CRAN
#'3.0  -  2019-08  (A. Ho, \email{an.ho@bsc.es})  -  Adopt multiApply
#'
#'@examples
#'# See examples on Load() to understand the first lines in this example
#'  \dontrun{
#'data_path <- system.file('sample_data', package = 's2dverification')
#'exp <- list(
#'         name = 'experiment',
#'         path = file.path(data_path, 'model/$EXP_NAME$/monthly_mean',
#'                          '$VAR_NAME$_3hourly/$VAR_NAME$_$START_DATES$.nc')
#'       )
#'obs <- list(
#'         name = 'observation',
#'         path = file.path(data_path, 'observation/$OBS_NAME$/monthly_mean',
#'                          '$VAR_NAME$/$VAR_NAME$_$YEAR$$MONTH$.nc')
#'       )
#'# Now we are ready to use Load().
#'startDates <- c('19851101', '19901101', '19951101', '20001101', '20051101')
#'sampleData <- Load('tos', list(exp), list(obs), startDates,
#'                   leadtimemin = 1, leadtimemax = 4, output = 'lonlat',
#'                   latmin = 27, latmax = 48, lonmin = -12, lonmax = 40)
#'  }
#'  \dontshow{
#'startDates <- c('19851101', '19901101', '19951101', '20001101', '20051101')
#'sampleData <- s2dverification:::.LoadSampleData('tos', c('experiment'), 
#'                                                c('observation'), startDates,
#'                                                output = 'lonlat', 
#'                                                latmin = 27, latmax = 48, 
#'                                                lonmin = -12, lonmax = 40) 
#'  }
#'sampleData$mod <- Season(sampleData$mod, 4, 11, 1, 12)
#'eno <- Eno(sampleData$mod[1, 1, , 1, , ], 1)
#'PlotEquiMap(eno, sampleData$lon, sampleData$lat)
#'
#'@importFrom stats acf na.pass na.fail
#'@import multiApply
#'@export
Eno <- function(obs, posdim, na.action = na.pass, ncores = NULL) {

  #Check params:

  if (is.null(obs)) {
    stop("Parameter 'obs' cannot be NULL.")
  }
  if (!is.numeric(obs)) {
    stop("Parameter 'obs' must be a numeric array.")
  }

  if (is.null(posdim)) {
    stop("Parameter 'posdim' cannot be NULL.")
  }

  if (!is.numeric(posdim) && !is.character(posdim)) {
      stop("Parameter 'posdim' must be an positive integer or character.")
  }

  if (is.numeric(posdim)) {
    if (posdim <= 0 | posdim %% 1 != 0) {
      stop("Parameter 'posdim' must be a positive integer or character.")
    } else if (posdim > length(dim(obs))) {
      stop("Parameter 'posdim' excesses the dimension length of parameter 'obs'.")
    } else {  #posdim is okay for being integer. But change it to character.
      if (!is.null(names(dim(obs)))) {
        posdim <- names(dim(obs))[posdim]
      } else {
        names(dim(obs)) <- paste0("D", 1:length(dim(obs)))
        posdim <- names(dim(obs))[posdim]
      }
    }
  }

  if (is.character(posdim)) {
    if (is.null(names(dim(obs)))) {
      stop(paste0("Parameter 'obs' doesn't have dimension names. ", 
                  "Add names or specify parameter 'posdim' by number."))
    } else if (!any(posdim %in% names(dim(obs)))) {
      stop(paste0("Parameter 'posdim' does not match any dimension name of ",
                  "parameter 'obs'."))
    }
  }

  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(obs))) {
    stop(paste0("NA value in paratemter 'obs', which is not accepted when ",
                "parameter 'na.action' = 'na.fail'."))
  }

  if (!is.null(ncores)) {
    if (!is.numeric(ncores)) {
      stop("Parameter 'ncores' must be a positive integer.")
    } else if (ncores <= 0 | ncores %% 1 != 0) {
      stop("Parameter 'ncores' must be a positive integer.")
    }
  }

  #Calculation:

  effnumobs <- Apply(data = list(obs),
                     target_dims = posdim,
                     fun = .Eno,
                     na.action = na.action,
                     ncores = ncores)$output1

  return(effnumobs)
}

.Eno <- function(x, na.action) {
  # Checks:
  if (!is.numeric(x)) {
      stop("Parameter 'x' must be a numeric vector.")
  }

  if (length(sort(x)) > 1) {
    n <- length(sort(x))
#    if (n == 0) {
#      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
  }
  eno
}

