#' @rdname CST_WeightIndex
#' @title Computing the weights for SFSs using the Best Index PDFs
#' of a CSTools object
#' @author Eroteida Sanchez-Garcia - AEMET, \email{esanchezg@aemet.es}
#' @description This function implements the computation to obtain the
#' normalized weights for SFS members using the Best Index estimation 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 index_exp An object of the class 's2dv_cube' containing the indices
#' or drivers of climate variability (as e.g., the North Atlantic Oscillation
#' (NAO)) estimate from SFSs.
#' The index_exp object is expected to have an element named \code{$data}
#' with dimensions (time, dataset, member, season).
#' @param pdf_exp An object of the class 's2dv_cube' containing the statistics
#' which define a Gaussian PDFs for weighting the values estimate for the SFS
#' ensemble members.
#' The pdf_exp object is expected to have an element named \code{$data}
#' with dimensions (time, statistic = 2, season).
#' The firt statistic is the parameter 'mean' of the PDFs and
#' the second statistic is the parameter 'standard deviation' of the PDFs.
#' @return CST_WeightIndex() returns a CSTools object (i.e., of the
#' class 's2dv_cube').
#' This object has at least an element named \code{$data}
#' with dimensions (time, dataset, member, season).
#' @export
#' @examples
#' index_exp <- 1 : (5 * 3 * 7 * 4)
#' dim(index_exp) <- c(time = 5, dataset = 3, member = 7, season = 4)
#' index_exp <- list(data = index_exp)
#' pdf_exp <- 1 : (5 * 2 * 4)
#' dim(pdf_exp) <- c(time = 5, statistic = 2, season = 4)
#' pdf_exp <- list(data = pdf_exp)
#' class(index_exp) <- 's2dv_cube'
#' class(pdf_exp) <- 's2dv_cube'
#' res_CST <- CST_WeightIndex(index_exp, pdf_exp)
#' dim(res_CST$data)
#' #time   dataset member  season
#' # 5         3        7       4
CST_WeightIndex <- function(index_exp, pdf_exp) {
  if (!inherits(index_exp, 's2dv_cube') || !inherits(pdf_exp, 's2dv_cube')) {
    stop("Parameter 'index_exp' and 'pdf_exp' must be of the class 's2dv_cube', ",
         "as output by CSTools::CST_Load.")
  }
  if (is.null(names(dim(index_exp$data))) || is.null(names(dim(pdf_exp$data)))) {
    stop("Element 'data' from parameters 'index_exp' and 'pdf_exp'",
         " should have dimmension names.")
  }
  if(length(dim(index_exp$data)) != 4){
    stop("The number of dimensions in the component 'data' ",
         "of the parameter 'index_exp' must be 4")
  } else if (!all(names(dim(index_exp$data)) == c('time', 'dataset', 'member', 
                                                'season'))) {
    stop("Wrong dimensions of the component 'data' of parameter 'index_exp'")
  }
  if(length(dim(pdf_exp$data)) != 3){
    stop("The number of dimensions in the component 'data' ",
         "of the parameter 'pdf_exp' must be 3")
  } else if (!all(names(dim(pdf_exp$data)) == c('time', 'statistic', 
                                                'season'))){
      stop("Wrong dimensions of the component 'data' of parameter 'pdf_exp'")
  }
  if (dim(pdf_exp$data)['statistic'] != 2) {
    stop("The length of the dimension 'statistic' in the component 'data' ",
         "of the parameter 'pdf_exp' must be equal to 2.")
  }
  if (dim(index_exp$data)['time'] != dim(pdf_exp$data)['time']) {
    stop("The length of the dimensions 'time' in the component 'data' ",
         "of the parameter 'index_exp' and 'pdf_exp' must be equals.")
  }
  if (dim(index_exp$data)['season'] != dim(pdf_exp$data)['season']) {
    stop("The length of the dimensions 'season' in the component 'data' ",
         "of the parameter 'index_exp' and 'pdf_exp' must be equals.")
  }
  
  
  dimnames <- names(dim(index_exp$data))
  aWeigths <- WeightIndex(index_exp$data, pdf_exp$data)
  if((dim(aWeigths)[1] != dim(index_exp$data)[1]) ||
     (dim(aWeigths)[2] != dim(index_exp$data)[2]) ||
     (dim(aWeigths)[3] != dim(index_exp$data)[3]) ||
     (dim(aWeigths)[4] != dim(index_exp$data)[4])){
    stop("Wrong dimensions of variable 'aWeigths'")
  }
  names(dim(aWeigths)) <- dimnames
  weights_index_exp <- list()
  weights_index_exp$data <- aWeigths
  weights_index_exp$Datasets <- list(exp = index_exp$Datasets,
                                     pdf = pdf_exp$Datasets)
  weights_index_exp$source_files <- list(exp = index_exp$source_files,
                                         pdf = pdf_exp$source_files)
  weights_index_exp$Dates <- index_exp$Dates
  class(weights_index_exp)  <- 's2dv_cube'
  return(weights_index_exp)
}

