## TODO: Save obs percentiles

source("modules/Saving/paths2save.R")

save_data <- function(recipe, data,
		      skill_metrics = NULL,
		      probabilities = NULL) {
  # Wrapper for the saving functions.
  # recipe: The auto-s2s recipe
  # archive: The auto-s2s archive
  # data: output of load_datasets()
  # data: output of calibrate_datasets()
  # skill_metrics: output of compute_skill_metrics()
  # probabilities: output of compute_probabilities()
  # mean_bias: output of compute_mean_bias()

  # Sanity checks
  if (is.null(recipe)) {
    error(recipe$Run$logger, "The 'recipe' parameter is mandatory.")
    stop()
  }

  if (is.null(data)) {
    error(recipe$Run$logger,
	  paste("The 'data' parameter is mandatory. It should be a list",
		"of at least two s2dv_cubes containing the hcst and obs."))
    stop()
  }
  # Create output directory
  outdir <- get_dir(recipe)
  dir.create(outdir, showWarnings = FALSE, recursive = TRUE)

  # Export hindcast, forecast and observations onto outfile
  save_forecast(recipe = recipe, data_cube = data$hcst,
		type = 'hcst',
		outdir = outdir)
  if (!is.null(data$fcst)) {
    save_forecast(recipe = recipe, data_cube = data$fcst,
		  type = 'fcst',
		  outdir = outdir)
  }
  save_observations(recipe = recipe, data_cube = data$obs,
		    outdir = outdir)

  # Separate ensemble correlation from the rest of the metrics, as it has one
  # extra dimension "ensemble" and must be saved to a different file
  if ("corr" %in% names(skill_metrics)) {
    corr_metric_names <- grep("^corr", names(skill_metrics))
    corr_metrics <- skill_metrics[corr_metric_names]
    skill_metrics <- skill_metrics[-corr_metric_names]
    if (length(skill_metrics) == 0) {
      skill_metrics <- NULL
    }
  } else {
    corr_metrics <- NULL
  }

  # Export skill metrics onto outfile
  if (!is.null(skill_metrics)) {
    save_metrics(recipe = recipe, skill = skill_metrics,
		 data_cube = data$hcst,
		 outdir = outdir)
  }
  if (!is.null(corr_metrics)) {
    save_corr(recipe = recipe, skill = corr_metrics,
	      data_cube = data$hcst,
	      outdir = outdir)
  }
  
  # Export probabilities onto outfile
  if (!is.null(probabilities)) {
    save_percentiles(recipe = recipe, percentiles = probabilities$percentiles,
		     data_cube = data$hcst,
		     outdir = outdir)
    save_probabilities(recipe = recipe, probs = probabilities$probs,
		       data_cube = data$hcst,
		       type = "hcst",
		       outdir = outdir)
    if (!is.null(probabilities$probs_fcst)) {
      save_probabilities(recipe = recipe, probs = probabilities$probs_fcst, 
			 data_cube = data$fcst,
			 type = "fcst",
			 outdir = outdir)
    }
  }
}

get_global_attributes <- function(recipe, archive) {
  # Generates metadata of interest to add to the global attributes of the
  # netCDF files.
  parameters <- recipe$Analysis
  hcst_period <- paste0(parameters$Time$hcst_start, " to ",
			parameters$Time$hcst_end)
  current_time <- paste0(as.character(Sys.time()), " ", Sys.timezone())
  system_name <- parameters$Datasets$System$name
  reference_name <- parameters$Datasets$Reference$name

  attrs <- list(reference_period = hcst_period,
		institution_system = archive$System[[system_name]]$institution,
		institution_reference = archive$Reference[[reference_name]]$institution,
		system = system_name,
	        reference = reference_name,
		calibration_method = parameters$Workflow$Calibration$method,
		computed_on = current_time)
  
  return(attrs)
}

get_times <- function(store.freq, fcst.horizon, leadtimes, sdate, calendar) {
  # Generates time dimensions and the corresponding metadata.
  ## TODO: Subseasonal

  switch(fcst.horizon,
         "seasonal" = {time <- leadtimes; ref <- 'hours since '; 
                       stdname <- paste(strtoi(leadtimes), collapse=", ")},
         "subseasonal" = {len <- 4; ref <- 'hours since ';
                          stdname <- ''},
         "decadal" = {time <- leadtimes; ref <- 'hours since ';
                      stdname <- paste(strtoi(leadtimes), collapse=", ")})

  dim(time) <- length(time)
  sdate <- as.Date(sdate, format = '%Y%m%d') # reformatting
  metadata <- list(time = list(units = paste0(ref, sdate, 'T00:00:00'),
			       calendar = calendar))
  attr(time, 'variables') <- metadata
  names(dim(time)) <- 'time'
  
  sdate <- 1:length(sdate)
  dim(sdate) <- length(sdate)
  metadata <- list(sdate = list(standard_name = paste(strtoi(sdate),
						      collapse=", "),
                                units = paste0('Init date')))
  attr(sdate, 'variables') <- metadata
  names(dim(sdate)) <- 'sdate'

  return(list(time=time))
}

