## TODO: Remove in the next release
source("modules/Anomalies/compute_anomalies.R")

# 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, retrieve = TRUE) {

  ## TODO: Is this necessary?
  # Check
  if (retrieve) {
    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
    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)
    } 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)
    }
    # 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")
    }
    # Compute forecast anomaly field
    if (!is.null(data$fcst)) {
      # Compute hindcast climatology ensemble mean
      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)))) {
        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)
        clim_hcst <- InsertDim(clim$clim_exp, posdim = 1, lendim = 1,
                               name = "syear")
      } 
      # Store original dimensions
      dims <- dim(clim_hcst)
      ## Is there a better way to do this?
      # Repeat the array as many times as ensemble members
      clim_hcst <- rep(clim_hcst, data$fcst$dims[['ensemble']])
      # Rename and reorder dimensions
      dim(clim_hcst) <- c(dims, ensemble = data$fcst$dims[['ensemble']])
      clim_hcst <- Reorder(clim_hcst, order = names(dim(data$fcst$data)))
      # Get fcst anomalies
      data$fcst$data <- data$fcst$data - clim_hcst
      # Change metadata
      for (var in data$fcst$attrs$Variable$varName) {
	data$fcst$attrs$Variable$metadata[[var]]$long_name <-
	  paste(data$fcst$attrs$Variable$metadata[[var]]$long_name, "anomaly")
      }
    }

    # Display success messages and save data
    if (retrieve) {     
      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 #####")
      if (recipe$Analysis$Workflow$Anomalies$save != 'none') {
        info(recipe$Run$logger, "##### START SAVING ANOMALIES #####")
      }
      # Save outputs
      recipe$Run$output_dir <- paste0(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))
}
