#' @rdname CST_EMBestIndexWeighting
#' @title Computing the weighted ensemble means for SFSs of a CSTools object.
#' @author Eroteida Sanchez-Garcia - AEMET, \email{esanchezg@aemet.es}
#' @description This function implements the computation to obtain the weighted
#' ensemble means for SFSs through Best estimation of one index or driver of
#' climate variability (as e.g., the North Atlantic Oscillation (NAO)) and
#' accepts a CSTools object (an object of the class 's2dv_cube') as input.
#' @references Regionally improved seasonal forecast of precipitation through
#' Best estimation of winter NAO, Sanchez-Garcia, E. et al.,
#' Adv. Sci. Res., 16, 165174, 2019, https://doi.org/10.5194/asr-16-165-2019
#' @param var_exp An object of the class 's2dv_cube' containing the variable
#' (e.g. precipitation, temperature, NAO index) array.
#' The var_exp object is expected to have an element named \code{$data} with
#' three dimensions (time, dataset, member) for a zonal variable 
#' or five dimensions (time, dataset, member, lat, lon) for a spatial variable.
#' @param weights_index_exp An object of the class 's2dv_cube' containing
#' the normalized weights array.
#' The weights_index_exp object is expected to have an element named
#' \code{$data} with dimensions (time, dataset, member, season).
#' @return CST_EMBestIndexWeighting() returns a CSTools object (i.e., of the
#' class 's2dv_cube').
#' This object has at least an element named \code{$data}
#' with three or five dimensions depending if the variable is zonal
#' (as e.g. NAO index)(time, dataset, season) or it is spatial variable
#' (as e.g. precipitation or temperature) (time, dataset, season, lat, lon),
#' containing the ensemble means computing with weighted members.
#' @export
#' @examples
#' var_exp <- 1 : (4 * 2 * 3 * 3 * 2)
#' dim(var_exp) <- c(time = 4, dataset = 2, member = 3, lat = 3, lon = 2)
#' weights_index_exp <- runif(4 * 2 * 3, 0.0001, 0.9999)
#' dim(weights_index_exp) <- c(time = 4, dataset = 2, member = 3, season = 1)
#' var_exp <- list(data = var_exp)
#' weights_index_exp <- list(data = weights_index_exp)
#' class(var_exp) <- 's2dv_cube'
#' class(weights_index_exp) <- 's2dv_cube'
#' res_CST <- CST_EMBestIndexWeighting(var_exp, weights_index_exp)
#' dim(res_CST$data)
#' # time dataset   season    lat     lon
#' #    4       2        1      3       2
CST_EMBestIndexWeighting <- function(var_exp, weights_index_exp) {
  if (!inherits(var_exp, 's2dv_cube') || !inherits(weights_index_exp, 's2dv_cube')) {
    stop("Parameter 'var_exp' and 'weights_index_exp' must be of the class 's2dv_cube', ",
         "as output by CSTools::CST_Load.")
  }
  if (is.null(names(dim(var_exp$data))) 
      || is.null(names(dim(weights_index_exp$data)))) {
    stop("Element 'data' from parameters 'var_exp' and 'weights_index_exp'",
         " should have dimmension names.")
  }
  if(length(dim(var_exp$data)) == 3 ){
    if (!all(names(dim(var_exp$data)) == c('time', 'dataset', 'member'))){ 
      stop("Wrong dimensions of the component 'data' of parameter 'var_exp'")
    } 
  } else if (length(dim(var_exp$data)) == 5 ){
    if (!all(names(dim(var_exp$data)) == c('time', 'dataset', 'member', 
                                           'lat', 'lon'))){ 
      stop("Wrong dimensions of the component 'data' of parameter 'var_exp'")
    }
  } else {
    stop("Wrong dimensions of the component 'data' of parameter 'var_exp'")
  }
  if(length(dim(weights_index_exp$data)) == 4 ){
    if (!all(names(dim(weights_index_exp$data)) == c('time', 'dataset', 
                                                     'member', 'season'))){ 
      stop("Wrong dimensions of the component 'data' of parameter 'weights_index_exp'")
    } 
  } else {
    stop("Wrong dimensions of the component 'data' of parameter 'weights_index_exp'")
  }
  if (dim(var_exp$data)['time'] != dim(weights_index_exp$data)['time']){
    stop("The length of the dimension 'time' of the parameter 'var_exp' ",
         "in the component 'data' must be equal to the length of the dimension",
         " 'time' of the parameter 'weights_index_exp' in the component 'data'")
  }
  if (dim(var_exp$data)['dataset'] != dim(weights_index_exp$data)['dataset']){
    stop("The length of the dimension 'dataset' of the parameter 'var_exp' ",
         "in the component 'data' must be equal to the length of the dimension",
         " 'dataset' of the parameter 'weights_index_exp' in the component 'data'")
  }
  if (dim(var_exp$data)['member'] != dim(weights_index_exp$data)['member']){
    stop("The length of the dimension 'member' of the parameter 'var_exp' ",
         "in the component 'data' must be equal to the length of the dimension",
         " 'member' of the parameter 'weights_index_exp' in the component 'data'")
  }
  
  em <- EMBestIndexWeighting(var_exp$data, weights_index_exp$data)
  em_var_exp <- list()
  em_var_exp$data <- em
  em_var_exp$Datasets <- var_exp$Datasets
  em_var_exp$source_files <- var_exp$source_files
  em_var_exp$Dates <- var_exp$Dates
  return(em_var_exp)
}