get_latlon <- function(latitude, longitude) {
  # Adds dimensions and metadata to lat and lon
  # latitude: array containing the latitude values
  # longitude: array containing the longitude values
  
  dim(longitude) <- length(longitude)
  metadata <- list(longitude = list(units = 'degrees_east'))
  attr(longitude, 'variables') <- metadata
  names(dim(longitude)) <- 'longitude'
  
  dim(latitude) <- length(latitude)
  metadata <- list(latitude = list(units = 'degrees_north'))
  attr(latitude, 'variables') <- metadata
  names(dim(latitude)) <- 'latitude'

  return(list(lat=latitude, lon=longitude))

}

save_forecast <- function(recipe,
			  data_cube,
			  type = "hcst",
			  agg = "global",
                          outdir = NULL) {
  # Loops over the years in the s2dv_cube containing a hindcast or forecast
  # and exports each year to a netCDF file.
  # data_cube: s2dv_cube containing the data and metadata
  # recipe: the auto-s2s recipe
  # outdir: directory where the files should be saved
  # agg: aggregation, "global" or "country"

  lalo <- c('longitude', 'latitude')
  archive <- get_archive(recipe)
  dictionary <- read_yaml("conf/variable-dictionary.yml")
  variable <- data_cube$attrs$Variable$varName
  var.longname <- data_cube$attrs$Variable$metadata[[variable]]$long_name
  global_attributes <- get_global_attributes(recipe, archive)
  fcst.horizon <- tolower(recipe$Analysis$Horizon)
  store.freq <- recipe$Analysis$Variables$freq
  calendar <- archive$System[[global_attributes$system]]$calendar

  if (is.null(outdir)) {
    outdir <- get_dir(recipe)
  }

  # Generate vector containing leadtimes
  dates <- as.PCICt(ClimProjDiags::Subset(data_cube$attrs$Dates, 'syear', 1),
		    cal = calendar)
  if (fcst.horizon == 'decadal') {
    ## Method 1: Use the first date as init_date. But it may be better to use 
    ## the real initialized date (ask users)
    # init_date <- as.Date(data_cube$Dates$start[1], format = '%Y%m%d')
    ## Method 2: use initial month
    init_month <- archive$System[[recipe$Analysis$Datasets$System$name]]$initial_month
    if (type == 'hcst') {
      init_date <- as.PCICt(paste0(recipe$Analysis$Time$hcst_start, '-', 
                                   sprintf('%02d', init_month), '-01'),
			    cal = calendar)
    } else if (type == 'fcst') {
      init_date <- as.PCICt(paste0(recipe$Analysis$Time$fcst_year[1], '-',
                                   sprintf('%02d', init_month), '-01'),
			    cal = calendar)
    }
  } else {
    if (type == 'hcst') {
      init_date <- as.PCICt(paste0(recipe$Analysis$Time$hcst_start,
                            recipe$Analysis$Time$sdate),
	                    format = '%Y%m%d', cal = calendar)
    } else if (type == 'fcst') {
      init_date <- as.PCICt(paste0(recipe$Analysis$Time$fcst_year,
                            recipe$Analysis$Time$sdate),
			    format = '%Y%m%d', cal = calendar)
    }
  }
  # Get time difference in hours
  leadtimes <- as.numeric(dates - init_date)/3600

  syears <- seq(1:dim(data_cube$data)['syear'][[1]])
  # expect dim = [sday = 1, sweek = 1, syear, time]
  syears_val <- lubridate::year(data_cube$attrs$Dates[1, 1, , 1])
  for (i in syears) {
    # Select year from array and rearrange dimensions
    fcst <- ClimProjDiags::Subset(data_cube$data, 'syear', i, drop = T)

    if (!("time" %in% names(dim(fcst)))) {
      dim(fcst) <- c("time" = 1, dim(fcst))
    }
    if (tolower(agg) == "global") {
      fcst <- list(Reorder(fcst, c(lalo, 'ensemble', 'time')))
    } else {
      fcst <- list(Reorder(fcst, c('country', 'ensemble', 'time')))
    }

    # Add metadata
    var.sdname <- dictionary$vars[[variable]]$standard_name
    if (tolower(agg) == "country") {
      dims <- c('Country', 'ensemble', 'time')
      var.expname <- paste0(variable, '_country') 
      var.longname <- paste0("Country-Aggregated ", var.longname)
      var.units <- attr(data_cube$Variable, 'variable')$units
    } else {
      dims <- c(lalo, 'ensemble', 'time')
      var.expname <- variable
      var.sdname <- var.sdname
      var.units <- data_cube$attrs$Variable$metadata[[variable]]$units
    }
  
    metadata <- list(fcst = list(name = var.expname,
      			         standard_name = var.sdname,
      			         long_name = var.longname,
                                 units = var.units))
    attr(fcst[[1]], 'variables') <- metadata
    names(dim(fcst[[1]])) <- dims
    # Add global attributes
    attr(fcst[[1]], 'global_attrs') <- global_attributes
    
    # Select start date
    if (fcst.horizon == 'decadal') {
      ## NOTE: Not good to use data_cube$load_parameters$dat1 because decadal
      ## data has been reshaped
      # fcst.sdate <- format(as.Date(data_cube$Dates$start[i]), '%Y%m%d')

      # init_date is like "1990-11-01"
      init_date <- as.POSIXct(init_date)
      fcst.sdate <- init_date + lubridate::years(syears_val[i] - lubridate::year(init_date))
      fcst.sdate <- format(fcst.sdate, '%Y%m%d')

    } else {
      fcst.sdate <- data_cube$attrs$load_parameters$dat1$file_date[[1]][i]
    }

    # Get time dimension values and metadata
    times <- get_times(store.freq, fcst.horizon, leadtimes, fcst.sdate, calendar)
    time <- times$time

    # Generate name of output file
    outfile <- get_filename(outdir, recipe, data_cube$attrs$Variable$varName,
  			    fcst.sdate, agg, "exp")
  
    # Get grid data and metadata and export to netCDF
    if (tolower(agg) == "country") { 
      country <- get_countries(grid)
      ArrayToNc(append(country, time, fcst), outfile)
    } else {
      latitude <- data_cube$coords$lat[1:length(data_cube$coords$lat)]
      longitude <- data_cube$coords$lon[1:length(data_cube$coords$lon)]
      latlon <- get_latlon(latitude, longitude)
      # Compile variables into a list and export to netCDF
      vars <- list(latlon$lat, latlon$lon, time)
      vars <- c(vars, fcst)
      ArrayToNc(vars, outfile)
    }
  }
  info(recipe$Run$logger, paste("#####", toupper(type),
				"SAVED TO NETCDF FILE #####"))
}


