LoadConfigurationFile <- function(environment) {
  # This function loads all the data contained in the configuration file 
  # specified as parameter 'configfile' in the function Load() of 
  # s2dverification.
  # Extends the environment given as parameter with the variables and 
  # functions needed for the configuration file mechanism to work.
  # Check ?LoadConfigurationFile for a detailed explanation on how to prepare
  # your own configuration file.

  with(environment, {

  # This function adds a key, value pair into a dictionary and checks there is no duplication
  putInDictionary <- function(dictionary, key, value) {
    if (key %in% names(dictionary)) {
      dictionary[[key]] <- value
      dictionary
    } else {
      dictionary[[length(dictionary) + 1]] <- value
      if (is.null(names(dictionary))) {
        names(dictionary) <- c(key)
      } else {
        names(dictionary)[[length(names(dictionary))]] <- key
      }
      dictionary
    }
  }

  # This function replaces all the occurrences of a variable in a string by their corresponding string stored in the replace_values.
  replaceVariablesInString <- function(string, replace_values) {
    if (length(strsplit(string, "\\$")[[1]]) > 1) {
      parts <- strsplit(string, "\\$")[[1]]
      output <- ""
      i <- 0
      for (part in parts) {
        if (i %% 2 == 0) {
          output <- paste(output, part, sep = "")
        } else {
          if (part %in% names(replace_values)) {
            output <- paste(output, replaceVariablesInString(replace_values[[part]], replace_values), sep = "")
          } else {
            # TODO: ERROR raise
            print(paste('The variable $', part, '$ was not defined in the configuration file.', sep = ''))
          }
        }
        i <- i + 1
      }
      output
    } else {
      string
    }
  }

  # This function obtains the information of a dataset and variable pair,
  # applying all the entries that match in the configuration file.
  getDatasetInfo <- function(matching_entries) {
    experiment_defaults <- c('$DEFAULT_EXP_MAIN_PATH$', '$DEFAULT_EXP_FILE_PATH$', '$DEFAULT_GRID$', '$DEFAULT_NC_VAR_NAME$')
    observation_defaults <- c('$DEFAULT_OBS_MAIN_PATH$', '$DEFAULT_OBS_FILE_PATH$', '$DEFAULT_NC_VAR_NAME$')
    info <- NULL

    for (entry in matching_entries) {
      if (is.null(info)) {
        info <- entry[-1:-2]
        if (length(info) == 4) {
          info[which(info == '*')] <- experiment_defaults[which(info == '*')]
        } else {
          info[which(info == '*')] <- observation_defaults[which(info == '*')]
        }
      } else {
        info[which(entry[-1:-2] != '*')] <- entry[-1:-2][which(entry[-1:-2] != '*')]
      }
    }
    
    info
  }

  # This function calculates the maximum of a positive vector.
  positiveMax <- function(x) {
    max(c(0, x[which(!is.na(x) && !is.null(x))]))
  }

  # Read the data from the configuration file.
  if (is.null(configfile)) {
    configfile <- system.file("config", "IC3.conf", package = "s2dverification")
  }
  all_lines <- readLines(configfile)
  all_lines <- gsub("\t", "", all_lines)
  all_lines <- gsub(" ", "", all_lines)
  all_lines <- all_lines[-grep("^#", all_lines)]
  all_lines <- all_lines[-grep("^$", all_lines)]
  key_positions <- grep("^!!", all_lines)

  # Check that the format of the configuration file is right.
  if (length(key_positions) != 12) {
    print('The configuration file is corrupted: the key lines do not match the expected pattern.')
  }

  # Start parsing the configuration.
  # Parse the 2-dimensional (atmospheric) and global mean (oceanic) variable entries
  if (key_positions[1] + 1 < key_positions[2]) {
    atm_var_lines <- all_lines[(key_positions[1] + 1):(key_positions[2] - 1)]
  } else {
    atm_var_lines <- c()
  }
  atm_vars <- unlist(strsplit(atm_var_lines, ","))
  if (key_positions[2] + 1 < key_positions[3]) {
    global_mean_var_lines <- all_lines[(key_positions[2] + 1):(key_positions[3] - 1)]
  } else {
    global_mean_var_lines <- c()
  }
  global_mean_vars <- unlist(strsplit(gsub(" ", "", global_mean_var_lines), ","))

  # The variables that are used in the configuration filed are kept in 'replace_values'.
  replace_values <- c()
  # We add in this table of variables the predefined values.
  replace_values <- putInDictionary(replace_values, "VAR_NAME", var)
  replace_values <- putInDictionary(replace_values, "STORE_FREQ", storefreq)
  # Parse the variables definitions in the whole configuration file
  if (key_positions[3] + 1 < key_positions[4]) {
    exp_table_definitions <- all_lines[(key_positions[3] + 1):(key_positions[4] - 1)]
  } else {
    exp_table_definitions <- c()
  }
  if (key_positions[5] + 1 < key_positions[6]) {
    file_per_member_exp_table_definitions <- all_lines[(key_positions[5] + 1):(key_positions[6] - 1)]
  } else {
    file_per_member_exp_table_definitions <- c()
  }
  if (key_positions[7] + 1 < key_positions[8]) {
    obs_table_definitions <- all_lines[(key_positions[7] + 1):(key_positions[8] - 1)]
  } else {
    obs_table_definitions <- c()
  }
  if (key_positions[9] + 1 < key_positions[10]) {
    file_per_member_obs_table_definitions <- all_lines[(key_positions[9] + 1):(key_positions[10] - 1)]
  } else {
    file_per_member_obs_table_definitions <- c()
  }
  if (key_positions[11] + 1 < key_positions[12]) {
    file_per_dataset_obs_table_definitions <- all_lines[(key_positions[11] + 1):(key_positions[12] - 1)]
  } else {
    file_per_dataset_obs_table_definitions <- c()
  }
  all_definitions <- c(exp_table_definitions, file_per_member_exp_table_definitions, obs_table_definitions)
  all_definitions <- c(all_definitions, file_per_member_obs_table_definitions, file_per_dataset_obs_table_definitions)
  if (length(grep("=", all_definitions)) == length(all_definitions)) {
    for (definition in all_definitions) {
      if (length(which(strsplit(definition, "")[[1]] == "=")) == 1) {
        var_name <- strsplit(definition, "=")[[1]][1]
        var_value <- strsplit(definition, "=")[[1]][2]
        if ((length(which(strsplit(var_value, "")[[1]] == "$")) %% 2) == 0) {
          replace_values <- putInDictionary(replace_values, var_name, var_value)
        } else {
          print('The configuration file is corrupted: there are incorrect variable definition lines in the definition zones. A closing "$" symbol may be missing.')
        }
      } else {
        print('The configuration file is corrupted: there are incorrect definition lines in the definition zones.')
      }
    }
  } else {
    print('The configuration file is corrupted: there are malformed definition lines in the definition zones.')
  }

  # Parse the entries in the tables
  ## These are the indices of the key positions in the vector of key positions
  tables_key_positions <- c(4, 6, 8, 10, 12)
  current_table <- 1
  for (table_key_position in tables_key_positions) {
    datasets <- list(c(), c(), c(), c())

    if (current_table < 3) {
      default_values <- c("$DEFAULT_EXP_MAIN_PATH$", "$DEFAULT_EXP_FILE_PATH$", "$DEFAULT_GRID$", "$DEFAULT_NC_VAR_NAME$")
    } else {
      default_values <- c("$DEFAULT_OBS_MAIN_PATH$", "$DEFAULT_OBS_FILE_PATH$", "$DEFAULT_NC_VAR_NAME$")
    }
    previous_values <- c("*", "*", default_values)
    table_lines <- c()
    table_end <- ifelse(table_key_position == max(tables_key_positions), length(all_lines), key_positions[table_key_position + 1] - 1)
    if ((key_positions[table_key_position] + 1) <= table_end) {
      table_lines <- all_lines[(key_positions[table_key_position] + 1):table_end]
      table_lines <- strsplit(table_lines, ",")
    }

    for (entry in table_lines) {
      if (entry[1] == '"') {
        entry[1] <- previous_values[1]
      }
      if ((length(entry) > 1)) {
        if (entry[2] == '"') {
          entry[2] <- previous_values[2]
        }
      } else {
        print('The variable column must be defined in all the entries in the tables in the configuration file.')
      }
      for (value_position in 1:length(default_values)) {
        if ((length(entry) > value_position + 1)) {
          if (entry[value_position + 2] == '"') {
            entry[value_position + 2] <- previous_values[value_position + 2]
          }
        } else {
          entry[value_position + 2] <- '*'
        }
      }
      if (entry[1] == '*') {
        if (entry[2] == '*') {
          datasets[[1]] <- c(datasets[[1]], list(entry))
        } else {
          datasets[[3]] <- c(datasets[[3]], list(entry))
        }
      } else {
        if (entry[2] == '*') {
          datasets[[2]] <- c(datasets[[2]], list(entry))
        } else {
          datasets[[4]] <- c(datasets[[4]], list(entry))
        }
      }
      previous_values <- entry
    }

    if (current_table == 1) {
      experiments <- datasets
    } else if (current_table == 2) {
      file_per_member_exps <- datasets
    } else if (current_table == 3) {
      observations <- datasets
    } else if (current_table == 4) {
      file_per_member_obs <- datasets
    } else if (current_table == 5) {
      file_per_dataset_obs <- datasets
    }

    current_table <- current_table + 1
  }

  # Check that the parameters are right
  has_entries_in_atm_vars <- sum(apply(array(atm_vars), 1, regexpr, var) > 0) > 0
  has_entries_in_global_mean_vars <- sum(apply(array(global_mean_vars), 1, regexpr, var) > 0) > 0
  if (has_entries_in_atm_vars) {
    is_atm_var <- TRUE
  } else if (has_entries_in_global_mean_vars) {
    is_atm_var <- FALSE
  } else {
    print('The variable specified is not available. Please check the supported variables in the configuration file.')
  }
  var_entries_in_exps <- c()
  if (length(unlist(experiments, recursive = FALSE)) > 0) {
    var_entries_in_exps <- which(apply(array(unlist(lapply(experiments, lapply, "[[", 2))), 1, regexpr, var) > 0)
  }
  var_entries_in_file_per_member_exps <- c()
  if (length(unlist(file_per_member_exps, recursive = FALSE)) > 0) {
    var_entries_in_file_per_member_exps <- which(apply(array(unlist(lapply(file_per_member_exps, lapply, "[[", 2))), 1, regexpr, var) > 0)
  }
  var_entries_in_obs <- c()
  if (length(unlist(observations, recursive = FALSE)) > 0) {
    var_entries_in_obs <- which(apply(array(unlist(lapply(observations, lapply, "[[", 2))), 1, regexpr, var) > 0)
  }
  var_entries_in_file_per_member_obs <- c()
  if (length(unlist(file_per_member_obs, recursive = FALSE)) > 0) {
    var_entries_in_file_per_member_obs <- which(apply(array(unlist(lapply(file_per_member_obs, lapply, "[[", 2))), 1, regexpr, var) > 0)
  }
  var_entries_in_file_per_dataset_obs <- c()
  if (length(unlist(file_per_dataset_obs, recursive = FALSE)) > 0) {
    var_entries_in_file_per_dataset_obs <- which(apply(array(unlist(lapply(file_per_dataset_obs, lapply, "[[", 2))), 1, regexpr, var) > 0)
  }

  exp_entries_in_exps <- exp_entries_in_exps_levels <- vector("list", length(exp))
  exp_entries_in_file_per_member_exps <- exp_entries_in_file_per_member_exps_levels <- vector("list", length(exp))
  for (jmod in 1:length(exp)) {
    if (length(unlist(experiments, recursive = FALSE)) > 0) {
      matches <- which(apply(array(unlist(lapply(experiments, lapply, "[[", 1))), 1, regexpr, exp[jmod]) > 0)
      if (length(matches) > 0) {
        exp_entries_in_exps[[jmod]] <- matches
        exps_levels <- lapply(as.list(1:4), f <- function(x) {x <- array(x, length(experiments[[x]]))})
        exp_entries_in_exps_levels[[jmod]] <- unlist(exps_levels)[matches]
      }
    }
    
    if (length(unlist(file_per_member_exps, recursive = FALSE)) > 0) {
      matches <- which(apply(array(unlist(lapply(file_per_member_exps, lapply, "[[", 1))), 1, regexpr, exp[jmod]) > 0)
      if (length(matches) > 0) {
        exp_entries_in_file_per_member_exps[[jmod]] <- matches
        file_per_member_exps_levels <- lapply(as.list(1:4), f <- function(x) {x <- array(x, length(file_per_member_exps[[x]]))})
        exp_entries_in_file_per_member_exps_levels[[jmod]] <- unlist(file_per_member_exps_levels)[matches]
      }
    }
  }
  if (sum(unlist(lapply(exp_entries_in_exps, length)) + unlist(lapply(exp_entries_in_file_per_member_exps, length)) > 0) < length(exp)) {
    print('The following experiments are not available:')
    exp[which((unlist(lapply(exp_entries_in_exps, length)) + unlist(lapply(exp_entries_in_file_per_member_exps, length)) > 0) == FALSE)]
    print('Please check the supported experiments in the configuration file.')
  }
  if (length(exp) > 0) {
    is_file_per_member_exp <- unlist(lapply(exp_entries_in_file_per_member_exps_levels, positiveMax)) >
                              unlist(lapply(exp_entries_in_exps_levels, positiveMax))
  } else {
    is_file_per_member_exp <- FALSE
  }
  exp_info <- list()
  for (jmod in 1:length(exp)) {
    if (is_file_per_member_exp[jmod]) {
      exp_var_matching_entries <- unlist(file_per_member_exps, recursive = FALSE)[intersect(var_entries_in_file_per_member_exps, exp_entries_in_file_per_member_exps[[jmod]])]
    } else {
      exp_var_matching_entries <- unlist(experiments, recursive = FALSE)[intersect(var_entries_in_exps, exp_entries_in_exps[[jmod]])]
    }
    if (length(exp_var_matching_entries) == 0) {
      print(paste('There are no matching entries in the configuration file for the experiment', exp[jmod], 'and the variable', var))
    } else {
      exp_info <- c(exp_info, list(getDatasetInfo(exp_var_matching_entries)))
    }
  }
  
  obs_entries_in_obs <- obs_entries_in_obs_levels <- vector("list", length(obs))
  obs_entries_in_file_per_member_obs <- obs_entries_in_file_per_member_obs_levels <- vector("list", length(obs))
  obs_entries_in_file_per_dataset_obs <- obs_entries_in_file_per_dataset_obs_levels <- vector("list", length(obs))
  for (jobs in 1:length(obs)) {
    if (length(unlist(observations, recursive = FALSE)) > 0) {
      matches <- which(apply(array(unlist(lapply(observations, lapply, "[[", 1))), 1, regexpr, obs[jobs]) > 0)
      if (length(matches) > 0) {
        obs_entries_in_obs[[jobs]] <- matches
        obs_levels <- lapply(as.list(1:4), f <- function(x) {x <- array(x, length(observations[[x]]))})
        obs_entries_in_obs_levels[[jobs]] <- unlist(obs_levels)[matches]
      }
    }

    if (length(unlist(file_per_member_obs, recursive = FALSE)) > 0) {
      matches <- which(apply(array(unlist(lapply(file_per_member_obs, lapply, "[[", 1))), 1, regexpr, obs[jobs]) > 0)
      if (length(matches) > 0) {
        obs_entries_in_file_per_member_obs[[jobs]] <- matches
        file_per_member_obs_levels <- lapply(as.list(1:4), f <- function(x) {x <- array(x, length(file_per_member_obs[[x]]))})
        obs_entries_in_file_per_member_obs_levels[[jobs]] <- unlist(file_per_member_obs_levels)[matches]
      }
    }

    if (length(unlist(file_per_dataset_obs, recursive = FALSE)) > 0) {
      matches <- which(apply(array(unlist(lapply(file_per_dataset_obs, lapply, "[[", 1))), 1, regexpr, obs[jobs]) > 0)
      if (length(matches) > 0) {
        obs_entries_in_file_per_dataset_obs[[jobs]] <- matches
        file_per_dataset_obs_levels <- lapply(as.list(1:4), f <- function(x) {x <- array(x, length(file_per_dataset_obs[[x]]))})
        obs_entries_in_file_per_dataset_obs_levels[[jobs]] <- unlist(file_per_dataset_obs_levels)[matches]
      }
    }
  }
  if (sum(unlist(lapply(obs_entries_in_obs, length)) + unlist(lapply(obs_entries_in_file_per_member_obs, length)) + 
          unlist(lapply(obs_entries_in_file_per_dataset_obs, length)) > 0) < length(obs)) {
    print('The following observations are not available:')
    obs[which((unlist(lapply(obs_entries_in_obs, length)) + unlist(lapply(obs_entries_in_file_per_member_obs, length)) + 
               unlist(lapply(obs_entries_in_file_per_dataset_obs, length)) > 0) == FALSE)]
    print('Please check the supported observations in the configuration file.')
  }
  if (length(obs) > 0) {
    is_file_per_member_obs <- unlist(lapply(obs_entries_in_file_per_member_obs_levels, positiveMax)) >
                              pmax(unlist(lapply(obs_entries_in_obs_levels, positiveMax)),
                                   unlist(lapply(obs_entries_in_file_per_dataset_obs_levels, positiveMax)))
    is_file_per_dataset_obs <- unlist(lapply(obs_entries_in_file_per_dataset_obs_levels, positiveMax)) >
                               pmax(unlist(lapply(obs_entries_in_obs_levels, positiveMax)),
                                    unlist(lapply(obs_entries_in_file_per_member_obs_levels, positiveMax)))
  } else {
    is_file_per_member_obs <- is_file_per_dataset_obs <- FALSE
  }
  obs_info <- list()
  for (jobs in 1:length(obs)) {
    if (is_file_per_member_obs[jobs]) {
      obs_var_matching_entries <- unlist(file_per_member_obs, recursive = FALSE)[intersect(var_entries_in_file_per_member_obs, obs_entries_in_file_per_member_obs[[jobs]])]
    } else if (is_file_per_dataset_obs[jobs]) {
      obs_var_matching_entries <- unlist(file_per_dataset_obs, recursive = FALSE)[intersect(var_entries_in_file_per_dataset_obs, obs_entries_in_file_per_dataset_obs[[jobs]])]
    } else {
      obs_var_matching_entries <- unlist(observations, recursive = FALSE)[intersect(var_entries_in_obs, obs_entries_in_obs[[jobs]])]
    }
    if (length(obs_var_matching_entries) == 0) {
      print(paste('There are no matching entries in the configuration file for the observation', obs[jobs], 'and the variable', var))
    } else {
      obs_info <- c(obs_info, list(getDatasetInfo(obs_var_matching_entries)))
    }
  }

  })
}
