#' @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 at least three dimensions (time, statistic = 2, season).
#' It could have one or two dimension (dataset and/or member) equal to 1.
#' 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 (!any(names(dim(pdf_exp$data)) == 'statistic')){
    stop("The component 'data' of the parameter 'pdf_exp' must be the ",
         "dimension 'statistic'")
  }
  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 (any(names(dim(pdf_exp$data)) == 'member') &
        dim(pdf_exp$data)['member'] != 1) {
    stop("The length of the dimension 'member' in the component 'data' ",
         "of the parameter 'pdf_exp' must be equal to 1.")
  }
  if (any(names(dim(pdf_exp$data)) == 'dataset') &
        dim(pdf_exp$data)['dataset'] != 1) {
    stop("The length of the dimension 'dataset' in the component 'data' ",
         "of the parameter 'pdf_exp' must be equal to 1.")
  }
  if (!any(names(dim(index_exp$data)) == 'time')){
    stop("The component 'data' of the parameter 'index_exp' must be the ",
         "dimension 'time'")
  }
  if (!any(names(dim(pdf_exp$data)) == 'time')){
    stop("The component 'data' of the parameter 'pdf_exp' must be the ",
         "dimension 'time'")
  }
  if (dim(index_exp$data)['time'] != dim(pdf_exp$data)['time']){
    stop("The length of the dimension 'time' of the parameter 'index_exp' ",
         "in the component 'data' must be equal to the length of the dimension",
         " 'time' of the parameter 'pdf_exp' in the component 'data'")
  }
  if (dim(index_exp$data)['time'] != dim(pdf_exp$data)['time']){
    stop("The length of the dimension 'time' of the parameter 'index_exp' ",
         "in the component 'data' must be equal to the length of the dimension",
         " 'time' of the parameter 'pdf_exp' in the component 'data'")
  }
  if (dim(index_exp$data)['season'] != dim(pdf_exp$data)['season']){
    stop("The length of the dimension 'season' of the parameter 'index_exp' ",
         "in the component 'data' must be equal to the length of the dimension",
         " 'season' of the parameter 'pdf_exp' in the component 'data'")
  }
  if (!any(names(dim(index_exp$data)) == 'member')){
    stop("The component 'data' of the parameter 'index_exp' must be the ",
         "dimension 'member'")
  }
  if (any(names(dim(pdf_exp$data)) == 'member')) {
    Dim <- dim(pdf_exp$data)
    dim(pdf_exp$data) <- Dim[-which(names(Dim) == 'member')]
  }
  if (any(names(dim(pdf_exp$data)) == 'dataset')) {
    Dim <- dim(pdf_exp$data)
    dim(pdf_exp$data) <- Dim[-which(names(Dim) == 'dataset')]
  }
  dimnames <- names(dim(index_exp$data))
  aWeigths <- WeightIndex(index_exp$data, pdf_exp$data)
  pos <- match(dimnames, names(dim(aWeigths)))
  aWeigths <- aperm(aWeigths, pos)
  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).
#' @param pdf_exp Statistics array to define a Gaussian PDF with dimensions
#' (time, statistic = 2) 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).
#' @import multiApply
#' @export
#' @examples
#' # Example for the WeightIndex function
#' index_exp <- 1 : (10 * 3 * 5)
#' dim(index_exp) <- c(time = 10, dataset = 3, member = 5)
#' pdf_exp <- 1 : (10 * 2)
#' dim(pdf_exp) <- c(time = 10, statistic = 2)
#' res <- WeightIndex(index_exp, pdf_exp)
#' dim(res)
#' # time   dataset    member
#' #   10         3         5
#' str(res)
#' # num [1:10, 1:3, 1:5] 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)
  aweights <- aweights$output1
  dimnames <- names(dim(index_exp))
  pos <- match(dimnames, names(dim(aweights)))
  aweights <- aperm(aweights, pos)
  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)
}