save_observations <- function(recipe,
			      data_cube,
                              agg = "global",
			      outdir = NULL) {
  # Loops over the years in the s2dv_cube containing the observations and
  # exports each year to a netCDF file.
  # data_cube: s2dv_cube containing the data and metadata
  # recipe: the auto-s2s recipe
  # outdir: directory where the files should be saved
  # agg: aggregation, "global" or "country"

  lalo <- c('longitude', 'latitude')
  archive <- get_archive(recipe)
  dictionary <- read_yaml("conf/variable-dictionary.yml")
  variable <- data_cube$attrs$Variable$varName
  var.longname <- data_cube$attrs$Variable$metadata[[variable]]$long_name
  global_attributes <- get_global_attributes(recipe, archive)
  fcst.horizon <- tolower(recipe$Analysis$Horizon)
  store.freq <- recipe$Analysis$Variables$freq
  calendar <- archive$Reference[[global_attributes$reference]]$calendar

  if (is.null(outdir)) {
    outdir <- get_dir(recipe)
  }
  
  # Generate vector containing leadtimes
  ## TODO: Move to a separate function?
  dates <- as.PCICt(ClimProjDiags::Subset(data_cube$attrs$Dates, 'syear', 1),
		    cal = calendar)
  if (fcst.horizon == 'decadal') {
    ## Method 1: Use the first date as init_date. But it may be better to use 
    ## the real initialized date (ask users)
#    init_date <- as.Date(data_cube$Dates$start[1], format = '%Y%m%d')
    ##  Method 2: use initial month
    init_month <- archive$System[[recipe$Analysis$Datasets$System$name]]$initial_month
    init_date <- as.PCICt(paste0(recipe$Analysis$Time$hcst_start, '-', 
                                 sprintf('%02d', init_month), '-01'),
			  cal = calendar)

  } else {
    init_date <- as.PCICt(paste0(recipe$Analysis$Time$hcst_start,
                          recipe$Analysis$Time$sdate),
	                  format = '%Y%m%d', cal = calendar)
  }
  # Get time difference in hours
  leadtimes <- as.numeric(dates - init_date)/3600

  syears <- seq(1:dim(data_cube$data)['syear'][[1]])
  ## expect dim = [sday = 1, sweek = 1, syear, time]
  syears_val <- lubridate::year(data_cube$attrs$Dates[1, 1, , 1])
  for (i in syears) {
    # Select year from array and rearrange dimensions
    fcst <- ClimProjDiags::Subset(data_cube$data, 'syear', i, drop = T)

    if (!("time" %in% names(dim(fcst)))) {
      dim(fcst) <- c("time" = 1, dim(fcst))
    }
    if (tolower(agg) == "global") {
      fcst <- list(Reorder(fcst, c(lalo, 'time')))
    } else {
      fcst <- list(Reorder(fcst, c('country', 'time')))
    }

    # Add metadata
    var.sdname <- dictionary$vars[[variable]]$standard_name
    if (tolower(agg) == "country") {
      dims <- c('Country', 'time')
      var.expname <- paste0(variable, '_country') 
      var.longname <- paste0("Country-Aggregated ", var.longname)
      var.units <- data_cube$attrs$Variable$metadata[[variable]]$units
    } else {
      dims <- c(lalo, 'time')
      var.expname <- variable
      var.units <- data_cube$attrs$Variable$metadata[[variable]]$units
    }
  
    metadata <- list(fcst = list(name = var.expname,
      			         standard_name = var.sdname,
      			         long_name = var.longname,
                                 units = var.units))
    attr(fcst[[1]], 'variables') <- metadata
    names(dim(fcst[[1]])) <- dims
    # Add global attributes
    attr(fcst[[1]], 'global_attrs') <- global_attributes
    
    # Select start date. The date is computed for each year, and adapted for
    # consistency with the hcst/fcst dates, so that both sets of files have
    # the same name pattern.
    ## Because observations are loaded differently in the daily vs. monthly
    ## cases, different approaches are necessary.
    if (fcst.horizon == 'decadal') {
      # init_date is like "1990-11-01"
      init_date <- as.POSIXct(init_date)
      fcst.sdate <- init_date + lubridate::years(syears_val[i] - lubridate::year(init_date))
    } else {

      if (store.freq == "monthly_mean") {
        fcst.sdate <- data_cube$attrs$load_parameters$dat1$file_date[[1]][i]
        fcst.sdate <- as.Date(paste0(fcst.sdate, "01"), '%Y%m%d')
      } else {
        fcst.sdate <- as.Date(data_cube$attrs$Dates[i])
      }
    }

    # Ensure the year is correct if the first leadtime goes to the next year
    init_date <- as.POSIXct(init_date)
    if (lubridate::month(fcst.sdate) < lubridate::month(init_date)) {
      lubridate::year(fcst.sdate) <- lubridate::year(fcst.sdate) + 1
    }
    # Ensure that the initialization month is consistent with the hindcast
    lubridate::month(fcst.sdate) <- lubridate::month(init_date)
    fcst.sdate <- format(fcst.sdate, format = '%Y%m%d')

    # Get time dimension values and metadata
    times <- get_times(store.freq, fcst.horizon, leadtimes, fcst.sdate, calendar)
    time <- times$time

    # Generate name of output file
    outfile <- get_filename(outdir, recipe, data_cube$attrs$Variable$varName,
  			    fcst.sdate, agg, "obs")
  
    # Get grid data and metadata and export to netCDF
    if (tolower(agg) == "country") { 
      country <- get_countries(grid)
      ArrayToNc(append(country, time, fcst), outfile)
    } else {
      latitude <- data_cube$coords$lat[1:length(data_cube$coords$lat)]
      longitude <- data_cube$coords$lon[1:length(data_cube$coords$lon)]
      latlon <- get_latlon(latitude, longitude)
      # Compile variables into a list and export to netCDF
      vars <- list(latlon$lat, latlon$lon, time)
      vars <- c(vars, fcst)
      ArrayToNc(vars, outfile)
    }
  }
  info(recipe$Run$logger, "##### OBS SAVED TO NETCDF FILE #####")
}

