# Compute the hcst, obs and fcst anomalies with or without cross-validation
# and return them, along with the hcst and obs climatologies.

Anomalies <- function(recipe, data) {

  if (is.null(recipe$Analysis$Workflow$Anomalies$compute)) {
    error(recipe$Run$logger, 
          paste("The anomaly module has been called, but the element",
                "'Workflow:Anomalies:compute' is missing from the recipe."))
    stop()
  }
  if (recipe$Analysis$Workflow$Anomalies$compute) {
    if (recipe$Analysis$Workflow$Anomalies$cross_validation) {
      cross <- TRUE
      cross_msg <- "with"
    } else {
      cross <- FALSE
      cross_msg <- "without"
    }
    if (is.null(recipe$Analysis$remove_NAs)) {
      na.rm <- FALSE
    } else {
      na.rm <- recipe$Analysis$remove_NAs
    }
    original_dims <- data$hcst$dim

    # Save full fields
    hcst_fullvalue <- data$hcst
    obs_fullvalue <- data$obs
    # Compute anomalies
    # 1. Case where hcst, fcst and obs share the same grid
    if (isTRUE(all.equal(as.vector(data$hcst$coords$latitude),
                         as.vector(data$obs$coords$latitude))) &&
        isTRUE(all.equal(as.vector(data$hcst$coords$longitude),
                         as.vector(data$obs$coord$longitude)))) {
      anom <- CST_Anomaly(data$hcst, data$obs,
                          cross = cross,
                          memb = TRUE,
                          memb_dim = 'ensemble',
                          dim_anom = 'syear',
                          dat_dim = c('dat', 'ensemble'),
                          ftime_dim = 'time',
                          ncores = recipe$Analysis$ncores)
      # Reorder dims
      anom$exp$data <- Reorder(anom$exp$data, names(original_dims))
      anom$obs$data <- Reorder(anom$obs$data, names(original_dims))
      # Hindcast climatology
      data$hcst <- anom$exp
      data$obs <- anom$obs
      remove(anom)
      # Forecast anomalies
      if (!is.null(data$fcst)) {
        clim <- s2dv::Clim(hcst_fullvalue$data, obs_fullvalue$data,
                           time_dim = "syear",
                           dat_dim = c("dat", "ensemble"),
                           memb = FALSE,
                           memb_dim = "ensemble",
                           ftime_dim = "time",
                           ncores = recipe$Analysis$ncores)
        data$fcst$data <- Ano(data = data$fcst$data, clim = clim$clim_exp)
      }
    # 2. Case where hcst and fcst are in a different grid than obs
    } else {
      ## TODO: Remove when cross-validation is implemented for this use case
      if (cross) {
        warn(recipe$Run$logger,
             paste("Anomaly computation in cross-validation has not been",
                   "implemented yet for the case where hcst and obs have",
                   "different grids. The climatology will be computed as",
                   "a simple mean."))
      }
      clim_hcst <- Apply(data$hcst$data,
                         target_dims = c('syear', 'ensemble'),
                         mean,
                         na.rm = na.rm,
                         ncores = recipe$Analysis$ncores)$output1
      clim_obs <- Apply(data$obs$data,
                        target_dims = c('syear', 'ensemble'),
                        mean,
                        na.rm = na.rm,
                        ncores = recipe$Anaysis$ncores)$output1
      data$hcst$data <- Ano(data = data$hcst$data, clim = clim_hcst)
      data$obs$data <- Ano(data = data$obs$data, clim = clim_obs)
      if (!is.null(data$fcst)) {
        data$fcst$data <- Ano(data = data$fcst$data, clim = clim_hcst)
      }
    }
    # Change variable metadata
    for (var in data$hcst$attrs$Variable$varName) {
      # Change hcst longname
      data$hcst$attrs$Variable$metadata[[var]]$long_name <-
        paste(data$hcst$attrs$Variable$metadata[[var]]$long_name, "anomaly")
      # Change obs longname
      data$obs$attrs$Variable$metadata[[var]]$long_name <- 
        paste(data$obs$attrs$Variable$metadata[[var]]$long_name, "anomaly")
      if (!is.null(data$fcst)) {
        data$fcst$attrs$Variable$metadata[[var]]$long_name <-
          paste(data$fcst$attrs$Variable$metadata[[var]]$long_name, "anomaly")
      }
    }
    info(recipe$Run$logger,
         paste("The anomalies have been computed,", cross_msg,
               "cross-validation. The original full fields are returned as",
               "$hcst.full_val and $obs.full_val."))

    info(recipe$Run$logger, "##### ANOMALIES COMPUTED SUCCESSFULLY #####")

    # Save outputs
    if (recipe$Analysis$Workflow$Anomalies$save != 'none') {

      info(recipe$Run$logger, "##### START SAVING ANOMALIES #####")
      recipe$Run$output_dir <- file.path(recipe$Run$output_dir,
                                      "outputs", "Anomalies")
      # Save forecast
      if ((recipe$Analysis$Workflow$Anomalies$save %in% 
           c('all', 'exp_only', 'fcst_only')) && !is.null(data$fcst)) {
        save_forecast(recipe = recipe, data_cube = data$fcst, type = 'fcst')
      }
      # Save hindcast
      if (recipe$Analysis$Workflow$Anomalies$save %in%
          c('all', 'exp_only')) {
        save_forecast(recipe = recipe, data_cube = data$hcst, type = 'hcst')
      }
      # Save observation
      if (recipe$Analysis$Workflow$Anomalies$save == 'all') {
        save_observations(recipe = recipe, data_cube = data$obs)
      }
    }

  } else {
    warn(recipe$Run$logger, paste("The Anomalies module has been called, but",
         "recipe parameter Workflow:anomalies:compute is set to FALSE.",
         "The full fields will be returned."))
    hcst_fullvalue <- NULL
    obs_fullvalue <- NULL
    info(recipe$Run$logger, "##### ANOMALIES NOT COMPUTED #####")
  }

  ## TODO: Return fcst full value?
  .log_memory_usage(recipe$Run$logger, "After computing anomalies")

  return(list(hcst = data$hcst, obs = data$obs, fcst = data$fcst,
              hcst.full_val = hcst_fullvalue, obs.full_val = obs_fullvalue))

}