#' @rdname WeightIndex
#' @title Computing the weights for SFSs using the Best Index PDFs.
#' @author Eroteida Sanchez-Garcia - AEMET, \email{esanchezg@aemet.es}
#' @description This function implements the computation to obtain the
#' normalized weights for each member of each SFS or dataset using the
#' Best Index estimation.
#' @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 index_exp Index (e.g. NAO index) array, from a dataset of SFSs
#' for a period of years, with dimensions (time, dataset, member, season).
#' @param pdf_exp Statistics array to define a Gaussian PDF with dimensions
#' (time, statistic = 2, season) for a period of years.
#' The firt statistic is the parameter 'mean' of the PDF and
#' the second statistic is the parameter 'standard deviation' of the PDF.
#' @return WeightIndex() returns a normalized weights array with dimensions
#' (time, dataset, member, season).
#' @import multiApply
#' @export
#' @examples
#' # Example for the WeightIndex function
#' index_exp <- 1 : (10 * 3 * 5 * 1)
#' dim(index_exp) <- c(time = 10, dataset = 3, member = 5, season = 1)
#' pdf_exp <- 1 : (10 * 2 * 1)
#' dim(pdf_exp) <- c(time = 10, statistic = 2, season = 1)
#' res <- WeightIndex(index_exp, pdf_exp)
#' dim(res)
#' # time   dataset    member season
#' #   10         3         5      1
#' str(res)
#' # num [1:10, 1:3, 1:5, 1] 0.976 0.958 0.935 0.908 0.881 ...
#'
WeightIndex <- function(index_exp, pdf_exp) {
  aweights <- Apply(list(index_exp, pdf_exp),
                    target_dims = list('member', 'statistic'),
                    fun = .WeightIndex)$output1
  
  dimnames <- names(dim(index_exp))
  pos <- match(dimnames, c('member', 'time', 'dataset', 'season'))
  aweights <- aperm(aweights, pos)
  names(dim(aweights)) <- dimnames
  return(aweights)

}

#' Atomic WeightIndex
#' @param index_exp Index (e.g. NAO index) array from a SFS with dimensions
#' (member)
#' @param pdf_exp Statistics array to define a Gaussian PDF with dimensions
#' (statistic = 2).
#' The firt statistic is the parameter 'mean' of the PDF and
#' the second statistic is the parameter 'standard deviation' of the PDF.
#' @return .WeightIndex returns an array of with dimensions (member),
#' the normalized weights for each member of a SFS using a Best NAO PDF.
#' @examples
#' # Example for the Atomic WeightIndex function
#' index_exp <- c(1.3,3,-1)
#' dim(index_exp) <- c(member = 3)
#' pdf_exp <- c(1.5,0.8)
#' dim(pdf_exp) <- c(statistic = 2)
#' res <- .WeightIndex(index_exp, pdf_exp)
#' str(res)
#' # num [1:3(1d)] 0.84338 0.15003 0.00659
#' dim(res)
#' # member
#' #      3
#' @noRd
.WeightIndex <- function(index_exp, pdf_exp) {
  aweights <- apply(index_exp, 1, dnorm, mean = pdf_exp[1], sd = pdf_exp[2])
  dim(aweights) <- dim(index_exp)
  sumWeights <- sum(aweights)
  aweightsNorm <- apply(aweights, 1, NormWeight, sumWeights)
  dim(aweightsNorm) <- dim(index_exp)
  return(aweightsNorm)
}

# Auxiliar function to normalize a weight value
NormWeight <- function(weight, sumWeights) {
  return(weight/sumWeights)
}