## TODO: Place inside a function somewhere
# if (tolower(agg) == "country") {
#   load(mask.path)
#   grid <- europe.countries.iso
# } else {
#   grid <- list(lon=attr(var.obs, 'Variables')$dat1$longitude,
#                lat=attr(var.obs, 'Variables')$dat1$latitude)
# }

save_metrics <- function(recipe,
			 skill,
                         data_cube,
                         agg = "global",
			 outdir = NULL) {
  # This function adds metadata to the skill metrics in 'skill'
  # and exports them to a netCDF file inside 'outdir'.
  
  # Define grid dimensions and names
  lalo <- c('longitude', 'latitude')
  archive <- get_archive(recipe)
  dictionary <- read_yaml("conf/variable-dictionary.yml")


  # Remove singleton dimensions and rearrange lon, lat and time dims 
  if (tolower(agg) == "global") {
    skill <- lapply(skill, function(x) {
                    Reorder(x, c(lalo, 'time'))})
  }
  # Add global and variable attributes
  global_attributes <- get_global_attributes(recipe, archive)
  ## TODO: Sort out the logic once default behavior is decided
  if ((!is.null(recipe$Analysis$Workflow$Anomalies$compute)) && 
      (recipe$Analysis$Workflow$Anomalies$compute)) {
    global_attributes <- c(list(from_anomalies = "Yes"),
			   global_attributes)
  } else {
    global_attributes <- c(list(from_anomalies = "No"),
			   global_attributes)
  }
  attr(skill[[1]], 'global_attrs') <- global_attributes

  for (i in 1:length(skill)) {
    metric <- names(skill[i])
    long_name <- dictionary$metrics[[metric]]$long_name
    missing_val <- -9.e+33
    skill[[i]][is.na(skill[[i]])] <- missing_val
    if (tolower(agg) == "country") {
      sdname <- paste0(metric, " region-aggregated metric")
      dims <- c('Country', 'time')
    } else {
      sdname <- paste0(metric) #, " grid point metric")
      dims <- c(lalo, 'time')
    }
    metadata <- list(metric = list(name = metric,
				   standard_name = sdname,
				   long_name = long_name,
				   missing_value = missing_val))
    attr(skill[[i]], 'variables') <- metadata
    names(dim(skill[[i]])) <- dims
  }

  # Time indices and metadata
  fcst.horizon <- tolower(recipe$Analysis$Horizon)
  store.freq <- recipe$Analysis$Variables$freq
  calendar <- archive$System[[global_attributes$system]]$calendar

  # Generate vector containing leadtimes
  dates <- as.PCICt(ClimProjDiags::Subset(data_cube$attrs$Dates, 'syear', 1),
		    cal = calendar)

  if (fcst.horizon == 'decadal') {
    init_month <- archive$System[[recipe$Analysis$Datasets$System$name]]$initial_month
    init_date <- as.PCICt(paste0(recipe$Analysis$Time$hcst_start, '-',
                                 sprintf('%02d', init_month), '-01'),
			  cal = calendar)
  } else {
    init_date <- as.PCICt(paste0(recipe$Analysis$Time$hcst_start,
                          recipe$Analysis$Time$sdate),
                          format = '%Y%m%d', cal = calendar)
  }

  # Get time difference in hours
  leadtimes <- as.numeric(dates - init_date)/3600

  # Select start date
  # If a fcst is provided, use that as the ref. year. Otherwise use 1970.
  if (fcst.horizon == 'decadal') {
    if (!is.null(recipe$Analysis$Time$fcst_year)) {
      #PROBLEM: May be more than one fcst_year
      fcst.sdate <- paste0(recipe$Analysis$Time$fcst_year[1],
                           sprintf('%02d', init_month), '01')
    } else {
      fcst.sdate <- paste0("1970", sprintf('%02d', init_month), '01')
    }
  } else {
    if (!is.null(recipe$Analysis$Time$fcst_year)) {
      fcst.sdate <- paste0(recipe$Analysis$Time$fcst_year,
  	                   recipe$Analysis$Time$sdate)
    } else {
      fcst.sdate <- paste0("1970", recipe$Analysis$Time$sdate)
    }
  }

  times <- get_times(store.freq, fcst.horizon, leadtimes, fcst.sdate, calendar)
  time <- times$time

  # Generate name of output file
  if (is.null(outdir)) {
    outdir <- get_dir(recipe)
  }
  outfile <- get_filename(outdir, recipe, data_cube$attrs$Variable$varName,
			  fcst.sdate, agg, "skill")

  # Get grid data and metadata and export to netCDF
  if (tolower(agg) == "country") { 
    country <- get_countries(grid)
    ArrayToNc(append(country, time, skill), outfile)
  } else {
    latitude <- data_cube$coords$lat[1:length(data_cube$coords$lat)]
    longitude <- data_cube$coords$lon[1:length(data_cube$coords$lon)]
    latlon <- get_latlon(latitude, longitude)
    # Compile variables into a list and export to netCDF
    vars <- list(latlon$lat, latlon$lon, time)
    vars <- c(vars, skill)
    ArrayToNc(vars, outfile)
  }
  info(recipe$Run$logger, "##### SKILL METRICS SAVED TO NETCDF FILE #####")
}

