#'Compute the Mean Bias Skill Score
#'
#'The Mean Bias Skill Score is based on the Mean Error (Wilks, 2011)
#'between the ensemble mean forecast and the observations. It measures
#'the accuracy of the forecast and it can be used to assess whether a
#'forecast presents an improvement or a worsening with respect to a reference
#'forecast. The Mean Bias Skill Score ranges between minus infinite and 1.
#'Positive values indicate that the forecast has higher skill than the 
#'reference forecast, while negative values indicate that it has a lower skill.
#'Examples of reference forecasts are the climatological forecast (average of 
#'the observations), a previous model version, or another model. It is computed
#'as BiasSS = 1 - Bias_exp / Bias_ref. The statistical significance is obtained
#'based on a Random Walk test at the 95% confidence level (DelSole and Tippett,
#'2016).
#'
#'@param exp A named numerical array of the forecast with at least time 
#'  dimension.  
#'@param obs A named numerical array of the observation with at least time 
#'  dimension. The dimensions must be the same as 'exp' except 'memb_dim' and 
#'  'dat_dim'.
#'@param ref A named numerical array of the reference forecast data with at 
#'  least time and member dimension. The dimensions must be the same as 'exp' 
#'  except 'memb_dim' and 'dat_dim'. If there is only one reference dataset,
#'  it should not have dataset dimension. If there is corresponding reference 
#'  for each experiement, the dataset dimension must have the same length as in
#'  'exp'. If 'ref' is NULL, the climatological forecast is used as reference 
#'  forecast. The default value is NULL.
#'@param time_dim A character string indicating the name of the time dimension.
#'  The default value is 'sdate'.
#'@param memb_dim A character string indicating the name of the member dimension
#'  to compute the ensemble mean; it should be set to NULL if the parameter 'exp'
#'  is already the ensemble mean. The default value is NULL.
#'@param ncores An integer indicating the number of cores to use for parallel 
#'  computation. The default value is NULL.
#'
#'@return
#'\item{$biasSS}{
#'  A numerical array of BiasSS with dimensions the dimensions of
#'  'exp' except 'time_dim' and 'memb_dim' dimensions.
#'}
#'\item{$sign}{
#'  A logical array of the statistical significance of the BiasSS
#'  with the same dimensions as $biasSS.  
#'}
#'
#'@references 
#'Wilks, 2011; https://doi.org/10.1016/B978-0-12-385022-5.00008-7
#'DelSole and Tippett, 2016; https://doi.org/10.1175/MWR-D-15-0218.1
#'
#'@examples
#'exp <- array(rnorm(1000), dim = c(dat = 1, lat = 3, lon = 5, member = 10, year = 50))
#'ref <- array(rnorm(1000), dim = c(dat = 1, lat = 3, lon = 5, member = 10, year = 50))
#'obs <- array(rnorm(1000), dim = c(dat = 1, lat = 3, lon = 5, year = 50))
#'biasSS1 <- BiasSS(exp = exp, obs = obs, ref = ref, time_dim = 'year', memb_dim = 'member', na.rm = FALSE, ncores = 1)
#'biasSS2 <- BiasSS(exp = exp, obs = obs, ref = NULL, time_dim = 'year', memb_dim = 'member', na.rm = FALSE, ncores = 1)
#'
#'@import multiApply
#'@export

BiasSS <- function(exp, obs, ref = NULL, time_dim = 'sdate', memb_dim = NULL, na.rm = FALSE, ncores = NULL) {
  
  ## Checks
  
  
  ## Ensemble mean
  if (!is.null(memb_dim)) {
    exp <- MeanDims(exp, memb_dim, na.rm = na.rm)
    if (!is.null(ref)) {
      ref <- MeanDims(ref, memb_dim, na.rm = na.rm)
    }
  }
  
  ## Mean bias skill score
  if (is.null(ref)) {
    ss <- multiApply::Apply(data = list(exp, obs),
                            target_dims = time_dim,
                            fun = .BiasSS, na.rm = na.rm,
                            ref = ref, ncores = ncores)
    
  } else {
    ss <- multiApply::Apply(data = list(exp, obs, ref),
                            target_dims = time_dim,
                            fun = .BiasSS, na.rm = na.rm,
                            ncores = ncores)
  }
  return(ss)
}

.BiasSS <- function(exp, obs, ref = NULL, na.rm = FALSE) {
  
  if (isTRUE(na.rm)) {
    if (is.null(ref)) {
      good_values <- !is.na(exp) & !is.na(obs)
      exp <- exp[good_values]
      obs <- obs[good_values]
    } else {
      good_values <- !is.na(exp) & !is.na(ref) & !is.na(obs)
      exp <- exp[good_values]
      ref <- ref[good_values]
      obs <- obs[good_values]
    }
  }
  
  ## Bias of the exp
  bias_exp <- .Bias(exp = exp, obs = obs)
  
  ## Bias of the ref
  if (is.null(ref)) { ## Climatological forecast
    ref <- mean(obs, na.rm = na.rm)
  }
  bias_ref <- .Bias(exp = ref, obs = obs)
  
  ## Skill score and significance
  ss <- list()
  ss$biasSS <- 1 - mean(abs(bias_exp), na.rm = na.rm) / mean(abs(bias_ref), na.rm = na.rm)
  ss$sign <- .RandomWalkTest(skill_A = bias_exp, skill_B = bias_ref)$signif
  
  return(ss)
}
