# written by Fabian Stenzel, based on work by Sebastian Ostberg
# 2022-2023 - stenzel@pik-potsdam.de

################# EcoRisk calc functions  ###################

#' Wrapper for calculating the ecosystem change metric EcoRisk
#'
#' Function to read in data for ecorisk, and call the calculation function once,
#' if overtime is FALSE, or for each timeslice of length window years, if
#' overtime is TRUE
#'
#' @param path_ref folder of reference run
#' @param path_scen folder of scenario run
#' @param read_saved_data whether to read in previously saved data
#'        (default: FALSE)
#' @param save_data file to save read in data to (default NULL)
#' @param save_ecorisk file to save EcoRisk data to (default NULL)
#' @param nitrogen include nitrogen outputs for pools and fluxes into EcoRisk
#'        calculation (default FALSE)
#' @param weighting apply "old" (Ostberg-like), "new", or "equal" weighting of
#'        vegetation_structure_change weights (default "equal")
#' @param time_span_reference vector of years to use as scenario period
#' @param time_span_scenario vector of years to use as scenario period
#' @param dimensions_only_local flag whether to use only local change component
#'        for water/carbon/nitrogen fluxes and pools, or use an average of
#'        local change, global change and ecosystem balance (default FALSE)
#' @param overtime logical: calculate ecorisk as time-series? (default: FALSE)
#' @param window integer, number of years for window length (default: 30)
#' @param debug_mode write out all nitrogen state variables (default FALSE)
#' @param replace_input_file_names list with alternative names for output
#'        identifiers to replace the ones in inst/ext_files/metric_files.yml.
#'        e.g. list(npp="mnpp") would replace the expected output for npp with
#'        mnpp followed by the automatically detected file extension (.bin.json)
#' @param suppress_warnings suppress warnings - default: TRUE
#' @param external_variability use externally supplied variability for the
#'        reference period? experimental! (default: FALSE)
#' @param c2vr external variability array
#'
#' @return list data object containing arrays of ecorisk_total,
#'         vegetation_structure_change, local_change, global_importance,
#'         ecosystem_balance, carbon_stocks, carbon_fluxes, water_fluxes
#'         (+ nitrogen_stocks and nitrogen_fluxes)
#'
#' @examples
#' \dontrun{
#' ecorisk_wrapper(
#'   path_ref = pnv_folder,
#'   path_scen = run_folder,
#'   read_saved_data = FALSE,
#'   nitrogen = TRUE,
#'   save_data = NULL,
#'   save_ecorisk = NULL,
#'   time_span_reference = c(1550:1579),
#'   time_span_scenario = c(1987:2016)
#' )
#' }
#'
#' @md
#' @export
ecorisk_wrapper <- function(path_ref,
                            path_scen,
                            read_saved_data = FALSE,
                            save_data = NULL,
                            save_ecorisk = NULL,
                            nitrogen = TRUE,
                            weighting = "equal",
                            time_span_reference,
                            time_span_scenario,
                            dimensions_only_local = FALSE,
                            overtime = FALSE,
                            window = 30,
                            debug_mode = FALSE,
                            replace_input_file_names = NULL,
                            external_variability = FALSE,
                            c2vr = NULL,
                            suppress_warnings = TRUE) {
  # check timespan consistency
  nyears <- length(time_span_reference)
  nyears_scen <- length(time_span_scenario)

  # prepare slices for overtime calculation
  slices <- (nyears_scen - window + 1)
  window_half <- round(window / 2.0)
  slice_years <- time_span_scenario[
    (window_half + 1):(nyears_scen - window_half + 1)
  ]

  if ((!nyears == window) || nyears_scen < window) {
    stop(
      "Timespan in reference is not equal to window size (",
      window,
      "), or scenario timespan is smaller than window size."
    )
  }

  if (!read_saved_data) {
    # translate output names (from metric_files.yml) and
    # folders to files_scenarios/reference lists
    metric_files <- system.file(
      "extdata",
      "metric_files.yml",
      package = "biospheremetrics"
    ) %>%
      yaml::read_yaml()

    file_extension <- get_major_file_ext(paste0(path_scen))
    files_scenario <- list()
    files_reference <- list()

    for (output in names(metric_files$metric$ecorisk_nitrogen$output)) {
      # Iterate over all outputs
      if (is.null(replace_input_file_names[[output]])) {
        for (file in metric_files$file_name[[output]]) {
          full_file_path_lu <- paste0(path_scen, file, ".", file_extension)
          if (file.exists(full_file_path_lu)) {
            files_scenario[[output]] <- full_file_path_lu
          }
          full_file_path_pnv <- paste0(path_ref, file, ".", file_extension)
          if (file.exists(full_file_path_pnv)) {
            files_reference[[output]] <- full_file_path_pnv
          }
        }
        if (is.null(files_scenario[[output]])) {
          stop(
            "None of the default file names for ", output,
            " were found in ", path_scen, " - please check or define manually",
            " using argument 'replace_input_file_names'. Stopping."
          )
        }
        if (is.null(files_reference[[output]])) {
          stop(
            "None of the default file names for ", output,
            " were found in ", path_ref, " - please check or define manually",
            " using argument 'replace_input_file_names'. Stopping."
          )
        }
      } else {
        files_scenario[[output]] <- paste0(
          path_scen,
          replace_input_file_names[[output]], ".", file_extension
        )
        files_reference[[output]] <- paste0(
          path_ref,
          replace_input_file_names[[output]], ".", file_extension
        )
      }
    }
  } # end if !read_saved_data
  if (overtime && (window != nyears)) stop("Overtime is enabled, but window \
                  length (", window, ") does not match the reference nyears.")

  if (read_saved_data) {
    if (!is.null(save_data)) {
      message("Loading saved data from:", save_data)
      load(file = save_data)
    } else {
      stop(
        "save_data is not specified as parameter, ",
        "nothing to load ... exiting"
      )
    }
  } else {
    # first read in all lpjml output files required for computing EcoRisks
    returned_vars <- read_ecorisk_data(
      files_reference = files_reference,
      files_scenario = files_scenario,
      save_file = save_data,
      nitrogen = nitrogen,
      time_span_reference = time_span_reference,
      time_span_scenario = time_span_scenario,
      debug_mode = debug_mode,
      suppress_warnings = suppress_warnings
    )
    if (overtime == FALSE && slices > 1) {
      print("Overtime == FALSE, but too many time slices.
            I assume this is just a read all data operation. Exiting.")
      return(NULL)
    }

    # extract variables from return list object and give them proper names
    state_ref <- returned_vars$state_ref
    state_scen <- returned_vars$state_scen
    fpc_ref <- returned_vars$fpc_ref
    fpc_scen <- returned_vars$fpc_scen
    bft_ref <- returned_vars$bft_ref
    bft_scen <- returned_vars$bft_scen
    cft_ref <- returned_vars$cft_ref
    cft_scen <- returned_vars$cft_scen
    lat <- returned_vars$lat
    lon <- returned_vars$lon
    cell_area <- returned_vars$cell_area
    rm(returned_vars)
  }

  ncells <- length(cell_area)
  cell_ids <- 0:(ncells - 1)

  ecorisk <- list(
    ecorisk_total = array(0,
      dim = c(ncells, slices),
      dimnames = list(cell = cell_ids, year = slice_years)
    ),
    vegetation_structure_change = array(0,
      dim = c(ncells, slices),
      dimnames = list(cell = cell_ids, year = slice_years)
    ),
    local_change = array(0,
      dim = c(ncells, slices),
      dimnames = list(cell = cell_ids, year = slice_years)
    ),
    global_importance = array(0,
      dim = c(ncells, slices),
      dimnames = list(cell = cell_ids, year = slice_years)
    ),
    ecosystem_balance = array(0,
      dim = c(ncells, slices),
      dimnames = list(cell = cell_ids, year = slice_years)
    ),
    c2vr = array(0,
      dim = c(4, ncells, slices),
      dimnames = list(part = 1:4, cell = cell_ids, year = slice_years)
    ),
    carbon_stocks = array(0,
      dim = c(ncells, slices),
      dimnames = list(cell = cell_ids, year = slice_years)
    ),
    carbon_fluxes = array(0,
      dim = c(ncells, slices),
      dimnames = list(cell = cell_ids, year = slice_years)
    ),
    carbon_total = array(0,
      dim = c(ncells, slices),
      dimnames = list(cell = cell_ids, year = slice_years)
    ),
    water_total = array(0,
      dim = c(ncells, slices),
      dimnames = list(cell = cell_ids, year = slice_years)
    ),
    water_fluxes = array(0,
      dim = c(ncells, slices),
      dimnames = list(cell = cell_ids, year = slice_years)
    ),
    nitrogen_stocks = array(0,
      dim = c(ncells, slices),
      dimnames = list(cell = cell_ids, year = slice_years)
    ),
    nitrogen_fluxes = array(0,
      dim = c(ncells, slices),
      dimnames = list(cell = cell_ids, year = slice_years)
    ),
    nitrogen_total = array(0,
      dim = c(ncells, slices),
      dimnames = list(cell = cell_ids, year = slice_years)
    ),
    lat = lat,
    lon = lon,
    cell_area = cell_area
  )
  for (y in slice_years) {
    year_range <- as.character((as.numeric(y) - window_half):
                                 (as.numeric(y) + window_half - 1))
    message("Calculating time slice ", y, " of ", slice_years[1], "-", slice_years[length(slice_years)])
    returned <- calc_ecorisk(
      fpc_ref = fpc_ref,
      fpc_scen = fpc_scen[, , year_range],
      bft_ref = bft_ref,
      bft_scen = bft_scen[, , year_range],
      cft_ref = cft_ref,
      cft_scen = cft_scen[, , year_range],
      state_ref = state_ref,
      state_scen = state_scen[, year_range, ],
      weighting = weighting,
      lat = lat,
      lon = lon,
      cell_area = cell_area,
      dimensions_only_local = dimensions_only_local,
      nitrogen = nitrogen,
      external_variability = external_variability,
      c2vr = c2vr
    )
    ecorisk$ecorisk_total[, as.character(y)] <- returned$ecorisk_total
    ecorisk$vegetation_structure_change[, as.character(y)] <- (
      returned$vegetation_structure_change
    )
    ecorisk$local_change[, as.character(y)] <- returned$local_change
    ecorisk$global_importance[, as.character(y)] <- returned$global_importance
    ecorisk$ecosystem_balance[, as.character(y)] <- returned$ecosystem_balance
    ecorisk$c2vr[, , as.character(y)] <- returned$c2vr
    ecorisk$carbon_stocks[, as.character(y)] <- returned$carbon_stocks
    ecorisk$carbon_fluxes[, as.character(y)] <- returned$carbon_fluxes
    ecorisk$carbon_total[, as.character(y)] <- returned$carbon_total
    ecorisk$water_total[, as.character(y)] <- returned$water_total
    ecorisk$water_fluxes[, as.character(y)] <- returned$water_fluxes
    if (nitrogen) {
      ecorisk$nitrogen_stocks[, as.character(y)] <- returned$nitrogen_stocks
      ecorisk$nitrogen_fluxes[, as.character(y)] <- returned$nitrogen_fluxes
      ecorisk$nitrogen_total[, as.character(y)] <- returned$nitrogen_total
    }
  }

  ############## export and save data if requested #############
  if (!(is.null(save_ecorisk))) {
    message("Saving EcoRisk data to: ", save_ecorisk)
    save(ecorisk, file = save_ecorisk)
  }
  #
  ###
  return(ecorisk)
}

#' Calculate the ecosystem change metric EcoRisk between 2 sets of states
#' This function is called by the wrapper function (ecorisk_wrapper),
#' unless you know what you are doing, don't use this function directly.
#'
#' Function to calculate the ecosystem change metric EcoRisk, based on
#' gamma/vegetation_structure_change
#' work from Sykes (1999), Heyder (2011), and Ostberg (2015,2018).
#' This is a reformulated version in R, not producing 100% similar values
#' than the C/bash version from Ostberg et al. 2018, but similar the methodology
#'
#' @param fpc_ref reference run data for fpc
#' @param fpc_scen scenario run data for fpc
#' @param bft_ref reference run data for fpc_bft
#' @param bft_scen scenario run data for fpc_bft
#' @param cft_ref reference run data for cftfrac
#' @param cft_scen scenario run data for cftfrac
#' @param state_ref reference run data for state variables
#' @param state_scen scenario run data for state variables
#' @param weighting apply "old" (Ostberg-like), "new", or "equal" weighting of
#'        vegetation_structure_change weights (default "equal")
#' @param lat latitude array
#' @param lon longitude array
#' @param cell_area cellarea array
#' @param dimensions_only_local flag whether to use only local change component
#'        for water/carbon/nitrogen fluxes and pools, or use an average of
#'        local change, global change and ecosystem balance (default FALSE)
#' @param nitrogen include nitrogen outputs (default: TRUE)
#' @param external_variability include external change_to_variability_ratio?
#'        (default: FALSE)
#' @param c2vr list with external change_to_variability_ratios for each
#'        component (default: NULL)
#'
#' @return list data object containing arrays of ecorisk_total,
#'         vegetation_structure_change, local_change, global_importance,
#'         ecosystem_balance, carbon_stocks, carbon_fluxes, water_fluxes
#'         (+ nitrogen_stocks and nitrogen_fluxes)
#'
#' @export
calc_ecorisk <- function(fpc_ref,
                         fpc_scen,
                         bft_ref,
                         bft_scen,
                         cft_ref,
                         cft_scen,
                         state_ref,
                         state_scen,
                         weighting = "equal",
                         lat,
                         lon,
                         cell_area,
                         dimensions_only_local = FALSE,
                         nitrogen = TRUE,
                         external_variability = FALSE,
                         c2vr = NULL) {
  if (external_variability && is.null(c2vr)) {
    stop("external_variability enabled, but not supplied (c2vr). Aborting.")
  }
  di_ref <- dim(fpc_ref)
  di_scen <- dim(fpc_scen)
  ncells <- di_ref[1]
  nyears <- di_ref[3]
  if (di_ref[3] != di_scen[3]) {
    stop("Dimension year does not match between fpc_scen and fpc_ref.")
  }
  # calc vegetation_structure_change and variability of
  #   vegetation_structure_change within
  # reference period S(vegetation_structure_change,
  #   sigma_vegetation_structure_change)
  fpc_ref_mean <- apply(fpc_ref, c(1, 2), mean)
  bft_ref_mean <- apply(bft_ref, c(1, 2), mean)
  cft_ref_mean <- apply(cft_ref, c(1, 2), mean)


  sigma_vegetation_structure_change_ref_list <- array(
    0,
    dim = c(ncells, nyears)
  )
  # calculate for every year of the reference period,
  #   vegetation_structure_change between that year and the average reference
  #   period year
  # this gives the variability of vegetation_structure_change within the
  #   reference period
  for (y in seq_len(nyears)) {
    sigma_vegetation_structure_change_ref_list[, y] <- calc_delta_v( # nolint
      fpc_ref = fpc_ref_mean,
      fpc_scen = fpc_ref[, , y],
      bft_ref = bft_ref_mean,
      bft_scen = bft_ref[, , y],
      cft_ref = cft_ref_mean,
      cft_scen = cft_ref[, , y],
      weighting = weighting
    )
  }

  # calculate the std deviation over the reference period for each gridcell
  vegetation_structure_changesd <- apply(
    sigma_vegetation_structure_change_ref_list,
    c(1),
    stats::sd
  )

  # calculate vegetation_structure_change between average reference and average
  #   scenario period
  vegetation_structure_change <- calc_delta_v(
    fpc_ref = fpc_ref_mean,
    fpc_scen = apply(fpc_scen, c(1, 2), mean),
    bft_ref = bft_ref_mean,
    bft_scen = apply(bft_scen, c(1, 2), mean),
    cft_ref = cft_ref_mean,
    cft_scen = apply(cft_scen, c(1, 2), mean),
    weighting = weighting
  )
  #
  ####
  ############## calc EcoRisk components ################
  # dimensions in the state vector
  # 1 "vegetation_carbon_pool"
  # 2 "soil_carbon_pool"
  # 3 "carbon_influx"
  # 4 "carbon_outflux"
  # 5 "soil_water_pool"
  # 6 "water_influx"
  # 7 "water_outflux"
  # 8 "other"
  # 9 "vegetation_nitrogen_pool"
  # 10 "soil_mineral_nitrogen_pool"
  # 11 "nitrogen_influx"
  # 12 "nitrogen_outflux"

  delta_var <- s_change_to_var_ratio(
    vegetation_structure_change,
    vegetation_structure_changesd
  )
  nitrogen_dimensions <- c(
    "vegetation_nitrogen_pool",
    "soil_mineral_nitrogen_pool",
    "nitrogen_influx",
    "nitrogen_outflux"
  )
  all_dimensions <- dimnames(state_scen)$class
  non_nitrogen_dimensions <- setdiff(all_dimensions, nitrogen_dimensions)
  if (nitrogen) {
    lc_raw <- calc_component(
      ref = state_ref,
      scen = state_scen,
      local = TRUE,
      cell_area = cell_area
    ) # local change

    gi_raw <- calc_component(
      ref = state_ref,
      scen = state_scen,
      local = FALSE,
      cell_area = cell_area
    ) # global importance

    eb_raw <- calc_ecosystem_balance(
      ref = state_ref,
      scen = state_scen
    ) # ecosystem balance
  } else {
    lc_raw <- calc_component(
      ref = state_ref[, , non_nitrogen_dimensions],
      scen = state_scen[, , non_nitrogen_dimensions],
      local = TRUE,
      cell_area = cell_area
    ) # local change

    gi_raw <- calc_component(
      ref = state_ref[, , non_nitrogen_dimensions],
      scen = state_scen[, , non_nitrogen_dimensions],
      local = FALSE,
      cell_area = cell_area
    ) # global importance

    eb_raw <- calc_ecosystem_balance(
      ref = state_ref[, , non_nitrogen_dimensions],
      scen = state_scen[, , non_nitrogen_dimensions]
    ) # ecosystem balance
  }
  if (dimensions_only_local == TRUE) {
    # carbon stocks (local change)
    cs <- calc_component(
      ref = state_ref[, , c("vegetation_carbon_pool", "soil_carbon_pool")],
      scen = state_scen[, , c("vegetation_carbon_pool", "soil_carbon_pool")],
      local = TRUE,
      cell_area = cell_area
    )$full
    # carbon fluxes (local change)
    cf <- calc_component(
      ref = state_ref[, , c("carbon_influx", "carbon_outflux")],
      scen = state_scen[, , c("carbon_influx", "carbon_outflux")],
      local = TRUE,
      cell_area = cell_area
    )$full
    # total carbon (local change)
    ct <- calc_component(
      ref = state_ref[, , c(
        "vegetation_carbon_pool",
        "soil_carbon_pool",
        "carbon_influx",
        "carbon_outflux"
      )],
      scen = state_scen[, , c(
        "vegetation_carbon_pool",
        "soil_carbon_pool",
        "carbon_influx",
        "carbon_outflux"
      )],
      local = TRUE,
      cell_area = cell_area
    )$full
    # water fluxes (local change)
    wf <- calc_component(
      ref = state_ref[, , c(
        "water_influx",
        "water_outflux"
      )],
      scen = state_scen[, , c(
        "water_influx",
        "water_outflux"
      )],
      local = TRUE,
      cell_area = cell_area
    )$full
    # total water (local change)
    wt <- calc_component(
      ref = state_ref[, , c(
        "water_influx",
        "water_outflux",
        "soil_water_pool"
      )],
      scen = state_scen[, , c(
        "water_influx",
        "water_outflux",
        "soil_water_pool"
      )],
      local = TRUE,
      cell_area = cell_area
    )$full

    # nitrogen stocks (local change)
    if (nitrogen) {
      ns <- calc_component(
        ref = state_ref[, , c(
          "vegetation_nitrogen_pool",
          "soil_mineral_nitrogen_pool"
        )],
        scen = state_scen[, , c(
          "vegetation_nitrogen_pool",
          "soil_mineral_nitrogen_pool"
        )],
        local = TRUE,
        cell_area = cell_area
      )$full
      # nitrogen fluxes (local change)
      nf <- calc_component(
        ref = state_ref[, , c("nitrogen_influx", "nitrogen_outflux")],
        scen = state_scen[, , c("nitrogen_influx", "nitrogen_outflux")],
        local = TRUE,
        cell_area = cell_area
      )$full
      # total nitrogen (local change)
      nt <- calc_component(
        ref = state_ref[, , c(
          "vegetation_nitrogen_pool",
          "soil_mineral_nitrogen_pool",
          "nitrogen_influx",
          "nitrogen_outflux"
        )],
        scen = state_scen[, , c(
          "vegetation_nitrogen_pool",
          "soil_mineral_nitrogen_pool",
          "nitrogen_influx",
          "nitrogen_outflux"
        )],
        local = TRUE,
        cell_area = cell_area
      )$full
    }
  } else { # local == FALSE
    cf <- (
      calc_component(
        ref = state_ref[, , c(
          "carbon_influx",
          "carbon_outflux"
        )],
        scen = state_scen[, , c(
          "carbon_influx",
          "carbon_outflux"
        )],
        local = TRUE,
        cell_area = cell_area
      )$full + # carbon fluxes
        calc_component(
          ref = state_ref[, , c(
            "carbon_influx",
            "carbon_outflux"
          )],
          scen = state_scen[, , c(
            "carbon_influx",
            "carbon_outflux"
          )],
          local = FALSE,
          cell_area = cell_area
        )$full +
        calc_ecosystem_balance(
          ref = state_ref[, , c(
            "carbon_influx",
            "carbon_outflux"
          )],
          scen = state_scen[, , c(
            "carbon_influx",
            "carbon_outflux"
          )]
        )$full
    ) / 3

    # carbon stocks
    cs <- (
      calc_component(
        ref = state_ref[, , c(
          "vegetation_carbon_pool",
          "soil_carbon_pool"
        )],
        scen = state_scen[, , c(
          "vegetation_carbon_pool",
          "soil_carbon_pool"
        )],
        local = TRUE,
        cell_area = cell_area
      )$full +
        calc_component(
          ref = state_ref[, , c(
            "vegetation_carbon_pool",
            "soil_carbon_pool"
          )],
          scen = state_scen[, , c(
            "vegetation_carbon_pool",
            "soil_carbon_pool"
          )],
          local = FALSE,
          cell_area = cell_area
        )$full +
        calc_ecosystem_balance(
          ref = state_ref[, , c(
            "vegetation_carbon_pool",
            "soil_carbon_pool"
          )],
          scen = state_scen[, , c(
            "vegetation_carbon_pool",
            "soil_carbon_pool"
          )]
        )$full
    ) / 3

    # carbon total
    ct <- (
      calc_component(
        ref = state_ref[, , c(
          "vegetation_carbon_pool",
          "soil_carbon_pool",
          "carbon_influx",
          "carbon_outflux"
        )],
        scen = state_scen[, , c(
          "vegetation_carbon_pool",
          "soil_carbon_pool",
          "carbon_influx",
          "carbon_outflux"
        )],
        local = TRUE,
        cell_area = cell_area
      )$full +
        calc_component(
          ref = state_ref[, , c(
            "vegetation_carbon_pool",
            "soil_carbon_pool",
            "carbon_influx",
            "carbon_outflux"
          )],
          scen = state_scen[, , c(
            "vegetation_carbon_pool",
            "soil_carbon_pool",
            "carbon_influx",
            "carbon_outflux"
          )],
          local = FALSE,
          cell_area = cell_area
        )$full +
        calc_ecosystem_balance(
          ref = state_ref[, , c(
            "vegetation_carbon_pool",
            "soil_carbon_pool",
            "carbon_influx",
            "carbon_outflux"
          )],
          scen = state_scen[, , c(
            "vegetation_carbon_pool",
            "soil_carbon_pool",
            "carbon_influx",
            "carbon_outflux"
          )]
        )$full
    ) / 3

    # water fluxes
    wf <- (
      calc_component(
        ref = state_ref[, , c(
          "water_influx",
          "water_outflux"
        )],
        scen = state_scen[, , c(
          "water_influx",
          "water_outflux"
        )],
        local = TRUE,
        cell_area = cell_area
      )$full +
        calc_component(
          ref = state_ref[, , c(
            "water_influx",
            "water_outflux"
          )],
          scen = state_scen[, , c(
            "water_influx",
            "water_outflux"
          )],
          local = FALSE,
          cell_area = cell_area
        )$full + calc_ecosystem_balance(
          ref = state_ref[, , c(
            "water_influx",
            "water_outflux"
          )],
          scen = state_scen[, , c(
            "water_influx",
            "water_outflux"
          )]
        )$full
    ) / 3

    # water total
    wt <- (
      calc_component(
        ref = state_ref[, , c(
          "water_influx",
          "water_outflux",
          "soil_water_pool"
        )],
        scen = state_scen[, , c(
          "water_influx",
          "water_outflux",
          "soil_water_pool"
        )],
        local = TRUE,
        cell_area = cell_area
      )$full +
        calc_component(
          ref = state_ref[, , c(
            "water_influx",
            "water_outflux",
            "soil_water_pool"
          )],
          scen = state_scen[, , c(
            "water_influx",
            "water_outflux",
            "soil_water_pool"
          )],
          local = FALSE,
          cell_area = cell_area
        )$full +
        calc_ecosystem_balance(
          ref = state_ref[, , c(
            "water_influx",
            "water_outflux",
            "soil_water_pool"
          )],
          scen = state_scen[, , c(
            "water_influx",
            "water_outflux",
            "soil_water_pool"
          )]
        )$full
    ) / 3

    if (nitrogen) {
      # nitrogen stocks (local change)
      ns <- (
        calc_component(
          ref = state_ref[, , c(
            "vegetation_nitrogen_pool",
            "soil_mineral_nitrogen_pool"
          )],
          scen = state_scen[, , c(
            "vegetation_nitrogen_pool",
            "soil_mineral_nitrogen_pool"
          )],
          local = TRUE,
          cell_area = cell_area
        )$full +
          calc_component(
            ref = state_ref[, , c(
              "vegetation_nitrogen_pool",
              "soil_mineral_nitrogen_pool"
            )],
            scen = state_scen[, , c(
              "vegetation_nitrogen_pool",
              "soil_mineral_nitrogen_pool"
            )],
            local = FALSE, cell_area = cell_area
          )$full +
          calc_ecosystem_balance(
            ref = state_ref[, , c(
              "vegetation_nitrogen_pool",
              "soil_mineral_nitrogen_pool"
            )],
            scen = state_scen[, , c(
              "vegetation_nitrogen_pool",
              "soil_mineral_nitrogen_pool"
            )]
          )$full
      ) / 3

      # nitrogen fluxes (local change)
      nf <- (
        calc_component(
          ref = state_ref[, , c(
            "nitrogen_influx",
            "nitrogen_outflux"
          )],
          scen = state_scen[, , c(
            "nitrogen_influx",
            "nitrogen_outflux"
          )],
          local = TRUE,
          cell_area = cell_area
        )$full +
          calc_component(
            ref = state_ref[, , c(
              "nitrogen_influx",
              "nitrogen_outflux"
            )],
            scen = state_scen[, , c(
              "nitrogen_influx",
              "nitrogen_outflux"
            )],
            local = FALSE,
            cell_area = cell_area
          )$full +
          calc_ecosystem_balance(
            ref = state_ref[, , c(
              "nitrogen_influx",
              "nitrogen_outflux"
            )],
            scen = state_scen[, , c(
              "nitrogen_influx",
              "nitrogen_outflux"
            )]
          )$full
      ) / 3

      nt <- (
        calc_component(
          ref = state_ref[, , c(
            "vegetation_nitrogen_pool",
            "soil_mineral_nitrogen_pool",
            "nitrogen_influx",
            "nitrogen_outflux"
          )],
          scen = state_scen[, , c(
            "vegetation_nitrogen_pool",
            "soil_mineral_nitrogen_pool",
            "nitrogen_influx",
            "nitrogen_outflux"
          )],
          local = TRUE,
          cell_area = cell_area
        )$full +
          calc_component(
            ref = state_ref[, , c(
              "vegetation_nitrogen_pool",
              "soil_mineral_nitrogen_pool",
              "nitrogen_influx",
              "nitrogen_outflux"
            )],
            scen = state_scen[, , c(
              "vegetation_nitrogen_pool",
              "soil_mineral_nitrogen_pool",
              "nitrogen_influx",
              "nitrogen_outflux"
            )],
            local = FALSE,
            cell_area = cell_area
          )$full +
          calc_ecosystem_balance(
            ref = state_ref[, , c(
              "vegetation_nitrogen_pool",
              "soil_mineral_nitrogen_pool",
              "nitrogen_influx",
              "nitrogen_outflux"
            )],
            scen = state_scen[, , c(
              "vegetation_nitrogen_pool",
              "soil_mineral_nitrogen_pool",
              "nitrogen_influx",
              "nitrogen_outflux"
            )]
          )$full
      ) / 3
    }
  }
  if (external_variability) {
    delta <- vegetation_structure_change * c2vr["vs", ] # vegetation_structure
    lc <- lc_raw$value * c2vr["lc", ]
    gi <- gi_raw$value * c2vr["gi", ]
    eb <- eb_raw$value * c2vr["eb", ]
  } else {
    delta <- vegetation_structure_change * delta_var # vegetation_structure
    lc <- lc_raw$value * lc_raw$var
    gi <- gi_raw$value * gi_raw$var
    eb <- eb_raw$value * eb_raw$var
    c2vr <- rbind(delta_var, lc_raw$var, gi_raw$var, eb_raw$var) # dim=(4,ncell)
    dimnames(c2vr) <- list(
      component = c("vs", "lc", "gi", "eb"),
      cell = 0:(ncells - 1)
    )
  }

  # calc total EcoRisk as the average of the 4 components
  ecorisk_full <- (delta + lc + gi + eb) / 4 # check for NAs

  if (nitrogen) {
    ecorisk <- list(
      ecorisk_total = ecorisk_full,
      vegetation_structure_change = delta,
      local_change = lc,
      global_importance = gi,
      ecosystem_balance = eb,
      c2vr = c2vr,
      carbon_stocks = cs,
      carbon_fluxes = cf,
      carbon_total = ct,
      water_fluxes = wf,
      water_stocks = NA,
      water_total = wt,
      nitrogen_stocks = ns,
      nitrogen_fluxes = nf,
      nitrogen_total = nt
    )
  } else {
    ecorisk <- list(
      ecorisk_total = ecorisk_full,
      vegetation_structure_change = delta,
      local_change = lc,
      global_importance = gi,
      ecosystem_balance = eb,
      c2vr = c2vr,
      carbon_stocks = cs,
      carbon_fluxes = cf,
      carbon_total = ct,
      water_fluxes = wf,
      water_stocks = NA,
      water_total = wt,
      nitrogen_stocks = NA,
      nitrogen_fluxes = NA,
      nitrogen_total = NA
    )
  }
  ###
  return(ecorisk)
}

#' Read in output data from LPJmL to calculate the ecosystem change metric
#' EcoRisk. This function is called by the wrapper function (ecorisk_wrapper),
#' unless you know what you are doing, don't use this function directly.
#'
#' Utility function to read in output data from LPJmL for calculation of EcoRisk
#'
#' @param files_reference folder of reference run
#' @param files_scenario folder of scenario run
#' @param save_file file to save read in data to (default NULL)
#' @param time_span_reference vector of years to use as scenario period
#' @param time_span_scenario vector of years to use as scenario period
#' @param nitrogen include nitrogen outputs for pools and fluxes into EcoRisk
#'                 calculation (default FALSE)
#' @param debug_mode write out all nitrogen state variables (default FALSE)
#' @param suppress_warnings suppress writing of Warnings, default: TRUE
#'
#' @return list data object containing arrays of state_ref, mean_state_ref,
#'         state_scen, mean_state_scen, fpc_ref, fpc_scen, bft_ref, bft_scen,
#'         cft_ref, cft_scen, lat, lon, cell_area
#'
#' @export
read_ecorisk_data <- function(
    files_reference, # nolint
    files_scenario,
    save_file = NULL,
    time_span_reference,
    time_span_scenario,
    nitrogen,
    debug_mode = FALSE,
    suppress_warnings = TRUE) {
  file_type <- tools::file_ext(files_reference$grid)

  if (file_type %in% c("json", "clm")) {
    # read grid
    grid <- lpjmlkit::read_io(
      files_reference$grid
    ) %>% suppressWarnings()
    # calculate cell area
    cell_area <- drop(lpjmlkit::read_io(
      filename = files_reference$terr_area
    )$data) %>% suppressWarnings() # in m2
    lat <- grid$data[, , 2]
    lon <- grid$data[, , 1]
    ncells <- length(lat)
    nyears <- length(time_span_scenario)
    nyears_ref <- length(time_span_reference)

    ### read in lpjml output
    # for vegetation_structure_change (fpc,fpc_bft,cftfrac)
    message("Reading in fpc, fpc_bft, cftfrac")

    cft_scen <- aperm(lpjmlkit::read_io(
      files_scenario$cftfrac,
      subset = list(year = as.character(time_span_scenario))
    ) %>%
      lpjmlkit::transform(to = c("year_month_day")) %>%
      lpjmlkit::as_array(aggregate = list(month = sum)), c(1, 3, 2)) %>%
      suppressWarnings()

    bft_scen <- aperm(lpjmlkit::read_io(
      files_scenario$fpc_bft,
      subset = list(year = as.character(time_span_scenario))
    ) %>%
      lpjmlkit::transform(to = c("year_month_day")) %>%
      lpjmlkit::as_array(aggregate = list(month = sum)), c(1, 3, 2)) %>%
      suppressWarnings()

    fpc_scen <- aperm(lpjmlkit::read_io(
      files_scenario$fpc,
      subset = list(year = as.character(time_span_scenario))
    ) %>%
      lpjmlkit::transform(to = c("year_month_day")) %>%
      lpjmlkit::as_array(aggregate = list(month = sum)), c(1, 3, 2)) %>%
      suppressWarnings()

    if (file.exists(files_reference$cftfrac)) {
      cft_ref <- aperm(lpjmlkit::read_io(
        files_reference$cftfrac,
        subset = list(year = as.character(time_span_reference))
      ) %>%
        lpjmlkit::transform(to = c("year_month_day")) %>%
        lpjmlkit::as_array(aggregate = list(month = sum)), c(1, 3, 2)) %>%
        suppressWarnings()
    } else {
      cft_ref <- cft_scen * 0
    }

    if (file.exists(files_reference$fpc_bft)) {
      bft_ref <- aperm(lpjmlkit::read_io(
        files_reference$fpc_bft,
        subset = list(year = as.character(time_span_reference))
      ) %>%
        lpjmlkit::transform(to = c("year_month_day")) %>%
        lpjmlkit::as_array(aggregate = list(month = sum)), c(1, 3, 2)) %>%
        suppressWarnings()
    } else {
      bft_ref <- bft_scen * 0
    }

    fpc_ref <- aperm(lpjmlkit::read_io(
      files_reference$fpc,
      subset = list(year = as.character(time_span_reference))
    ) %>%
      lpjmlkit::transform(to = c("year_month_day")) %>%
      lpjmlkit::as_array(aggregate = list(month = sum)), c(1, 3, 2)) %>%
      suppressWarnings()

    #### new input reading ###
    metric_files <- system.file(
      "extdata",
      "metric_files.yml",
      package = "biospheremetrics"
    ) %>%
      yaml::read_yaml()
    nclasses <- length(metric_files$metric$ecorisk_nitrogen$metric_class)
    nstate_dimensions <- 0
    for (i in seq_len(nclasses)) {
      nstate_dimensions <- nstate_dimensions +
        length(metric_files$metric$ecorisk_nitrogen$metric_class[[i]])
    }
    state_ref <- array(0, dim = c(ncells, nyears_ref, nstate_dimensions))
    state_scen <- array(0, dim = c(ncells, nyears, nstate_dimensions))
    class_names <- seq_len(nstate_dimensions)
    index <- 1
    # iterate over main classes (carbon pools, water fluxes ...)
    for (c in seq_len(nclasses)) {
      classe <- metric_files$metric$ecorisk_nitrogen$metric_class[[c]]
      nsubclasses <- length(classe)
      # iterate over subclasses (vegetation carbon, soil water ...)
      for (s in seq_len(nsubclasses)) {
        subclass <- classe[s]
        class_names[index] <- names(subclass)
        vars <- split_sign(unlist(subclass))
        for (v in seq_len(length(vars[, 1]))) {
          path_scen_file <- files_scenario[[vars[v, "variable"]]]
          if (file.exists(path_scen_file)) {
            header_scen <- lpjmlkit::read_meta(filename = path_scen_file)
            message(
              "Reading in ", path_scen_file, " with unit ", header_scen$unit,
              " -> as part of ", class_names[index]
            )
            var_scen <- lpjmlkit::read_io(
              path_scen_file,
              subset = list(year = as.character(time_span_scenario))
            ) %>%
              lpjmlkit::transform(to = c("year_month_day")) %>%
              lpjmlkit::as_array(aggregate = list(month = sum, band = sum), ) %>%
              drop() %>%
              suppressWarnings()
          } else {
            stop(paste("Couldn't read in:", path_scen_file, " - stopping!"))
          }
          path_ref_file <- files_reference[[vars[v, "variable"]]]
          if (file.exists(path_ref_file)) {
            header_ref <- lpjmlkit::read_meta(path_ref_file)
            message(
              "Reading in ", path_ref_file, " with unit ", header_ref$unit,
              " -> as part of ", class_names[index]
            )
            var_ref <- lpjmlkit::read_io(
              path_ref_file,
              subset = list(year = as.character(time_span_reference))
            ) %>%
              lpjmlkit::transform(to = c("year_month_day")) %>%
              lpjmlkit::as_array(aggregate = list(month = sum, band = sum)) %>%
              drop() %>%
              suppressWarnings()
          } else {
            stop(paste("Couldn't read in:", path_ref_file, " - stopping!"))
          }
          # if (window > 30){
          #  if (vars[v,"sign"] == "+"){
          #    state_scen[,,index,] <- state_scen[,,index,] + var_scen
          #    state_ref[,,index,] <- state_ref[,,index,] + var_ref
          #  } else { # vars[v,"sign"] == "-"
          #    state_scen[,,index,] <- state_scen[,,index,] - var_scen
          #    state_ref[,,index,] <- state_ref[,,index,] - var_ref
          #  }
          # }else{
          if (vars[v, "sign"] == "+") {
            state_scen[, , index] <- state_scen[, , index] + var_scen
            state_ref[, , index] <- state_ref[, , index] + var_ref
          } else { # vars[v,"sign"] == "-"
            state_scen[, , index] <- state_scen[, , index] - var_scen
            state_ref[, , index] <- state_ref[, , index] - var_ref
          }
          # }
        }
        index <- index + 1
      }
    }

    dimnames(state_scen) <- list(
      cell = 0:(ncells - 1),
      year = as.character(time_span_scenario), class = class_names
    )
    dimnames(state_ref) <- list(
      cell = 0:(ncells - 1),
      year = as.character(time_span_reference), class = class_names
    )
  } else if (file_type == "nc") { # to be added
    stop(
      "nc reading has not been updated to latest functionality. ",
      "Please contact Fabian Stenzel"
    )
  } else {
    stop("Unrecognized file type (", file_type, ")")
  }

  if (!(is.null(save_file))) {
    message("Saving data to: ", save_file)
    save(state_ref, state_scen, fpc_ref, fpc_scen,
      bft_ref, bft_scen, cft_ref, cft_scen, lat, lon, cell_area,
      file = save_file
    )
  }
  return(
    list(
      state_ref = state_ref,
      state_scen = state_scen,
      fpc_ref = fpc_ref,
      fpc_scen = fpc_scen,
      bft_ref = bft_ref,
      bft_scen = bft_scen,
      cft_ref = cft_ref,
      cft_scen = cft_scen,
      lat = lat,
      lon = lon,
      cell_area = cell_area
    )
  )
}

#' Calculates changes in vegetation structure (vegetation_structure_change)
#'
#' Utility function to calculate changes in vegetation structure
#' (vegetation_structure_change) for calculation of EcoRisk
#'
#' @param fpc_ref reference fpc array (dim: [ncells,npfts+1])
#' @param fpc_scen scenario fpc array (dim: [ncells,npfts+1])
#' @param bft_ref reference bft array (dim: [ncells,nbfts])
#' @param bft_scen scenario bft array (dim: [ncells,nbfts])
#' @param cft_ref reference cft array (dim: [ncells,ncfts])
#' @param cft_scen scenario cft array (dim: [ncells,ncfts])
#' @param weighting apply "old" (Ostberg-like), "new", or "equal" weighting of
#'                  vegetation_structure_change weights (default "equal")
#'
#' @return vegetation_structure_change array of size ncells with the
#'         vegetation_structure_change value [0,1] for each cell
#'
#' @examples
#' \dontrun{
#' vegetation_structure_change <- calc_delta_v(
#'   fpc_ref = fpc_ref_mean,
#'   fpc_scen = apply(fpc_scen, c(1, 2), mean),
#'   bft_ref = bft_ref_mean,
#'   bft_scen = apply(bft_scen, c(1, 2), mean),
#'   cft_ref = cft_ref_mean,
#'   cft_scen = apply(cft_scen, c(1, 2), mean),
#'   weighting = "equal"
#' )
#' }
#' @export
calc_delta_v <- function(fpc_ref, # nolint
                         fpc_scen,
                         bft_ref,
                         bft_scen,
                         cft_ref,
                         cft_scen,
                         weighting = "equal") {
  di <- dim(fpc_ref)
  ncells <- di[1]
  npfts <- di[2] - 1

  fpc_ref[fpc_ref < 0] <- 0
  fpc_scen[fpc_scen < 0] <- 0
  bft_ref[bft_ref < 0] <- 0
  bft_scen[bft_scen < 0] <- 0
  cft_ref[cft_ref < 0] <- 0
  cft_scen[cft_scen < 0] <- 0

  if (npfts == 9) {
    # barren = 1 - crop area - natural vegetation area +
    #   barren under bioenergy trees
    barren_area_ref <- (
      1 - rowSums(cft_ref) -
        rowSums(fpc_ref[, 2:10]) * fpc_ref[, 1] +
        rowSums(cft_ref[, c(16, 32)]) * (1 - rowSums(bft_ref[, c(1:4, 7:10)]))
    )

    barren_area_ref[barren_area_ref < 0] <- 0

    tree_area_ref <- array(0, dim = c(ncells, 11))

    # natural tree area fractions scaled by total natural frac
    tree_area_ref[, 1:7] <- (
      fpc_ref[, 2:8] * fpc_ref[, 1]
    )

    # fraction of rainfed tropical and temperate BE trees scaled by total
    #   rainfed bioenergy tree area and relative fpc of bioenergy trees and
    #   grass under bioenergy trees
    tree_area_ref[, 8:9] <- (
      cft_ref[, 16] * bft_ref[, 1:2] / rowSums(bft_ref[, c(1, 2, 4)])
    )

    # fraction of irrigated tropical and temperate BE trees scaled by total
    #   irrigated bioenergy tree area and relative fpc of bioenergy trees and
    #   grass under bioenergy trees
    tree_area_ref[, 10:11] <- (
      cft_ref[, 32] * bft_ref[, 7:8] / rowSums(bft_ref[, c(7, 8, 10)])
    )

    grass_area_ref <- array(0, dim = c(ncells, 20))

    # natural grass
    grass_area_ref[, 1:2] <- fpc_ref[, 9:10] * fpc_ref[, 1]

    # crops
    grass_area_ref[, 3:15] <- cft_ref[, 1:13] + cft_ref[, 17:29]

    # managed grass rf
    grass_area_ref[, 16] <- cft_ref[, 14]

    # managed grass irr
    grass_area_ref[, 17] <- cft_ref[, 30]

    # bioenergy grass
    grass_area_ref[, 18] <- cft_ref[, 15] + cft_ref[, 31]

    # fraction of rainfed grass under bioenergy trees
    grass_area_ref[, 19] <- (
      cft_ref[, 16] * bft_ref[, 4] / rowSums(bft_ref[, c(1, 2, 4)])
    )

    # fraction of irrigated grass under bioenergy trees
    grass_area_ref[, 20] <- (
      cft_ref[, 32] * bft_ref[, 10] / rowSums(bft_ref[, c(7, 8, 10)])
    )

    # barren
    barren_area_scen <- (
      1 - rowSums(cft_scen) -
        rowSums(fpc_scen[, 2:10]) * fpc_scen[, 1] +
        rowSums(cft_scen[, c(16, 32)]) * (1 - rowSums(bft_scen[, c(1:4, 7:10)]))
    )

    barren_area_scen[barren_area_scen < 0] <- 0

    tree_area_scen <- array(0, dim = c(ncells, 11))

    # natural tree area fractions scaled by total natural frac
    tree_area_scen[, 1:7] <- (
      fpc_scen[, 2:8] * fpc_scen[, 1]
    )

    # fraction of rainfed tropical and temperate BE trees scaled by total
    #   rainfed bioenergy tree area and relative fpc of bioenergy trees and
    #   grass under bioenergy trees
    tree_area_scen[, 8:9] <- (
      cft_scen[, 16] * bft_scen[, 1:2] / rowSums(bft_scen[, c(1, 2, 4)])
    )

    # fraction of irrigated tropical and temperate BE trees scaled by total
    #   irrigated bioenergy tree area and relative fpc of bioenergy trees and
    #   grass under bioenergy trees
    tree_area_scen[, 10:11] <- (
      cft_scen[, 32] * bft_scen[, 7:8] / rowSums(bft_scen[, c(7, 8, 10)])
    )
    grass_area_scen <- array(0, dim = c(ncells, 20))

    # natural grass
    grass_area_scen[, 1:2] <- fpc_scen[, 9:10] * fpc_scen[, 1]

    # crops
    grass_area_scen[, 3:15] <- cft_scen[, 1:13] + cft_scen[, 17:29]

    # managed grass rf
    grass_area_scen[, 16] <- cft_scen[, 14]

    # managed grass irr
    grass_area_scen[, 17] <- cft_scen[, 30]

    # bioenergy grass
    grass_area_scen[, 18] <- cft_scen[, 15] + cft_scen[, 31]

    # fraction of rainfed grass under bioenergy trees
    grass_area_scen[, 19] <- (
      cft_scen[, 16] * bft_scen[, 4] / rowSums(bft_scen[, c(1, 2, 4)])
    )

    # fraction of irrigated grass under bioenergy trees
    grass_area_scen[, 20] <- (
      cft_scen[, 32] * bft_scen[, 10] / rowSums(bft_scen[, c(7, 8, 10)])
    )

    # evergreenness, needleleavedness, tropicalness, borealness, naturalness
    tree_attributes <- matrix(
      c(
        c(1, 0, 1, 0, 1), # 1 TrBE
        c(0, 0, 1, 0, 1), # 2 TrBR
        c(1, 1, 0, 0, 1), # 3 TeNE
        c(1, 0, 0, 0, 1), # 4 TeBE
        c(0, 0, 0, 0, 1), # 5 TeBS
        c(1, 1, 0, 1, 1), # 6 BoNE
        c(0, 0.25, 0, 1, 1), # 7 BoS (including larchs)
        c(1, 0, 1, 0, 0), # 8 TrBi tropical bioenergy rainfed
        c(0, 0, 0, 0, 0), # 9 TeBi temperate bioenergy rainfed
        c(1, 0, 1, 0, 0), # 10 TrBi tropical bioenergy irrigated
        c(0, 0, 0, 0, 0) # 11 TeBi temperate bioenergy irrigated
      ),
      nrow = 11,
      byrow = TRUE
    )

    if (weighting == "equal") {
      tree_weights <- c(0.2, 0.2, 0.2, 0.2, 0.2)

      # changed compared to Sebastian Ostberg's method
    } else if (weighting == "new") {
      tree_weights <- c(0.2, 0.2, 0.3, 0.3, 0.3) / 1.3

      # Sebastian's method (no downscaling to weightsum 1)
    } else if (weighting == "old") {
      tree_weights <- c(0.2, 0.2, 0.3, 0.3, 0.3)
    } else {
      stop("Unknown method of weighting.")
    }

    grass_attributes <- array(0, dim = c(ncells, 20, 2))
    # 1 C3grass
    # 2 C4grass
    # 3 TemperateCereals
    # 4 Rice
    # 5 Maize
    # 6 TropicalCereals
    # 7 Pulses
    # 8 TemperateRoots
    # 9 TropicalRoots
    # 10 Sunflower
    # 11 Soybean
    # 12 Groundnut
    # 13 Rapeseed
    # 14 Sugarcane
    # 15 Others
    # 16 Managed grass rainfed
    # 17 Managed grass irrigated
    # 18 Bioenergy grass
    # 19 Grass under rainfed Bioenergy trees
    # 20 Grass under irrigated Bioenergy trees

    # tropicalness
    grass_attributes[, , 1] <- rep(
      c(0, 1, 0, 1, 1, 1, 0.5, 0, 1, 0.5, 1, 1, 0.5, 1, 0.5, NA, NA, 1, NA, NA),
      each = ncells
    )

    # naturalness
    grass_attributes[, , 2] <- rep(
      c(1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
      each = ncells
    )

    # dynamic share of tropicalness for rf/irr grasslands taken from ratio of
    #   bioenergy grasses
    dyn_grass_attributes <- cbind(
      bft_scen[, 6] / rowSums(bft_scen[, 5:6]),
      bft_scen[, 12] / rowSums(bft_scen[, 11:12])
    )

    dyn_grass_attributes[!is.finite(dyn_grass_attributes)] <- 0

    # managed grass rf/irr
    grass_attributes[, 16:17, 1] <- dyn_grass_attributes

    # grass under biotrees rf/irr (taken from managed grass)
    grass_attributes[, 19:20, 1] <- dyn_grass_attributes

    if (weighting == "equal") {
      grass_weights <- c(0.2, 0.2)

      # changed compared to Sebastian Ostberg's method
    } else if (weighting == "new") {
      grass_weights <- c(0.5, 0.5)

      # Sebastian Ostbergs's method (no downscaling to weightsum 1)
    } else if (weighting == "old") {
      grass_weights <- c(0.3, 0.3)
    } else {
      stop("Unknown method of weighting.")
    }
  } else if (npfts == 11) {
    # barren = 1 - crop area - natural vegetation area +
    #   barren under bioenergy trees
    barren_area_ref <- (
      1 - rowSums(cft_ref) -
        rowSums(fpc_ref[, 2:12]) * fpc_ref[, 1] +
        rowSums(cft_ref[, c(16, 32)]) * (1 - rowSums(bft_ref[, c(4:9, 13:18)]))
    )

    barren_area_ref[barren_area_ref < 0] <- 0

    tree_area_ref <- array(0, dim = c(ncells, 12))

    # natural tree area fractions scaled by total natural frac
    tree_area_ref[, 1:8] <- fpc_ref[, 2:9] * fpc_ref[, 1]

    # fraction of rainfed tropical and temperate BE trees scaled by total
    #   rainfed bioenergy tree area and relative fpc of bioenergy trees and
    #   grass under bioenergy trees
    tree_area_ref[, 9:10] <- (
      cft_ref[, 16] * bft_ref[, 7:8] / rowSums(bft_ref[, 4:8])
    )

    # fraction of irrigated tropical and temperate BE trees scaled by total
    #   irrigated bioenergy tree area and relative fpc of bioenergy trees and
    #   grass under bioenergy trees
    tree_area_ref[, 11:12] <- (
      cft_ref[, 32] * bft_ref[, 16:17] / rowSums(bft_ref[, 13:17])
    )

    grass_area_ref <- array(0, dim = c(ncells, 21))

    # natural grass
    grass_area_ref[, 1:3] <- fpc_ref[, 10:12] * fpc_ref[, 1]

    # crops
    grass_area_ref[, 4:16] <- cft_ref[, 1:13] + cft_ref[, 17:29]

    # managed grass rf
    grass_area_ref[, 17] <- cft_ref[, 14]

    # managed grass irr
    grass_area_ref[, 18] <- cft_ref[, 30]

    # bioenergy grass
    grass_area_ref[, 19] <- cft_ref[, 15] + cft_ref[, 31]

    # fraction of rainfed grass under bioenergy trees
    grass_area_ref[, 20] <- (
      cft_ref[, 16] * rowSums(bft_ref[, 4:6]) / rowSums(bft_ref[, 4:8])
    )

    # fraction of irrigated grass under bioenergy trees
    grass_area_ref[, 21] <- (
      cft_ref[, 32] * rowSums(bft_ref[, 13:15]) / rowSums(bft_ref[, 13:17])
    )

    # barren = 1 - crop area - natural vegetation area +
    #   barren under bioenergy trees
    barren_area_scen <- (
      1 - rowSums(cft_scen) -
        rowSums(fpc_scen[, 2:12]) * fpc_scen[, 1] +
        rowSums(cft_scen[, c(16, 32)]) *
          (1 - rowSums(bft_scen[, c(4:9, 13:18)]))
    )

    barren_area_scen[barren_area_scen < 0] <- 0

    tree_area_scen <- array(0, dim = c(ncells, 12))

    # natural tree area fractions scaled by total natural frac
    tree_area_scen[, 1:8] <- fpc_scen[, 2:9] * fpc_scen[, 1]

    # fraction of rainfed tropical and temperate BE trees scaled by total
    #   rainfed bioenergy tree area and relative fpc of bioenergy trees and
    #   grass under bioenergy trees
    tree_area_scen[, 9:10] <- (
      cft_scen[, 16] * bft_scen[, 7:8] / rowSums(bft_scen[, 4:8])
    )

    # fraction of irrigated tropical and temperate BE trees scaled by total
    #   irrigated bioenergy tree area and relative fpc of bioenergy trees and
    #   grass under bioenergy trees
    tree_area_scen[, 11:12] <- (
      cft_scen[, 32] * bft_scen[, 16:17] / rowSums(bft_scen[, 13:17])
    )

    grass_area_scen <- array(0, dim = c(ncells, 21))

    # natural grass
    grass_area_scen[, 1:3] <- fpc_scen[, 10:12] * fpc_scen[, 1]

    # crops
    grass_area_scen[, 4:16] <- cft_scen[, 1:13] + cft_scen[, 17:29]

    # managed grass rf
    grass_area_scen[, 17] <- cft_scen[, 14]

    # managed grass irr
    grass_area_scen[, 18] <- cft_scen[, 30]

    # bioenergy grass
    grass_area_scen[, 19] <- cft_scen[, 15] + cft_scen[, 31]

    # fraction of rainfed grass under bioenergy trees
    grass_area_scen[, 20] <- (
      cft_scen[, 16] * rowSums(bft_scen[, 4:6]) / rowSums(bft_scen[, 4:8])
    )

    # fraction of irrigated grass under bioenergy trees
    grass_area_scen[, 21] <- (
      cft_scen[, 32] * rowSums(bft_scen[, 13:15]) / rowSums(bft_scen[, 13:17])
    )

    # evergreenness, needleleavedness, tropicalness, borealness, naturalness
    tree_attributes <- matrix(
      c(
        c(1, 0, 1, 0, 1), # 1 TrBE
        c(0, 0, 1, 0, 1), # 2 TrBR
        c(1, 1, 0, 0, 1), # 3 TeNE
        c(1, 0, 0, 0, 1), # 4 TeBE
        c(0, 0, 0, 0, 1), # 5 TeBS
        c(1, 1, 0, 1, 1), # 6 BoNE
        c(0, 0, 0, 1, 1), # 7 BoBS
        c(0, 1, 0, 1, 1), # 8 BoNS
        c(1, 0, 1, 0, 0), # 9 TrBi tropical bioenergy rainfed
        c(0, 0, 0, 0, 0), # 10 TeBi temperate bioenergy rainfed
        c(1, 0, 1, 0, 0), # 11 TrBi tropical bioenergy irrigated
        c(0, 0, 0, 0, 0) # 12 TeBi temperate bioenergy irrigated
      ),
      nrow = 12,
      byrow = TRUE
    )

    if (weighting == "equal") {
      tree_weights <- c(0.2, 0.2, 0.2, 0.2, 0.2)

      # changed compared to Sebastian Ostberg's method
    } else if (weighting == "new") {
      tree_weights <- c(0.2, 0.2, 0.3, 0.3, 0.3) / 1.3

      # Sebastian Ostberg's method (no downscaling to weightsum 1)
    } else if (weighting == "old") {
      tree_weights <- c(0.2, 0.2, 0.3, 0.3, 0.3)
    } else {
      stop("Unknown method of weighting.")
    }

    grass_attributes <- array(0, dim = c(ncells, 21, 3))
    # 1 C4grass tropic
    # 2 C3grass temperate
    # 3 C3grass polar
    # 4 TemperateCereals
    # 5 Rice
    # 6 Maize
    # 7 TropicalCereals
    # 8 Pulses
    # 9 TemperateRoots
    # 10 TropicalRoots
    # 11 Sunflower
    # 12 Soybean
    # 13 Groundnut
    # 14 Rapeseed
    # 15 Sugarcane
    # 16 Others
    # 17 Managed grass rainfed
    # 18 Managed grass irrigated
    # 19 Bioenergy grass
    # 20 Grass under rainfed Bioenergy trees
    # 21 Grass under irrigated Bioenergy trees

    # tropicalness
    grass_attributes[, , 1] <- rep(
      c(
        1, 0, 0, 0, 1, 1, 1, 0.5, 0, 1, 0.5,
        1, 1, 0.5, 1, 0.5, NA, NA, 1, NA, NA
      ),
      each = ncells
    )

    # borealness
    grass_attributes[, , 2] <- rep(
      c(0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NA, NA, 0, NA, NA),
      each = ncells
    )

    # naturalness
    grass_attributes[, , 3] <- rep(
      c(1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
      each = ncells
    )

    # dynamic share of tropicalness for grass under irr biotrees
    dyn_trop_grass_attributes <- cbind(
      # dynamic share of tropicalness for rf grasslands
      bft_scen[, 1] / rowSums(bft_scen[, 1:3]),
      # dynamic share of tropicalness for irr grasslands
      bft_scen[, 10] / rowSums(bft_scen[, 10:12]),
      # dynamic share of tropicalness for grass under rf biotrees
      bft_scen[, 4] / rowSums(bft_scen[, 4:6]),
      bft_scen[, 13] / rowSums(bft_scen[, 13:15])
    )

    dyn_trop_grass_attributes[!is.finite(dyn_trop_grass_attributes)] <- 0

    # managed grass rf/irr, grass under biotrees rf/irr
    grass_attributes[, c(17, 18, 20, 21), 1] <- dyn_trop_grass_attributes

    # dynamic share of borealness for grass under irr biotrees
    dyn_boreal_grass_attributes <- cbind(
      # dynamic share of borealness for rf grasslands
      bft_scen[, 3] / rowSums(bft_scen[, 1:3]),
      # dynamic share of borealness for irr grasslands
      bft_scen[, 12] / rowSums(bft_scen[, 10:12]),
      # dynamic share of borealness for grass under rf biotrees
      bft_scen[, 6] / rowSums(bft_scen[, 4:6]),
      bft_scen[, 15] / rowSums(bft_scen[, 13:15])
    )

    dyn_boreal_grass_attributes[!is.finite(dyn_boreal_grass_attributes)] <- 0

    # managed grass rf/irr, grass under biotrees rf/irr
    grass_attributes[, c(17, 18, 20, 21), 2] <- dyn_boreal_grass_attributes

    if (weighting == "equal") {
      grass_weights <- c(0.2, 0.2, 0.2)
    } else if (weighting == "old" || weighting == "new") {
      grass_weights <- c(0.3333333, 0.3333333, 0.3333333)
    } else {
      stop("Unknown method of weighting.")
    }
  } else {
    stop("Unknown number of pfts.")
  }

  # compute vegetation_structure_change
  barren_v <- fBasics::rowMins(cbind(barren_area_ref, barren_area_scen))

  trees_v <- fBasics::rowMins(
    cbind(
      rowSums(tree_area_ref, na.rm = TRUE),
      rowSums(tree_area_scen, na.rm = TRUE)
    )
  )

  grass_v <- fBasics::rowMins(
    cbind(
      rowSums(grass_area_ref, na.rm = TRUE),
      rowSums(grass_area_scen, na.rm = TRUE)
    )
  )

  inner_sum_trees <- (
    # evergreenness
    abs(
      rowSums(tree_area_ref[, ] *
        rep(tree_attributes[, 1], each = ncells), na.rm = TRUE) -
        rowSums(tree_area_scen[, ] *
          rep(tree_attributes[, 1], each = ncells), na.rm = TRUE)
    ) * tree_weights[1] +
      # needleleavedness
      abs(
        rowSums(tree_area_ref[, ] *
          rep(tree_attributes[, 2], each = ncells), na.rm = TRUE) -
          rowSums(tree_area_scen[, ] *
            rep(tree_attributes[, 2], each = ncells), na.rm = TRUE)
      ) * tree_weights[2] +
      # tropicalness
      abs(
        rowSums(tree_area_ref[, ] *
          rep(tree_attributes[, 3], each = ncells), na.rm = TRUE) -
          rowSums(tree_area_scen[, ] *
            rep(tree_attributes[, 3], each = ncells), na.rm = TRUE)
      ) * tree_weights[3] +
      # borealness
      abs(
        rowSums(tree_area_ref[, ] *
          rep(tree_attributes[, 4], each = ncells), na.rm = TRUE) -
          rowSums(tree_area_scen[, ] *
            rep(tree_attributes[, 4], each = ncells), na.rm = TRUE)
      ) * tree_weights[4] +
      # naturalness
      abs(
        rowSums(tree_area_ref[, ] *
          rep(tree_attributes[, 5], each = ncells), na.rm = TRUE) -
          rowSums(tree_area_scen[, ] *
            rep(tree_attributes[, 5], each = ncells), na.rm = TRUE)
      ) * tree_weights[5]
  )

  if (npfts == 9) {
    inner_sum_grasses <- (
      # tropicalness
      abs(
        rowSums(grass_area_ref[, ] * grass_attributes[, , 1], na.rm = TRUE) -
          rowSums(grass_area_scen[, ] * grass_attributes[, , 1], na.rm = TRUE)
      ) * grass_weights[1] +
        # naturalness
        abs(
          rowSums(grass_area_ref[, ] * grass_attributes[, , 2], na.rm = TRUE) -
            rowSums(grass_area_scen[, ] * grass_attributes[, , 2], na.rm = TRUE)
        ) * grass_weights[2]
    )
  } else if (npfts == 11) {
    inner_sum_grasses <- (
      # tropicalness
      abs(
        rowSums(grass_area_ref[, ] * grass_attributes[, , 1], na.rm = TRUE) -
          rowSums(grass_area_scen[, ] * grass_attributes[, , 1], na.rm = TRUE)
      ) * grass_weights[1] +
        # borealness
        abs(
          rowSums(grass_area_ref[, ] * grass_attributes[, , 2], na.rm = TRUE) -
            rowSums(grass_area_scen[, ] * grass_attributes[, , 2], na.rm = TRUE)
        ) * grass_weights[2] +
        # naturalness
        abs(
          rowSums(grass_area_ref[, ] * grass_attributes[, , 3], na.rm = TRUE) -
            rowSums(grass_area_scen[, ] * grass_attributes[, , 3], na.rm = TRUE)
        ) * grass_weights[3]
    )
  } else {
    stop("Unknown number of pfts.")
  }

  vegetation_structure_change <- (
    1 - barren_v -
      trees_v * (1 - inner_sum_trees) -
      grass_v * (1 - inner_sum_grasses)
  )

  vegetation_structure_change[vegetation_structure_change < 0] <- 0

  vegetation_structure_change[!is.finite(vegetation_structure_change)] <- 0

  return(vegetation_structure_change)
}


################# further EcoRisk utility functions ##################

t_sigmoid_trafo <- function(x) {
  return(-1 / exp(3) + (1 + 1 / exp(3)) / (1 + exp(-6 * (x - 0.5))))
}


balance <- function(v1, v2) {
  return(1 - sum(v1 * v2) / (sqrt(sum(v1 * v1)) * sqrt(sum(v2 * v2))))
}


std_cellwise <- function(a) {
  return(apply(a, 1, stats::sd))
}


global_yearly_weighted_mean <- function(a, cell_area) {
  # a is matrix with dim=c(cells,years)
  # cell_area the corresponding cell_area array with dim=c(cells)
  return(
    sum(a * cell_area, na.rm = TRUE) /
      (length(a[1, ]) * sum(cell_area, na.rm = TRUE))
  )
}


globally_weighted_mean_foreach_var <- function(x, cell_area) { # nolint
  # x is matrix with dim=c(ncells,vars)
  # if vars==1 inflate x
  le <- length(x)
  if (le == length(cell_area)) dim(x) <- c(le, 1)

  # cell_area the corresponding cell_area array to x with dim=c(ncells)
  return(colSums(x * cell_area, na.rm = TRUE) / sum(cell_area, na.rm = TRUE))
}


s_change_to_var_ratio <- function(x, s) {
  return(1 / (1 + exp(-4 * (x / s - 2))))
}

aggregate_ecorisk_data_to_lower_resolution <- function(ecorisk_data_file,
                                                       new_resolution) {
  ecorisk_data_file <- "/p/projects/open/Fabian/Metrics/data/ecorisk_202311_data.RData"
  aggregation_factor <- 2 # 2 means from 0.5x0.5 to 1x1
  load(ecorisk_data_file) # load ecorisk data objects:
  # scenario data: state_scen,cft_scen,bft_scen,fpc_scen
  # reference data: state_ref,cft_ref,bft_ref,fpc_ref,
  # grid data: cell_area,lat,lon
  str(state_ref)

  state_scen <-
    state_ref <-
    fpc_scen <- fpc_ref
  bft_scen <- bft_ref
  cft_scen <- cft_ref

  di_state <- dim(state_scen)
  di_fpc <- dim(fpc_scen)
  di_bft <- dim(bft_scen)
  di_cft <- dim(cft_scen)
}


#' based on Heyder 2011 eq. 6-9; epsilon case handling from code
#'   by Sebastian Ostberg (not documented in papers)
#' @param ref mean reference state vector of dimension c(ncells,variables)
#' @param scen mean scenario state vector of dimension c(ncells,variables)
#' @param epsilon threshold for variables to be treated as 0
#'
#' @returns the length of the difference vector for each cell
state_diff_local <- function(ref, scen, epsilon = 10^-4) {
  # Ostberg code: case change_metric_lu_comparison_jun2013.c
  di <- dim(ref)
  # generally normalize the scenario state vector by the reference state
  s_scen <- scen / ref
  s_ref <- array(1, dim = di) # initialize

  # for variables in places, where ref is small (<epsilon),
  #   but scen larger (Ostberg code, line 798)
  # Sebastian set back scenario and reference vector, to keep the unscaled
  #   values (Ostberg code, line 804)
  cells_ref0 <- abs(ref) < epsilon & abs(scen) > epsilon
  s_scen[cells_ref0] <- scen[cells_ref0]
  s_ref[cells_ref0] <- ref[cells_ref0]

  # for variables in places, where ref and scen are small (<epsilon),
  #   return 0 (both are 1, difference is 0) (Ostberg code, line 809)
  s_scen[abs(ref) < epsilon & abs(scen) < epsilon] <- 1 # no change

  # normalize both state vectors by the sqrt(amount of state variables) to
  #   ensure length(s_ref)==1 (this is part of the weighting in the Ostberg
  #   code)
  s_ref <- s_ref / sqrt(di[2])
  s_scen <- s_scen / sqrt(di[2])

  # length of the local difference vector s_scen (sl2) - s_ref (sl1)
  return(sqrt(rowSums((s_scen - s_ref) * (s_scen - s_ref))))
}



#' c based on Heyder 2011 eq. 10-13
#'
#' @param ref mean reference state vector of dimension c(ncells,variables)
#' @param scen mean scenario state vector of dimension c(ncells,variables)
#' @param cell_area area of each cell as a vector of dim=c(ncells)
#' @param epsilon threshold for variables to be treated as 0
#'
#' @returns the length of the difference vector for each cell
state_diff_global <- function(ref, scen, cell_area, epsilon = 10^-4) {
  di <- dim(ref)
  ncells <- di[1]
  global_mean_ref <- globally_weighted_mean_foreach_var(ref, cell_area)
  global_mean_scen <- globally_weighted_mean_foreach_var(scen, cell_area)

  # if global mean state in ref period is 0 (e.g. for landuse vars in pnv run?)
  # take the mean scen state instead
  cells_ref0 <- abs(global_mean_ref) < epsilon & abs(global_mean_scen) > epsilon
  global_mean_ref[cells_ref0] <- global_mean_scen[cells_ref0]
  # if both are 0 take 1, then the division is defined but 0 - 0 leads
  # to no change, which is what EcoRisk should show
  cells_both0 <- (
    abs(global_mean_ref) < epsilon & abs(global_mean_scen) < epsilon
  )
  global_mean_ref[cells_both0] <- 1

  norm <- rep(global_mean_ref, each = ncells)
  dim(norm) <- dim(ref)
  s_scen <- scen / norm
  s_ref <- ref / norm

  # normalize both state vectors by the sqrt(amount of state variables) to
  #   ensure length(s_ref)==1
  # (this is part of the weighting in the Ostberg code)
  s_ref <- s_ref / sqrt(di[2])
  s_scen <- s_scen / sqrt(di[2])

  # length of the difference vector s_scen (sl2) - s_ref (sl1) for each cell
  return(sqrt(rowSums((s_scen - s_ref) * (s_scen - s_ref))))
}

calc_component <- function(ref, scen, local, cell_area) {
  di <- dim(ref)
  ncells <- di[1]
  nyears <- di[2]

  # calc mean ref and scen state
  if (length(di) == 2) {
    dim(ref) <- c(di, 1)
    dim(scen) <- c(di, 1)
  }
  ref_mean <- apply(ref, c(1, 3), mean)
  scen_mean <- apply(scen, c(1, 3), mean)


  if (local) {
    x <- t_sigmoid_trafo(state_diff_local(ref = ref_mean, scen = scen_mean))
  } else {
    x <- t_sigmoid_trafo(
      state_diff_global(ref = ref_mean, scen = scen_mean, cell_area = cell_area)
    )
  }
  # calculation of the change-to-variability ratio in my view is mathematically
  #   not correctly described in Heyder and Ostberg
  # - the way I understand it: recalculate the c/g/b value for each year of the
  #   ref period compared to the mean of the ref period as "scenario" and then
  #   calc the variability (sd) of that
  sigma_x_ref_list <- array(0, dim = c(ncells, nyears))
  for (i in seq_len(nyears)) {
    if (local) {
      sigma_x_ref_list[, i] <- t_sigmoid_trafo(
        state_diff_local(ref = ref_mean, scen = ref[, i, ])
      )
    } else {
      sigma_x_ref_list[, i] <- t_sigmoid_trafo(
        state_diff_global(
          ref = ref_mean,
          scen = ref[, i, ],
          cell_area = cell_area
        )
      )
    }
  }

  sigma_x_ref <- apply(sigma_x_ref_list, 1, stats::sd)
  c2vr <- s_change_to_var_ratio(x, sigma_x_ref)
  return(
    list(
      full = x * c2vr,
      value = x,
      var = c2vr
    )
  )
}


balance_shift <- function(ref, scen, epsilon = 10^-4) {
  # param ref with dimension c(ncells,nvars)
  # param scen with dimension c(ncells,nvars)

  if (is.null(dim(ref))) dim(ref) <- c(length(ref), 1)


  # first normalize as for local change
  s_scen <- scen / ref
  s_ref <- array(1, dim = dim(ref))

  # for variables in places, where ref is small (<epsilon), but scen larger
  # (Ostberg code, line 798/vector length calc in line 837)
  # set back scenario vector, to keep the unscaled values (Ostberg code,
  #   line 805)
  s_scen[abs(ref) < epsilon & abs(scen) > epsilon] <- (
    scen[abs(ref) < epsilon & abs(scen) > epsilon]
  )

  # for variables in places, where ref and scen are small (<epsilon),
  # set scen to 1 (both are 1, difference is 0 -- no change) (Ostberg code,
  #   line 809)

  # results in no change
  s_scen[abs(ref) < epsilon & abs(scen) < epsilon] <- 1
  # absa(_ts) in Sebastians Ostberg's code
  abs_ref <- sqrt(rowSums(s_ref * s_ref))
  # absb(_ts) in Sebastian Ostberg's code
  abs_scen <- sqrt(rowSums(s_scen * s_scen))

  # =1-angle_ts
  b1 <- 1 - (rowSums(s_ref * s_scen) / abs_ref / abs_scen) # todo: all -> 0

  # restrain to the maximum range for the acos function
  b1[b1 < 0] <- 0
  b1[b1 > 2] <- 2
  angle <- acos(1 - b1) * 360 / 2 / pi
  angle[b1 == 1] <- 0
  b <- b1 * 2
  b[angle > 60] <- 1

  return(b)
}


calc_ecosystem_balance <- function(ref, scen) {
  di <- dim(ref)
  ncells <- di[1]
  nyears <- di[2]

  if (length(di) == 2) {
    dim(ref) <- c(di, 1)
    dim(scen) <- c(di, 1)
  }
  ref_mean <- apply(ref, c(1, 3), mean)
  scen_mean <- apply(scen, c(1, 3), mean)


  b <- balance_shift(ref = ref_mean, scen = scen_mean)
  # calculation of the change-to-variability ratio in my view is mathematically
  #   not correctly described in Heyder and Ostberg
  # - the way I understand it: recalculate the c/g/b value for each year of the
  #   ref period compared to the mean
  # of the ref period as "scenario" and then calc the variability (sd) of that
  sigma_b_ref_list <- array(0, dim = c(ncells, nyears))
  for (i in seq_len(nyears)) {
    sigma_b_ref_list[, i] <- balance_shift(ref = ref_mean, scen = ref[, i, ])
  }
  sigma_b_ref <- apply(sigma_b_ref_list, 1, stats::sd)
  c2vr <- s_change_to_var_ratio(b, sigma_b_ref)
  return(
    list(
      full = b * c2vr,
      value = b,
      var = c2vr
    )
  )
}


#' Create modified EcoRisk data file
#'
#' Function to create a modified EcoRisk data file where each reference cell is
#' compared to the average reference biome cell. The scenario period is
#' overwritten with the original reference period and all reference cells are
#' set to the average cell of the prescribed reference biome ref_biom
#'
#' @param data_file_in path to input data
#' @param data_file_out path to save modified data to
#' @param biome_classes_in biome classes object as returned from classify_biomes
#' @param ref_biom reference biome from biome classes that all cells should
#'        be compared to
#'
#' @export
replace_ref_data_with_average_ref_biome_cell <- function(
    # nolint
    data_file_in,
    data_file_out,
    biome_classes_in,
    ref_biom) {
  if (data_file_in == data_file_out) {
    stop(
      "Same file for input and output of data, would overwrite ",
      "original data. Aborting."
    )
  }

  load(data_file_in)

  ref_cells <- which(biome_classes_in$biome_id == ref_biom)

  # first set all scen vars to the ref vars # [1:64240, 1:30, 1:10]
  state_scen <- state_ref

  fpc_scen <- fpc_ref
  bft_scen <- bft_ref
  cft_scen <- cft_ref

  di_state <- dim(state_scen)
  di_fpc <- dim(fpc_scen)
  di_bft <- dim(bft_scen)
  di_cft <- dim(cft_scen)

  # now replace all ref cells with that of the mean ref biom cell
  # FS 2022-08-10: keeping the year-to-year variation
  if (length(ref_cells) == 1) {
    av_year_state <- state_scen[ref_cells, , ]
    fpc_ref <- rep(fpc_scen[ref_cells, , ], each = di_fpc[1])
    bft_ref <- rep(bft_scen[ref_cells, , ], each = di_bft[1])
    cft_ref <- rep(cft_scen[ref_cells, , ], each = di_cft[1])
  } else {
    av_year_state <- apply(state_scen[ref_cells, , ], c(2, 3), mean)
    fpc_ref <- rep(
      apply(fpc_scen[ref_cells, , ], c(2, 3), mean),
      each = di_fpc[1]
    )
    bft_ref <- rep(
      apply(bft_scen[ref_cells, , ], c(2, 3), mean),
      each = di_bft[1]
    )
    cft_ref <- rep(
      apply(cft_scen[ref_cells, , ], c(2, 3), mean),
      each = di_cft[1]
    )
  }
  state_ref <- rep(av_year_state, each = di_state[1])
  dim(state_ref) <- di_state
  dimnames(state_ref) <- dimnames(state_scen)


  # is the same for each year, thus for the mean just take one year
  # mean_state_ref <- rep(colMeans(av_year_state), each = di_state[1])
  # FS: mean states were removed from data file, removing also here

  dim(fpc_ref) <- di_fpc
  dimnames(fpc_ref) <- dimnames(fpc_scen)
  dim(bft_ref) <- di_bft
  dimnames(bft_ref) <- dimnames(bft_scen)
  dim(cft_ref) <- di_cft
  dimnames(cft_ref) <- dimnames(cft_scen)


  # and write out the modified data
  # save(state_ref,mean_state_ref,state_scen,mean_state_scen,fpc_ref,fpc_scen,
  # bft_ref,bft_scen,cft_ref,cft_scen,lat,lon,cell_area,file = data_file_out)
  save(state_ref,
    state_scen,
    fpc_ref,
    fpc_scen,
    bft_ref,
    bft_scen,
    cft_ref,
    cft_scen,
    lat,
    lon,
    cell_area,
    file = data_file_out
  )
}


#' Create modified EcoRisk data for crosstable
#'
#' Function to create a modified EcoRisk data file where for each biome
#' the average scenario cell is compared to the average scenario cell of all
#' other biomes. This can then be used to compute a crosstable with the average
#' difference between each of them as in the SI of Ostberg et al. 2013
#' (Critical impacts of global warming on land ecosystems)
#'
#' @param data_file_in path to input data
#' @param data_file_out path to save modified data to
#' @param biome_classes_in biome classes object as returned from classify_biomes
#' @param pick_cells pick one specific cell as representative for the biome
#'        instead of computing the average state
#' @param baseline_ref logical, use reference state as baseline?
#'        default is FALSE - use scenario state
#'
#' @return c2vr array to be used in the ecorisk call
#'
#' @export
ecorisk_cross_table <- function(data_file_in,
                                data_file_out,
                                biome_classes_in,
                                pick_cells = NULL,
                                baseline_ref = FALSE) {
  if (data_file_in == data_file_out) {
    stop(
      "Same file for input and output of data, would overwrite original data. ",
      "Aborting."
    )
  }
  load(data_file_in)

  # calculate ecorisk to get the variability
  ecorisk_c2vr <- drop(ecorisk_wrapper(
    path_ref = NULL,
    path_scen = NULL,
    read_saved_data = TRUE,
    nitrogen = TRUE,
    weighting = "equal",
    save_data = data_file_in,
    save_ecorisk = NULL,
    time_span_reference = as.character(1550:1579),
    time_span_scenario = as.character(1985:2014),
    dimensions_only_local = FALSE
  )$c2vr)

  if (baseline_ref) {
    # save reference state vectors, they contain relevant data (scen can go)
    state_sav <- state_ref
    fpc_sav <- fpc_ref
    bft_sav <- bft_ref
    cft_sav <- cft_ref
  } else {
    # save scenario state vectors, they contain relevant data (ref can go)
    state_sav <- state_scen
    fpc_sav <- fpc_scen
    bft_sav <- bft_scen
    cft_sav <- cft_scen
  }


  nbiomes <- max(biome_classes_in$biome_id) # by default 19
  state_ref <- array(0, dim = c(nbiomes, nbiomes, dim(state_sav)[2:3]))
  state_scen <- state_ref
  fpc_ref <- array(0, dim = c(nbiomes, nbiomes, dim(fpc_sav)[2:3]))
  fpc_scen <- fpc_ref
  bft_ref <- array(0, dim = c(nbiomes, nbiomes, dim(bft_sav)[2:3]))
  bft_scen <- bft_ref
  cft_ref <- array(0, dim = c(nbiomes, nbiomes, dim(cft_sav)[2:3]))
  cft_scen <- cft_ref
  c2vr <- array(0, dim = c(4, nbiomes))

  # now replace all ref cells with that of the mean ref biome cell
  for (b in sort(unique(biome_classes_in$biome_id))) {
    ref_cells <- which(biome_classes_in$biome_id == b)

    if (is.null(pick_cells)) {
      if (length(ref_cells) == 1) {
        # average over cells, keeping the average year-to-year variation
        av_state <- state_sav[ref_cells, , ]
        av_fpc <- fpc_sav[ref_cells, , ]
        av_bft <- bft_sav[ref_cells, , ]
        av_cft <- cft_sav[ref_cells, , ]
        av_c2vr <- ecorisk_c2vr[, ref_cells]
      } else {
        # average over cells, keeping the average year-to-year variation
        av_state <- apply(state_sav[ref_cells, , ], c(2, 3), mean)
        av_fpc <- apply(fpc_sav[ref_cells, , ], c(2, 3), mean)
        av_bft <- apply(bft_sav[ref_cells, , ], c(2, 3), mean)
        av_cft <- apply(cft_sav[ref_cells, , ], c(2, 3), mean)
        av_c2vr <- apply(ecorisk_c2vr[, ref_cells], c(1), mean)
      }
    } else { # use pick_cells
      av_state <- state_sav[pick_cells[b], , ]
      av_fpc <- fpc_sav[pick_cells[b], , ]
      av_bft <- bft_sav[pick_cells[b], , ]
      av_cft <- cft_sav[pick_cells[b], , ]
      av_c2vr <- ecorisk_c2vr[, pick_cells[b]]
    }

    state_ref[b, , , ] <- rep(av_state, each = nbiomes)
    state_scen[, b, , ] <- rep(av_state, each = nbiomes)

    mean_state_ref <- apply(state_ref, c(1, 3), mean)
    mean_state_scen <- apply(state_scen, c(1, 3), mean)

    fpc_ref[b, , , ] <- rep(av_fpc, each = nbiomes)
    fpc_scen[, b, , ] <- rep(av_fpc, each = nbiomes)

    bft_ref[b, , , ] <- rep(av_bft, each = nbiomes)
    bft_scen[, b, , ] <- rep(av_bft, each = nbiomes)

    cft_ref[b, , , ] <- rep(av_cft, each = nbiomes)
    cft_scen[, b, , ] <- rep(av_cft, each = nbiomes)
    c2vr[, b] <- av_c2vr
  }
  dim(state_ref) <- c(nbiomes * nbiomes, dim(state_sav)[2:3])
  dim(state_scen) <- c(nbiomes * nbiomes, dim(state_sav)[2:3])
  dim(fpc_ref) <- c(nbiomes * nbiomes, dim(fpc_sav)[2:3])
  dim(fpc_scen) <- c(nbiomes * nbiomes, dim(fpc_sav)[2:3])
  dim(bft_ref) <- c(nbiomes * nbiomes, dim(bft_sav)[2:3])
  dim(bft_scen) <- c(nbiomes * nbiomes, dim(bft_sav)[2:3])
  dim(cft_ref) <- c(nbiomes * nbiomes, dim(cft_sav)[2:3])
  dim(cft_scen) <- c(nbiomes * nbiomes, dim(cft_sav)[2:3])
  dimnames(state_ref) <- list(
    cell = as.character(seq_len(nbiomes * nbiomes)),
    year = dimnames(state_sav)$year,
    class = dimnames(state_sav)$class
  )
  dimnames(state_scen) <- dimnames(state_ref)
  dimnames(fpc_ref) <- list(
    cell = as.character(seq_len(nbiomes * nbiomes)),
    band = dimnames(fpc_sav)$band,
    year = dimnames(fpc_sav)$year
  )
  dimnames(fpc_scen) <- dimnames(fpc_ref)
  dimnames(bft_ref) <- list(
    cell = as.character(seq_len(nbiomes * nbiomes)),
    band = dimnames(bft_sav)$band,
    year = dimnames(bft_sav)$year
  )
  dimnames(bft_scen) <- dimnames(bft_ref)
  dimnames(cft_ref) <- list(
    cell = as.character(seq_len(nbiomes * nbiomes)),
    band = dimnames(cft_sav)$band,
    year = dimnames(cft_sav)$year
  )
  dimnames(cft_scen) <- dimnames(cft_ref)
  dimnames(c2vr) <- list(
    component = c("vs", "lc", "gi", "eb"),
    biome = biome_classes_in$biome_names
  )


  lat <- rep(0, nbiomes * nbiomes)
  lon <- rep(1, nbiomes * nbiomes)
  cell_area <- rep(2, nbiomes * nbiomes)

  # and write out the modified data
  save(state_ref,
    mean_state_ref,
    state_scen,
    mean_state_scen,
    fpc_ref,
    fpc_scen,
    bft_ref,
    bft_scen,
    cft_ref,
    cft_scen,
    lat,
    lon,
    cell_area,
    file = data_file_out
  )
  return(c2vr)
}

#' Create modified EcoRisk data combining two time series
#'
#' Function to combine two EcoRisk data files (e.g. historic and futures) into
#' one file.
#'
#' @param hist_file path to input file with historic data
#' @param scen_file path to input file with scenario data
#' @param combined_file path to save modified data to
#'
#'
#' @export
ecorisk_combine_hist_and_scen_data <- function(hist_file, scen_file, combined_file) {
  load(hist_file)
  state_scen_hist <- state_scen
  fpc_scen_hist <- fpc_scen
  bft_scen_hist <- bft_scen
  cft_scen_hist <- cft_scen
  load(scen_file)
  state_scen_scen <- state_scen
  fpc_scen_scen <- fpc_scen
  bft_scen_scen <- bft_scen
  cft_scen_scen <- cft_scen

  dimnames_state_hist <- dimnames(state_scen_hist)
  dimnames_state_scen <- dimnames(state_scen_scen)
  dimnames_state_comb <- dimnames_state_hist
  dimnames_state_comb$year <- c(dimnames_state_hist$year, dimnames_state_scen$year)
  di_state_hist <- dim(state_scen_hist)
  di_state_scen <- dim(state_scen_scen)
  di_state_comb <- di_state_hist
  di_state_comb[2] <- di_state_hist[2] + di_state_scen[2]
  state_scen <- array(0, dim = di_state_comb)
  dimnames(state_scen) <- dimnames_state_comb
  state_scen[, dimnames_state_hist$year, ] <- state_scen_hist
  state_scen[, dimnames_state_scen$year, ] <- state_scen_scen

  dimnames_cft_hist <- dimnames(cft_scen_hist)
  dimnames_cft_scen <- dimnames(cft_scen_scen)
  dimnames_cft_comb <- dimnames_cft_hist
  dimnames_cft_comb$year <- c(dimnames_cft_hist$year, dimnames_cft_scen$year)
  di_cft_hist <- dim(cft_scen_hist)
  di_cft_scen <- dim(cft_scen_scen)
  di_cft_comb <- di_cft_hist
  di_cft_comb[3] <- di_cft_hist[3] + di_cft_scen[3]
  cft_scen <- array(0, dim = di_cft_comb)
  dimnames(cft_scen) <- dimnames_cft_comb
  cft_scen[, , dimnames_cft_hist$year] <- cft_scen_hist
  cft_scen[, , dimnames_cft_scen$year] <- cft_scen_scen

  dimnames_fpc_hist <- dimnames(fpc_scen_hist)
  dimnames_fpc_scen <- dimnames(fpc_scen_scen)
  dimnames_fpc_comb <- dimnames_fpc_hist
  dimnames_fpc_comb$year <- c(dimnames_fpc_hist$year, dimnames_fpc_scen$year)
  di_fpc_hist <- dim(fpc_scen_hist)
  di_fpc_scen <- dim(fpc_scen_scen)
  di_fpc_comb <- di_fpc_hist
  di_fpc_comb[3] <- di_fpc_hist[3] + di_fpc_scen[3]
  fpc_scen <- array(0, dim = di_fpc_comb)
  dimnames(fpc_scen) <- dimnames_fpc_comb
  fpc_scen[, , dimnames_fpc_hist$year] <- fpc_scen_hist
  fpc_scen[, , dimnames_fpc_scen$year] <- fpc_scen_scen

  dimnames_bft_hist <- dimnames(bft_scen_hist)
  dimnames_bft_scen <- dimnames(bft_scen_scen)
  dimnames_bft_comb <- dimnames_bft_hist
  dimnames_bft_comb$year <- c(dimnames_bft_hist$year, dimnames_bft_scen$year)
  di_bft_hist <- dim(bft_scen_hist)
  di_bft_scen <- dim(bft_scen_scen)
  di_bft_comb <- di_bft_hist
  di_bft_comb[3] <- di_bft_hist[3] + di_bft_scen[3]
  bft_scen <- array(0, dim = di_bft_comb)
  dimnames(bft_scen) <- dimnames_bft_comb
  bft_scen[, , dimnames_bft_hist$year] <- bft_scen_hist
  bft_scen[, , dimnames_bft_scen$year] <- bft_scen_scen

  if (file.exists(combined_file)) {
    message("Output file ", combined_file, " already exists. Aborting.")
  } else {
    message("Saving data to: ", combined_file)
    save(state_ref, state_scen, fpc_ref, fpc_scen, bft_ref, bft_scen,
      cft_ref, cft_scen, lat, lon, cell_area,
      file = combined_file
    )
  }
}

################# biome (dis-)aggregation functions ##################

#' Get biome names
#'
#' Returns biome names with variable length (abbreviated, short, or full)
#'
#' @param biome_name_length integer chose from 1,2,3 for abbreviated, short,
#'                        or full biome names
#'
#' @export
get_biome_names <- function(biome_name_length = 2) {
  biome_mapping <- utils::read.csv(
    file = system.file(
      "extdata",
      "biomes.csv",
      package = "biospheremetrics"
    ),
    sep = ";"
  )

  if (biome_name_length == 1) {
    biome_class_names <- biome_mapping$abbreviation
  } else if (biome_name_length == 2) {
    biome_class_names <- biome_mapping$short_name
  } else if (biome_name_length == 3) {
    biome_class_names <- biome_mapping$name
  } else {
    stop(
      "Value for parameter biome_name_length out of range 1,2,3 - ",
      "was given as: ",
      biome_name_length
    )
  }

  return(biome_class_names)
}


#' Averages EcoRisk values across regions
#'
#' Returns the average value across either 4 regions or all (19) biomes for
#' EcoRisk and each of the subcomponents for each
#'
#' @param data List object, of which every item should be disaggregated
#' @param biome_class biome class list object as returned by classify_biomes
#' @param type string controlling whether to return  minimum, mean, maximum
#'        ("minmeanmax") or Q10,Q50,Q90 ("quantile") - default: "quantile"
#' @param classes string for into how many regions should be disaggregated
#'        "4biomes" (tropics/temperate/boreal/arctic) or "allbiomes"
#'
#' @examples
#' \dontrun{
#' disaggregate_into_biomes(
#'   ecorisk = ecorisk,
#'   biome_class = biome_classes,
#'   type = "quantile", classes = "4biomes"
#' )
#' }
#' @export
disaggregate_into_biomes <- function(data, # nolint
                                     biome_class,
                                     type = "quantile",
                                     classes = "4biomes") {
  di <- dim(data[[1]])
  comp_names <- names(data)

  if (type == "minmeanmax") {
    type_names <- c("min", "mean", "max")
  } else if (type == "quantile") {
    type_names <- c("Q10", "Q50", "Q90")
  }

  if (length(di) > 1) {
    slices <- di[2]
  } else {
    slices <- 1
  }

  if (classes == "4biomes") {
    tropics <- c(1, 2, 9, 10, 11)
    temperate <- c(3, 4, 5, 12, 13, 14)
    boreal <- c(6, 7, 8)
    arctic <- c(15, 16)
    cell_list <- list(
      tropical_cells = which(biome_class$biome_id %in% tropics),
      temperate_cells = which(biome_class$biome_id %in% temperate),
      boreal_cells = which(biome_class$biome_id %in% boreal),
      arctic_cells = which(biome_class$biome_id %in% arctic)
    )
    nclasses <- 4
  } else if (classes == "allbiomes") {
    nclasses <- max(unique(biome_class$biome_id))
  } else {
    stop(
      "Unknown parameter classes: ",
      classes,
      ", should be either '4biomes' or 'allbiomes'"
    )
  }

  data_dims <- length(data)

  data_biomes <- array(0, dim = c(nclasses, data_dims, 3, slices))

  if (classes == "4biomes") { # aggregate to trop/temp/boreal/arctic
    for (s in seq_len(slices)) {
      for (b in seq_len(nclasses)) {
        for (cc in seq_len(data_dims)) {
          if (type == "minmeanmax") {
            data_biomes[b, cc, , s] <- c(
              min(data[[cc]][cell_list[[b]], s], na.rm = TRUE),
              mean(data[[cc]][cell_list[[b]], s], na.rm = TRUE),
              max(data[[cc]][cell_list[[b]], s], na.rm = TRUE)
            )
          } else if (type == "quantile") {
            data_biomes[b, cc, , s] <- c(
              stats::quantile(
                data[[cc]][cell_list[[b]], s],
                probs = c(0.1, 0.5, 0.9),
                na.rm = TRUE
              )
            )
          } else {
            stop(paste(
              "type", type,
              "unknown. please choose either 'quantile' or 'minmeanmax'"
            ))
          } # end if
        } # end for
      } # end for
    } # end for

    biome_names <- c("tropics", "temperate", "boreal", "arctic")
    dimnames(data_biomes) <- list(
      biome_names, comp_names,
      type_names, seq_len(slices)
    )
  } else if (classes == "allbiomes") { # calculate all biomes separately
    for (s in seq_len(slices)) {
      for (b in seq_len(nclasses)) {
        for (cc in seq_len(data_dims)) {
          if (type == "minmeanmax") {
            data_biomes[b, cc, , s] <-
              c(
                min(data[[cc]][which(biome_class$biome_id == b), s], na.rm = TRUE),
                mean(data[[cc]][which(biome_class$biome_id == b), s], na.rm = TRUE),
                max(data[[cc]][which(biome_class$biome_id == b), s], na.rm = TRUE)
              )
          } else if (type == "quantile") {
            data_biomes[b, cc, , s] <- c(
              stats::quantile(
                data[[cc]][which(biome_class$biome_id == b), s],
                probs = c(0.1, 0.5, 0.9),
                na.rm = TRUE
              )
            )
          } else {
            stop(paste(
              "type", type,
              "unknown. please choose either 'quantile' or 'minmeanmax'"
            ))
          } # end if
        } # end for
      } # end for
    } # end for

    biome_names <- biome_class$biome_names
    dimnames(data_biomes) <- list(
      biome_names,
      comp_names,
      type_names,
      seq_len(slices)
    )
  } else {
    stop(
      "Unknown parameter classes: ",
      classes,
      ", should be either '4biomes' or 'allbiomes'"
    )
  }
  return(drop(data_biomes))
}


#' Calculate ecorisk with each biomes average cell
#'
#' Function to calculate ecorisk with each biomes average cell
#' as a measure of internal variability
#'
#' @param biome_classes biome classes object as returned by classify biomes,
#'                      calculated for data_file_base
#' @param data_file_base base EcoRisk to compute differences with (only ref is
#'                      relevant)
#' @param intra_biome_distrib_file file to additionally write results to
#' @param create create new modified files, or read already existing ones?
#' @param res how finegrained the distribution should be (resolution)
#' @param nitrogen include nitrogen outputs (default: TRUE)
#' @param plotting whether plots for each biome should be created
#' @param plot_folder folder to plot into
#' @param time_span_reference suitable 30 year reference period (e.g.
#'                            c(1901,1930), c(1550,1579))
#' @param ecorisk_components integer. how many subcomponents does the
#'        ecorisk object have?

#' @return data object with distibution - dim: c(biomes,ecorisk_variables,bins)
#'
#' @export
calculate_within_biome_diffs <- function(biome_classes, # nolint
                                         data_file_base,
                                         intra_biome_distrib_file,
                                         create = FALSE,
                                         nitrogen = TRUE,
                                         res = 0.05,
                                         plotting = FALSE,
                                         plot_folder,
                                         time_span_reference,
                                         ecorisk_components = 13) {
  biomes_abbrv <- get_biome_names(1)
  # nbiomes, nEcoRiskvars, nHISTclasses
  intra_biome_distrib <- array(
    0,
    dim = c(length(biome_classes$biome_names), ecorisk_components, 1 / res)
  )

  # start
  for (b in sort(unique(biome_classes$biome_id))) {
    filebase <- strsplit(data_file_base, "_data.RData")[[1]]
    message(
      "Calculating differences with biome ", b, " (",
      biome_classes$biome_names[b], ")"
    )

    data_file <- paste0(
      filebase, "_compared_to_average_", biomes_abbrv[b], "_data.RData"
    )
    ecorisk_file <- paste0(
      filebase, "_compared_to_average_", biomes_abbrv[b], "_gamma.RData"
    )

    if (create) {
      replace_ref_data_with_average_ref_biome_cell(
        data_file_in = data_file_base,
        data_file_out = data_file,
        biome_classes_in = biome_classes,
        ref_biom = b
      )
      ecorisk <- ecorisk_wrapper(
        # does not need to be specified, as data is read from file
        path_ref = NULL,
        # does not need to be specified, as data is read from file
        path_scen = NULL,
        read_saved_data = TRUE,
        nitrogen = nitrogen,
        weighting = "equal",
        save_data = data_file,
        save_ecorisk = ecorisk_file,
        time_span_reference = time_span_reference,
        time_span_scenario = time_span_reference,
        dimensions_only_local = FALSE
      )
    } else {
      # contains ecorisk list object
      load(ecorisk_file)
    }

    # compute average values per focus biom
    ref_cells <- which(biome_classes$biome_id == b)
    for (v in seq_len(10)) {
      intra_biome_distrib[b, v, ] <- graphics::hist(
        ecorisk[[v]][ref_cells],
        breaks = seq(0, 1, res), plot = FALSE
      )$counts
      intra_biome_distrib[b, v, ] <- (
        intra_biome_distrib[b, v, ] / sum(intra_biome_distrib[b, v, ])
      )
    }

    if (plotting) {
      plot_ecorisk_map(
        file = paste0(
          plot_folder, "/compare_ecorisk_to_", biomes_abbrv[b], ".png"
        ),
        focus_biome = b, biome_classes = biome_classes$biome_id,
        ecorisk_object = ecorisk,
        plot_dimension = "ecorisk_total",
        title = biome_classes$biome_names[b],
        legendtitle = "",
        eps = FALSE,
        title_size = 2,
        leg_yes = TRUE
      )

      plot_ecorisk_map(
        file = paste0(
          plot_folder, "/compare_vegetation_structure_change_to_",
          biomes_abbrv[b], ".png" # nolint
        ),
        focus_biome = b, biome_classes = biome_classes$biome_id,
        ecorisk_object = ecorisk,
        plot_dimension = "vegetation_structure_change",
        title = biome_classes$biome_names[b],
        legendtitle = "",
        eps = FALSE,
        title_size = 2,
        leg_yes = TRUE
      )

      plot_ecorisk_map(
        file = paste0(
          plot_folder, "/compare_gi_to_", biomes_abbrv[b], ".png"
        ),
        focus_biome = b,
        biome_classes = biome_classes$biome_id,
        ecorisk_object = ecorisk,
        plot_dimension = "global_importance",
        title = biome_classes$biome_names[b],
        legendtitle = "",
        eps = FALSE,
        title_size = 2,
        leg_yes = TRUE
      )

      plot_ecorisk_map(
        file = paste0(
          plot_folder, "/compare_lc_to_", biomes_abbrv[b], ".png"
        ),
        focus_biome = b,
        biome_classes = biome_classes$biome_id,
        ecorisk_object = ecorisk,
        plot_dimension = "local_change",
        title = biome_classes$biome_names[b],
        legendtitle = "",
        eps = FALSE,
        title_size = 2,
        leg_yes = TRUE
      )

      plot_ecorisk_map(
        file = paste0(
          plot_folder, "/compare_eb_to_", biomes_abbrv[b], ".png"
        ),
        focus_biome = b,
        biome_classes = biome_classes$biome_id,
        ecorisk_object = ecorisk,
        plot_dimension = "ecosystem_balance",
        title = biome_classes$biome_names[b],
        legendtitle = "",
        eps = FALSE,
        title_size = 2,
        leg_yes = TRUE
      )
    } # end if plotting
  }
  ecorisk_dimensions <- names(ecorisk)

  dim(intra_biome_distrib) <- c(
    biome = length(biome_classes$biome_names),
    variable = ecorisk_components, bin = 1 / res
  )
  dimnames(intra_biome_distrib) <- list(
    biome = biomes_abbrv, variable = ecorisk_dimensions, bin = seq(res, 1, res)
  )
  save(intra_biome_distrib, file = intra_biome_distrib_file)

  return(intra_biome_distrib)
}