save_corr <- function(recipe,
		      skill,
                      data_cube,
                      agg = "global",
		      outdir = NULL) {
  # This function adds metadata to the ensemble correlation in 'skill'
  # and exports it to a netCDF file inside 'outdir'.
  
  archive <- get_archive(recipe)
  dictionary <- read_yaml("conf/variable-dictionary.yml")
  # Define grid dimensions and names
  lalo <- c('longitude', 'latitude')
  # Remove singleton dimensions and rearrange lon, lat and time dims 
  if (tolower(agg) == "global") {
    skill <- lapply(skill, function(x) {
                    Reorder(x, c(lalo, 'ensemble', 'time'))})
  }
  # Add global and variable attributes
  global_attributes <- get_global_attributes(recipe, archive)
  ## TODO: Sort out the logic once default behavior is decided
  if ((!is.null(recipe$Analysis$Workflow$Anomalies$compute)) && 
      (recipe$Analysis$Workflow$Anomalies$compute)) {
    global_attributes <- c(global_attributes, 
			   list(from_anomalies = "Yes"))
  } else {
    global_attributes <- c(global_attributes,
			   list(from_anomalies = "No"))
  }
  attr(skill[[1]], 'global_attrs') <- global_attributes

  for (i in 1:length(skill)) {
    metric <- names(skill[i])
    long_name <- dictionary$metrics[[metric]]$long_name
    missing_val <- -9.e+33
    skill[[i]][is.na(skill[[i]])] <- missing_val
    if (tolower(agg) == "country") {
      sdname <- paste0(metric, " region-aggregated metric")
      dims <- c('Country', 'ensemble', 'time')
    } else {
      sdname <- paste0(metric) #, " grid point metric") # formerly names(metric)
      dims <- c(lalo, 'ensemble', 'time')
    }
    metadata <- list(metric = list(name = metric,
				   standard_name = sdname,
				   long_name = long_name,
				   missing_value = missing_val))
    attr(skill[[i]], 'variables') <- metadata
    names(dim(skill[[i]])) <- dims
  }

  # Time indices and metadata
  fcst.horizon <- tolower(recipe$Analysis$Horizon)
  store.freq <- recipe$Analysis$Variables$freq
  calendar <- archive$System[[global_attributes$system]]$calendar
  
  # Generate vector containing leadtimes
  dates <- as.PCICt(ClimProjDiags::Subset(data_cube$attrs$Dates, 'syear', 1),
		    cal = calendar)
  if (fcst.horizon == 'decadal') {
    init_month <- archive$System[[recipe$Analysis$Datasets$System$name]]$initial_month
    init_date <- as.PCICt(paste0(recipe$Analysis$Time$hcst_start, '-',
                                 sprintf('%02d', init_month), '-01'),
			  cal = calendar)
  } else {
    init_date <- as.PCICt(paste0(recipe$Analysis$Time$hcst_start,
                          recipe$Analysis$Time$sdate),
                          format = '%Y%m%d', cal = calendar)
  }

  # Get time difference in hours
  leadtimes <- as.numeric(dates - init_date)/3600

  # Select start date
  # If a fcst is provided, use that as the ref. year. Otherwise use 1970.
  if (fcst.horizon == 'decadal') {
    if (!is.null(recipe$Analysis$Time$fcst_year)) {
      #PROBLEM: May be more than one fcst_year
      fcst.sdate <- paste0(recipe$Analysis$Time$fcst_year[1],
                           sprintf('%02d', init_month), '01')
    } else {
      fcst.sdate <- paste0("1970", sprintf('%02d', init_month), '01')
    }
  } else {
    if (!is.null(recipe$Analysis$Time$fcst_year)) {
      fcst.sdate <- paste0(recipe$Analysis$Time$fcst_year,
                           recipe$Analysis$Time$sdate)
    } else {
      fcst.sdate <- paste0("1970", recipe$Analysis$Time$sdate)
    }
  }

  times <- get_times(store.freq, fcst.horizon, leadtimes, fcst.sdate, calendar)
  time <- times$time

  # Generate name of output file
  if (is.null(outdir)) {
    outdir <- get_dir(recipe)
  }
  outfile <- get_filename(outdir, recipe, data_cube$attrs$Variable$varName,
			  fcst.sdate, agg,  "corr")

  # Get grid data and metadata and export to netCDF
  if (tolower(agg) == "country") { 
    country <- get_countries(grid)
    ArrayToNc(append(country, time, skill), outfile)
  } else {
    latitude <- data_cube$coords$lat[1:length(data_cube$coords$lat)]
    longitude <- data_cube$coords$lon[1:length(data_cube$coords$lon)]
    latlon <- get_latlon(latitude, longitude)
    # Compile variables into a list and export to netCDF
    vars <- list(latlon$lat, latlon$lon, time)
    vars <- c(vars, skill)
    ArrayToNc(vars, outfile)
  }
  info(recipe$Run$logger,
       "##### ENSEMBLE CORRELATION SAVED TO NETCDF FILE #####")
}

