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.
  #
  # Expected defined variables in the specified environment:
  #  - configfile
  #  - var
  #  - exp
  #  - obs
  # The relevant variables and function that extend the environment are the 
  # following:
  #  - putInDictionary
  #  - replaceVariablesInString
  #  - getDatasetInfo
  #  - is_file_per_member_exp
  #  - is_file_per_member_obs
  #  - is_file_per_dataset_obs
  #  - exp_info
  #  - obs_info
  #  - is_2d_var
  #
  # Args:
  #   environment: Environment to be extended
  #
  # Returns:
  #   This function returns nothing but extends the specified environment instead.
  #
  # History:
  #   0.1  #  2015-01  (N. Manubens, nicolau.manubens@ic3.cat)  # Original code

  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 {
            stop(paste('Error: 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$', '$DEFAULT_SUFFIX$', '$DEFAULT_VAR_MIN$', '$DEFAULT_VAR_MAX$')
    observation_defaults <- c('$DEFAULT_OBS_MAIN_PATH$', '$DEFAULT_OBS_FILE_PATH$', '$DEFAULT_NC_VAR_NAME$', '$DEFAULT_SUFFIX$', '$DEFAULT_VAR_MIN$', '$DEFAULT_VAR_MAX$')
    info <- NULL

    for (entry in matching_entries) {
      if (is.null(info)) {
        info <- entry[-1:-2]
        if (length(info) == 7) {
          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
  }

  if (!silent) {
    print(paste("* Reading configuration file:", configfile))
  }
  # Read the data from the configuration file.
  ## Remove comments, tabulations, spaces, empty lines, ...
  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)]
  ## Detect key lines
  key_positions <- grep("^!!", all_lines)

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

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

  # The variables that are used in the configuration filed are kept in 
  # 'replace_values', an associative array (key-value array or dictionary).
  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 {
          stop('Error: The configuration file is corrupted: there are incorrect variable definition lines in the definition zones. A closing "$" symbol may be missing.')
        }
      } else {
        stop('Error: The configuration file is corrupted: there are incorrect definition lines in the definition zones.')
      }
    }
  } else {
    stop('Error: 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$", '$DEFAULT_SUFFIX$', '$DEFAULT_VAR_MIN$', '$DEFAULT_VAR_MAX$')
    } else {
      default_values <- c("$DEFAULT_OBS_MAIN_PATH$", "$DEFAULT_OBS_FILE_PATH$", "$DEFAULT_NC_VAR_NAME$", '$DEFAULT_SUFFIX$', '$DEFAULT_VAR_MIN$', '$DEFAULT_VAR_MAX$')
    }
    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 {
        stop('Error: 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 var, exp and obs parameters are right and keep the entries that match for each dataset.
  # Afterwards, the matching entries are applied sequentially (as specified in ?LoadConfigurationFile)
  # and the replace_values are applied to the result.
  # Finally a path pattern for each dataset is provided.
  if (length(two_d_vars) > 0) {
    has_entries_in_2d_vars <- sum(apply(array(two_d_vars), 1, regexpr, var) > 0) > 0
  } else {
    has_entries_in_2d_vars <- FALSE
  }
  if (length(global_mean_vars) > 0) {
    has_entries_in_global_mean_vars <- sum(apply(array(global_mean_vars), 1, regexpr, var) > 0) > 0
  } else {
    has_entries_in_global_mean_vars <- FALSE
  }
  if (has_entries_in_2d_vars) {
    is_2d_var <- TRUE
  } else if (has_entries_in_global_mean_vars) {
    is_2d_var <- FALSE
  } else {
    stop('Error: 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)
  }

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

  exp_info <- list()
  is_file_per_member_exp <- rep(FALSE, length(exp))
  is_file_per_dataset_exp <- rep(FALSE, length(exp))
  jmod <- 1
  for (mod in exp) {
    mod_var_matching_entries <- mod_var_matching_entries_levels <- c()
    file_per_member_mod_var_matching_entries <- file_per_member_mod_var_matching_entries_levels <- c()
    
    if (length(unlist(experiments, recursive = FALSE)) > 0) {
      mod_entries_in_exps <- which(unlist(lapply(lapply(unlist(lapply(experiments, lapply, "[[", 1), recursive = FALSE), regexpr, mod), .IsFullMatch, mod)))
      if (length(mod_entries_in_exps) > 0) {
        mod_var_matching_entries <- unlist(experiments, recursive = FALSE)[intersect(var_entries_in_exps, mod_entries_in_exps)]
        experiments_levels <- lapply(as.list(1:4), f <- function(x) {x <- array(x, length(experiments[[x]]))})
        mod_var_matching_entries_levels <- unlist(experiments_levels)[intersect(var_entries_in_exps, mod_entries_in_exps)]
      }
    }

    if (length(unlist(file_per_member_exps, recursive = FALSE)) > 0) {
      file_per_member_mod_entries_in_exps <- which(unlist(lapply(lapply(unlist(lapply(file_per_member_exps, lapply, "[[", 1), recursive = FALSE), regexpr, mod), .IsFullMatch, mod)))
      if (length(file_per_member_mod_entries_in_exps) > 0) {
        file_per_member_mod_var_matching_entries <- unlist(file_per_member_exps, recursive = FALSE)[intersect(var_entries_in_file_per_member_exps, file_per_member_mod_entries_in_exps)]
        file_per_member_exps_levels <- lapply(as.list(1:4), f <- function(x) {x <- array(x, length(file_per_member_exps[[x]]))})
        file_per_member_mod_var_matching_entries_levels <- unlist(file_per_member_exps_levels)[intersect(var_entries_in_file_per_member_exps, file_per_member_mod_entries_in_exps)]
      }
    }

    if (positiveMax(file_per_member_mod_var_matching_entries_levels) > positiveMax(mod_var_matching_entries_levels)) {
      is_file_per_member_exp[jmod] <- TRUE
      mod_var_matching_entries <- file_per_member_mod_var_matching_entries
    }

    if (length(mod_var_matching_entries) == 0) {
      stop(paste('Error: There are no matching entries in the configuration file for the experiment', mod, 'and the variable', var, 
                 '. Please check the configuration file.)'))
    } else {
      exp_info <- c(exp_info, list(getDatasetInfo(mod_var_matching_entries)))
    }

    jmod <- jmod + 1
  }

  obs_info <- list()
  is_file_per_member_obs <- is_file_per_dataset_obs <- rep(FALSE, length(obs))
  jobs <- 1
  for (ref in obs) {
    ref_var_matching_entries <- ref_var_matching_entries_levels <- c()
    file_per_member_ref_var_matching_entries <- file_per_member_ref_var_matching_entries_levels <- c()
    file_per_dataset_ref_var_matching_entries <- file_per_dataset_ref_var_matching_entries_levels <- c()
    
    if (length(unlist(observations, recursive = FALSE)) > 0) {
      ref_entries_in_obs <- which(unlist(lapply(lapply(unlist(lapply(observations, lapply, "[[", 1), recursive = FALSE), regexpr, ref), .IsFullMatch, ref)))
      if (length(ref_entries_in_obs) > 0) {
        ref_var_matching_entries <- unlist(observations, recursive = FALSE)[intersect(var_entries_in_obs, ref_entries_in_obs)]
        obs_levels <- lapply(as.list(1:4), f <- function(x) {x <- array(x, length(observations[[x]]))})
        ref_var_matching_entries_levels <- unlist(obs_levels)[intersect(var_entries_in_obs, ref_entries_in_obs)]
      }
    }

    if (length(unlist(file_per_member_obs, recursive = FALSE)) > 0) {
      file_per_member_ref_entries_in_obs <- which(unlist(lapply(lapply(unlist(lapply(file_per_member_obs, lapply, "[[", 1), recursive = FALSE), regexpr, ref), .IsFullMatch, ref)))
      if (length(file_per_member_ref_entries_in_obs) > 0) {
        file_per_member_ref_var_matching_entries <- unlist(file_per_member_obs, recursive = FALSE)[intersect(var_entries_in_file_per_member_obs, file_per_member_ref_entries_in_obs)]
        file_per_member_obs_levels <- lapply(as.list(1:4), f <- function(x) {x <- array(x, length(file_per_member_obs[[x]]))})
        file_per_member_ref_var_matching_entries_levels <- unlist(file_per_member_obs_levels)[intersect(var_entries_in_file_per_member_obs, file_per_member_ref_entries_in_obs)]
      }
    }

    if (length(unlist(file_per_dataset_obs, recursive = FALSE)) > 0) {
      file_per_dataset_ref_entries_in_obs <- which(unlist(lapply(lapply(unlist(lapply(file_per_dataset_obs, lapply, "[[", 1), recursive = FALSE), regexpr, ref), .IsFullMatch, ref)))
      if (length(ref_entries_in_obs) > 0) {
        file_per_dataset_ref_var_matching_entries <- unlist(file_per_dataset_obs, recursive = FALSE)[intersect(var_entries_in_file_per_dataset_obs, file_per_dataset_ref_entries_in_obs)]
        file_per_dataset_obs_levels <- lapply(as.list(1:4), f <- function(x) {x <- array(x, length(file_per_dataset_obs[[x]]))})
        file_per_dataset_ref_var_matching_entries_levels <- unlist(file_per_dataset_obs_levels)[intersect(var_entries_in_file_per_dataset_obs, file_per_dataset_ref_entries_in_obs)]
      }
    }

    if (positiveMax(file_per_member_ref_var_matching_entries_levels) > 
        max(positiveMax(ref_var_matching_entries_levels), positiveMax(file_per_dataset_ref_var_matching_entries_levels))) {
      is_file_per_member_obs[jobs] <- TRUE
      ref_var_matching_entries <- file_per_member_ref_var_matching_entries
    } else if (positiveMax(file_per_dataset_ref_var_matching_entries_levels) > 
        max(positiveMax(ref_var_matching_entries_levels), positiveMax(file_per_member_ref_var_matching_entries_levels))) {
      is_file_per_dataset_obs[jobs] <- TRUE
      ref_var_matching_entries <- file_per_dataset_ref_var_matching_entries
    }

    if (length(ref_var_matching_entries) == 0) {
      stop(paste('Error: There are no matching entries in the configuration file for the observation', ref, 'and the variable', var, 
                 '. Please check the configuration file.)'))
    } else {
      obs_info <- c(obs_info, list(getDatasetInfo(ref_var_matching_entries)))
    }

    jobs <- jobs + 1
  }
  
  if (!silent) {
    print("* Configuration file read successfully. All pairs (var, exp) and (var, obs) have matching entries.")
  }

  })
}