#' @rdname EMBestIndexWeighting
#' @title Computing the weighted ensemble means for SFSs.
#' @author Eroteida Sanchez-Garcia - AEMET, \email{esanchezg@aemet.es}
#' @description This function implements the computation to obtain the weighted
#' ensemble means for SFSs through Best estimation of one index or driver of
#' climate variability (as e.g., the North Atlantic Oscillation (NAO)).
#' @references Regionally improved seasonal forecast of precipitation through Best
#' estimation of winter NAO, Sanchez-Garcia, E. et al.,
#' Adv. Sci. Res., 16, 165174, 2019, https://doi.org/10.5194/asr-16-165-2019
#' @param var_exp Variable (e.g. precipitation, temperature, NAO index)
#' array from a SFS with three dimensions (time, dataset, member) for a zonal 
#' variable or dimensions (time, dataset, member, lat, lon) for a spatial 
#' variable.
#' @param weights_index_exp Normalized weights array with dimensions 
#' (time, dataset, member, season).
#' @return EMBestIndexWeighting() returns an array with three or five dimensions
#' depending if the variable is zonal (as e.g. NAO index)(time, dataset, season)
#' or it is spatial variable (as e.g. precipitation or temperature)
#' (time, dataset, season, lat, lon), containing the ensemble means computing
#' with weighted members.
#' @import multiApply
#' @export
#' @examples
#' # Example 1 for the EMBestIndexWeighting function
#' var_exp <- 1 : (2 * 3 * 4)
#' dim(var_exp) <- c(time = 2, dataset = 3, member = 4)
#' weights_index_exp<- runif(24, min=0.001, max=0.999)
#' dim(weights_index_exp) <- c(time = 2, dataset = 3, member = 4, season = 1)
#' res <- EMBestIndexWeighting(var_exp, weights_index_exp)
#' dim(res)
#' # time dataset season
#' #    2       3      1
#' # Example 2 for the EMBestIndexWeighting function
#' var_exp <- 1 : (2 * 3 * 4 * 2 * 3)
#' dim(var_exp) <- c(time = 2, dataset = 3, member = 4, lat = 2, lon = 3)
#' weights_index_exp<- runif(48, min=0.001, max=0.999)
#' dim(weights_index_exp) <- c(time = 2, dataset = 3, member = 4, season = 2)
#' res <- EMBestIndexWeighting(var_exp, weights_index_exp)
#' dim(res)
#' # time dataset   season  lat     lon
#' #    2       3        2    2       3
#'
EMBestIndexWeighting <- function(var_exp, weights_index_exp) {
  res <- Apply(list(var_exp, weights_index_exp),
               target_dims = list(c('time','member'), c('time','member')),
               fun = .EMBestIndexWeighting)$output1
  Dim <- names(dim(var_exp))
  Dim[Dim == 'member'] <- 'season'
  pos <- match(Dim, names(dim(res)))
  res <- aperm(res, pos)                                        
  names(dim(res)) <- Dim
  return(res)
}

#' Atomic EMBestIndexWeighting
#' @param var_exp Variable (e.g. precipitation, temperature, NAO index)
#' array from a SFS with dimensions (time, member).
#' @param weights_index_exp Normalized weights array with dimensions
#' (time, member).
#' @return .EMBestIndexWeighting returns an array of with dimensions (time),
#' the ensemble means computing with weighted members.
#' @examples
#' # Example for the Atomic EMBestIndexWeighting function
#' var_exp <- 1 : 6
#' dim(var_exp) <- c(time = 2, member = 3)
#' weights_index_exp <- c(0.28, 0.15, 0.69, 0.64, 0.42, 0.17)
#' dim(weights_index_exp) <- c(time = 2, member = 3)
#' res <- .EMBestIndexWeighting(var_exp, weights_index_exp)
#' str(res)
#' # num [1:2(1d)] 4.45 3.88
#' dim(res)
#' # time
#' #    2
#' @noRd
.EMBestIndexWeighting <- function(var_exp, weights_index_exp) {
  posTime <- 1
  var_exp_em <- apply(var_exp * weights_index_exp, posTime, sum)
  dim(var_exp_em) <- c(time = length(var_exp_em))
  return(var_exp_em)
}