save_percentiles <- function(recipe,
			     percentiles,
                             data_cube,
                             agg = "global",
			     outdir = NULL) {
  # This function adds metadata to the percentiles
  # and exports them to a netCDF file inside 'outdir'.
  archive <- get_archive(recipe)
  
  # Define grid dimensions and names
  lalo <- c('longitude', 'latitude')
  # Remove singleton dimensions and rearrange lon, lat and time dims 
  if (tolower(agg) == "global") {
    percentiles <- lapply(percentiles, function(x) {
                        Reorder(x, c(lalo, 'time'))})
  }

  # Add global and variable attributes
  global_attributes <- get_global_attributes(recipe, archive)
  ## TODO: Sort out the logic once default behavior is decided
  if ((!is.null(recipe$Analysis$Workflow$Anomalies$compute)) && 
      (recipe$Analysis$Workflow$Anomalies$compute)) {
    global_attributes <- c(list(from_anomalies = "Yes"),
			   global_attributes)
  } else {
    global_attributes <- c(list(from_anomalies = "No"),
			   global_attributes)
  }
  attr(percentiles[[1]], 'global_attrs') <- global_attributes

  for (i in 1:length(percentiles)) {
    ## TODO: replace with proper standard names
    percentile <- names(percentiles[i])
    long_name <- paste0(gsub("^.*_", "", percentile), "th percentile")
    if (tolower(agg) == "country") {
      dims <- c('Country', 'time')
    } else {
      dims <- c(lalo, 'time')
    }
    metadata <- list(metric = list(name = percentile, long_name = long_name))
    attr(percentiles[[i]], 'variables') <- metadata
    names(dim(percentiles[[i]])) <- dims
  }

  # Time indices and metadata
  fcst.horizon <- tolower(recipe$Analysis$Horizon)
  store.freq <- recipe$Analysis$Variables$freq
  calendar <- archive$System[[global_attributes$system]]$calendar
  # Generate vector containing leadtimes
  dates <- as.PCICt(ClimProjDiags::Subset(data_cube$attrs$Dates, 'syear', 1),
		    cal = calendar)
  if (fcst.horizon == 'decadal') {
    init_month <- archive$System[[recipe$Analysis$Datasets$System$name]]$initial_month
    init_date <- as.PCICt(paste0(recipe$Analysis$Time$hcst_start, '-',
                                 sprintf('%02d', init_month), '-01'),
			  cal = calendar)
  } else {
    init_date <- as.PCICt(paste0(recipe$Analysis$Time$hcst_start,
                          recipe$Analysis$Time$sdate),
                          format = '%Y%m%d', cal = calendar)
  }

  # Get time difference in hours
  leadtimes <- as.numeric(dates - init_date)/3600

  # Select start date
  # If a fcst is provided, use that as the ref. year. Otherwise use 1970.
  if (fcst.horizon == 'decadal') {
    if (!is.null(recipe$Analysis$Time$fcst_year)) {
      #PROBLEM: May be more than one fcst_year
      fcst.sdate <- paste0(recipe$Analysis$Time$fcst_year[1],
                           sprintf('%02d', init_month), '01')
    } else {
      fcst.sdate <- paste0("1970", sprintf('%02d', init_month), '01')
    }
  } else {
    if (!is.null(recipe$Analysis$Time$fcst_year)) {
      fcst.sdate <- paste0(recipe$Analysis$Time$fcst_year,
                           recipe$Analysis$Time$sdate)
    } else {
      fcst.sdate <- paste0("1970", recipe$Analysis$Time$sdate)
    }
  }
  times <- get_times(store.freq, fcst.horizon, leadtimes, fcst.sdate, calendar)
  time <- times$time

  # Generate name of output file
  if (is.null(outdir)) {
    outdir <- get_dir(recipe)
  }
  outfile <- get_filename(outdir, recipe, data_cube$attrs$Variable$varName,
			  fcst.sdate, agg, "percentiles")
  # Get grid data and metadata and export to netCDF
  if (tolower(agg) == "country") { 
    country <- get_countries(grid)
    ArrayToNc(append(country, time, percentiles), outfile)
  } else {
    latitude <- data_cube$coords$lat[1:length(data_cube$coords$lat)]
    longitude <- data_cube$coords$lon[1:length(data_cube$coords$lon)]
    latlon <- get_latlon(latitude, longitude)
    # Compile variables into a list and export to netCDF
    vars <- list(latlon$lat, latlon$lon, time)
    vars <- c(vars, percentiles)
    ArrayToNc(vars, outfile)
  }
  info(recipe$Run$logger, "##### PERCENTILES SAVED TO NETCDF FILE #####")
}

save_probabilities <- function(recipe,
			       probs,
			       data_cube,
                               agg = "global",
			       type = "hcst",
			       outdir = NULL) {
  # Loops over the years in the s2dv_cube containing a hindcast or forecast
  # and exports the corresponding category probabilities to a netCDF file.
  # probs: array containing the probability data
  # recipe: the auto-s2s recipe
  # data_cube: s2dv_cube containing the data and metadata
  # outdir: directory where the files should be saved
  # type: 'exp' (hcst and fcst) or 'obs'
  # agg: aggregation, "global" or "country"
  # type: 'hcst' or 'fcst'

  lalo <- c('longitude', 'latitude')
  archive <- get_archive(recipe) 
  variable <- data_cube$attrs$Variable$varName
  var.longname <- data_cube$attrs$Variable$metadata[[variable]]$long_name
  global_attributes <- get_global_attributes(recipe, archive)
  if (is.null(outdir)) {
    outdir <- get_dir(recipe)
  }
  # Add anomaly computation to global attributes
  ## TODO: Sort out the logic once default behavior is decided
  if ((!is.null(recipe$Analysis$Workflow$Anomalies$compute)) && 
      (recipe$Analysis$Workflow$Anomalies$compute)) {
    global_attributes <- c(list(from_anomalies = "Yes"),
			   global_attributes)
  } else {
    global_attributes <- c(list(from_anomalies = "No"),
			   global_attributes)
  }
  fcst.horizon <- tolower(recipe$Analysis$Horizon)
  store.freq <- recipe$Analysis$Variables$freq
  calendar <- archive$System[[global_attributes$system]]$calendar

  # Generate vector containing leadtimes
  ## TODO: Move to a separate function?
  dates <- as.PCICt(ClimProjDiags::Subset(data_cube$attrs$Dates, 'syear', 1),
		    cal = calendar)
  if (fcst.horizon == 'decadal') {
    init_month <- archive$System[[recipe$Analysis$Datasets$System$name]]$initial_month
    init_date <- as.PCICt(paste0(recipe$Analysis$Time$hcst_start, '-',
                                 sprintf('%02d', init_month), '-01'),
			  cal = calendar)
  } else {
    init_date <- as.PCICt(paste0(recipe$Analysis$Time$hcst_start,
                          recipe$Analysis$Time$sdate),
                          format = '%Y%m%d', cal = calendar)
  }

  # Get time difference in hours
  leadtimes <- as.numeric(dates - init_date)/3600

  syears <- seq(1:dim(data_cube$data)['syear'][[1]])
  ## expect dim = [sday = 1, sweek = 1, syear, time]
  syears_val <- lubridate::year(data_cube$attrs$Dates[1, 1, , 1])
  for (i in syears) {
    # Select year from array and rearrange dimensions
    probs_syear <- lapply(probs, ClimProjDiags::Subset, 'syear', i, drop = 'selected')
    if (tolower(agg) == "global") {
      probs_syear <- lapply(probs_syear, function(x) {
		            Reorder(x, c(lalo, 'time'))})
    } else {
      probs_syear <- lapply(probs_syear, function(x) {
		            Reorder(x, c('country', 'time'))})
    }

    ## TODO: Replace for loop with something more efficient?
    for (bin in 1:length(probs_syear)) {
      prob_bin <- names(probs_syear[bin])
      long_name <- paste0(prob_bin, " probability category")
      if (tolower(agg) == "country") {
        dims <- c('Country', 'time')
      } else {
        dims <- c(lalo, 'time')
      }
      metadata <- list(metric = list(name = prob_bin, long_name = long_name))
      attr(probs_syear[[bin]], 'variables') <- metadata
      names(dim(probs_syear[[bin]])) <- dims # is this necessary?
    }

    # Add global attributes
    attr(probs_syear[[1]], 'global_attrs') <- global_attributes
    
    # Select start date
    if (fcst.horizon == 'decadal') {
      # init_date is like "1990-11-01"
      init_date <- as.POSIXct(init_date)
      fcst.sdate <- init_date + lubridate::years(syears_val[i] - lubridate::year(init_date))
      fcst.sdate <- format(fcst.sdate, '%Y%m%d')
    } else {
      fcst.sdate <- data_cube$attrs$load_parameters$dat1$file_date[[1]][i]
    }

    # Get time dimension values and metadata
    times <- get_times(store.freq, fcst.horizon, leadtimes, fcst.sdate, calendar)
    time <- times$time

    # Generate name of output file
    outfile <- get_filename(outdir, recipe, data_cube$attrs$Variable$varName,
  			    fcst.sdate, agg, "probs")
  
    # Get grid data and metadata and export to netCDF
    if (tolower(agg) == "country") { 
      country <- get_countries(grid)
      ArrayToNc(append(country, time, probs_syear), outfile)
    } else {
      latitude <- data_cube$coords$lat[1:length(data_cube$coords$lat)]
      longitude <- data_cube$coords$lon[1:length(data_cube$coords$lon)]
      latlon <- get_latlon(latitude, longitude)
      # Compile variables into a list and export to netCDF
      vars <- list(latlon$lat, latlon$lon, time)
      vars <- c(vars, probs_syear)
      ArrayToNc(vars, outfile)
    }
  }

  info(recipe$Run$logger,
       paste("#####", toupper(type),
	     "PROBABILITIES SAVED TO NETCDF FILE #####"))
}
