## Function to tell if a regexpr() match is a complete match to a specified name
.IsFullMatch <- function(x, name) {
  ifelse(x > 0 && attributes(x)$match.length == nchar(name), TRUE, FALSE)
}

.ConfigReplaceVariablesInString <- function(string, replace_values, allow_undefined_key_vars = FALSE) {
  # This function replaces all the occurrences of a variable in a string by 
  # their corresponding string stored in the 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, .ConfigReplaceVariablesInString(replace_values[[part]], replace_values, allow_undefined_key_vars), sep = "")
        } else if (allow_undefined_key_vars && (part %in% c("STORE_FREQ", "START_DATE", "MEMBER_NUMBER", "YEAR", "MONTH"))) {
          output <- paste0(output, "$", part, "$")
        } else {
          stop(paste('Error: The variable $', part, '$ was not defined in the configuration file.', sep = ''))
        }
      }
      i <- i + 1
    }
    output
  } else {
    string
  }
}

.LoadDataFile <- function(work_piece, explore_dims = FALSE, silent = FALSE) {
  #suppressPackageStartupMessages({library(ncdf4)})
  #suppressPackageStartupMessages({library(bigmemory)})
  #suppressPackageStartupMessages({library(plyr)})

  arrayIndex2VectorIndex <- function(indices, dims) {
    if (length(indices) > length(dims)) {
      stop("Error: indices do not match dimensions in arrayIndex2VectorIndex.")
    }
    position <- 1
    dims <- rev(dims)
    indices <- rev(indices)
    for (i in 1:length(indices)) {
      position <- position + (indices[i] - 1) * prod(dims[-c(1:i)])
    }
    position
  }

  if ((work_piece[['is_2d_var']] || work_piece[['is_file_per_dataset']]) && (Sys.which("cdo")[[1]] == "")) {
    stop("Error: CDO libraries not available")
  }

  file_found <- FALSE
  dims <- NULL

  filename <- work_piece[['filename']]
  namevar <- work_piece[['namevar']]
  output <- work_piece[['output']]
  # The names of all data files in the directory of the repository that match 
  # the pattern are obtained.
  if (length(grep("^http", filename)) > 0) {
    is_url <- TRUE
    files <- filename
    ## TODO: Check that the user is not using shell globbing exps.
  } else {
    is_url <- FALSE
    files <- Sys.glob(filename)
  }

  # If we don't find any, we leave the flag 'file_found' with a false value.
  if (length(files) > 0) {
    # The first file that matches the pattern is chosen and read.
    filename <- files[length(files)]
    filein <- filename
    file_found <- TRUE

    if (!silent) {
      if (explore_dims) {
        cat(paste("* Exploring dimensions...", filename, '\n'))
      }
      ##} else {
      ##  cat(paste("* Reading & processing data...", filename, '\n'))
      ##}
    }

    # We will fill in 'expected_dims' with the names of the expected dimensions of
    # the data array we'll retrieve from the file.
    expected_dims <- NULL
    remap_needed <- FALSE
    fnc <- nc_open(filein)
    if (!(namevar %in% names(fnc$var))) {
      stop("Error: The variable", namevar, "is not defined in the file", filename)
    }
    if (work_piece[['is_2d_var']]) {
      if (!is.null(work_piece[['grid']])) {
        # Here we read the grid type and its number of longitudes and latitudes
        file_info <- system(paste('cdo griddes', filein, '2> /dev/null'), intern = TRUE)
        grids_info <- strsplit(paste(file_info, collapse = ' '), '# gridID')[[1]][-1]
        gaussian_matches <- grep('gaussian', grids_info)
        lonlat_matches <- grep('lonlat', grids_info)
        if (length(gaussian_matches) == 0 && length(lonlat_matches) == 0) {
          stop("Error: Only 'gaussian' and 'lonlat' grids supported. See e.g: cdo sinfo ", filename)
        }
        if (length(gaussian_matches) + length(lonlat_matches) > 1) {
          stop("Error: Load() can't disambiguate: More than one lonlat/gaussian grid defined in ", filename)
        }
        if (length(gaussian_matches) > 0) {
          grid_index <- gaussian_matches[1]
          grid_type <- 'gaussian'
        } else {
          grid_index <- lonlat_matches[1]
          grid_type <- 'lonlat'
        }
        grid_lons <- as.integer(strsplit(strsplit(grids_info[grid_index], 'xsize *= *')[[1]][2], ' ')[[1]][1])
        grid_lats <- as.integer(strsplit(strsplit(grids_info[grid_index], 'ysize *= *')[[1]][2], ' ')[[1]][1])

        # Now we calculate the expected grid type and its lons and lats
        if (length(grep('^t\\d{1,+}grid$', work_piece[['grid']])) > 0) {
          expected_grid_type <- 'gaussian'
          expected_grid_res <- as.integer(strsplit(work_piece[['grid']], '[^0-9]{1,+}')[[1]][2])
          ## As seen in cdo's griddes.c: ntr2nlat()
          expected_grid_lats <- (expected_grid_res * 2 + 1) / 2
          if ((expected_grid_lats > 0) && (expected_grid_lats - trunc(expected_grid_lats) >= 0.5)) {
            expected_grid_lats <- ceiling(expected_grid_lats)
          } else {
            expected_grid_lats <- round(expected_grid_lats)
          }
          if (expected_grid_lats %% 2 > 0) {
            expected_grid_lats <- expected_grid_lats + 1
          }
          ## As seen in cdo's griddes.c: compNlon(), and as specified in ECMWF
          expected_grid_lons <- 2*expected_grid_lats
          keep_going <- TRUE
          while (keep_going) {
            n <- expected_grid_lons
            if (n %% 8 == 0) n <- trunc(n / 8)
            while (n %% 6 == 0) n <- trunc(n / 6)
            while (n %% 5 == 0) n <- trunc(n / 5)
            while (n %% 4 == 0) n <- trunc(n / 4)
            while (n %% 3 == 0) n <- trunc(n / 3)
            if (n %% 2 == 0) n <- trunc(n / 2)
            
            if (n <= 8) {
              keep_going <- FALSE
            } else {
              expected_grid_lons <- expected_grid_lons + 2
              if (expected_grid_lons > 9999) {
                stop("Error: pick another gaussian grid truncation. It doesn't fulfill the standards to apply FFT.")
              }
            }
          }
        } else if (length(grep('^r\\d{1,+}x\\d{1,+}$', work_piece[['grid']])) > 0) {
          expected_grid_type <- 'lonlat'
          expected_grid_lons <- as.integer(strsplit(work_piece[['grid']], '[^0-9]{1,+}')[[1]][2])
          expected_grid_lats <- as.integer(strsplit(work_piece[['grid']], '[^0-9]{1,+}')[[1]][3])
        } else {
          stop("Error: Only supported grid types in parameter 'grid' are t<RES>grid and r<NX>x<NY>")
        }
        if (!(grid_lons == expected_grid_lons && grid_lats == expected_grid_lats &&
            grid_type == expected_grid_type)) {
          remap_needed <- TRUE
        }
      }
      
      lonmin <- work_piece[['lon_limits']][1]
      lonmax <- work_piece[['lon_limits']][2]
      latmin <- work_piece[['lat_limits']][1]
      latmax <- work_piece[['lat_limits']][2]
      if (lonmin == 0 && lonmax == 360 && latmin == -90 && latmax == 90) {
        lonlat_subsetting_requested <- FALSE
      } else {
        lonlat_subsetting_requested <- TRUE
      }
      if (!lonlat_subsetting_requested && remap_needed) {
        nc_close(fnc)
        filecopy <- tempfile(pattern = "load", fileext = ".nc")
        file.copy(filein, filecopy)
        filein <- tempfile(pattern = "loadRegridded", fileext = ".nc")
        system(paste0("cdo ", work_piece[['remap']], ",", work_piece[['grid']], 
                      " -selname,", namevar, " ", filecopy, " ", filein, 
                      " 2>/dev/null", sep = ""))
        file.remove(filecopy)
        work_piece[['dimnames']][['longitudes']] <- 'lon'
        work_piece[['dimnames']][['latitudes']] <- 'lat'
        fnc <- nc_open(filein)
      }
      expected_dims <- c(work_piece[['dimnames']][['longitudes']],
                         work_piece[['dimnames']][['latitudes']])
    }
    var_dimnames <- unlist(lapply(fnc$var[[namevar]][['dim']], '[[', 'name'))
    nmemb <- nltime <- NULL
    ## Sometimes CDO renames 'members' dimension to 'lev'
    old_members_dimname <- NULL
    if (('lev' %in% var_dimnames) && !(work_piece[['dimnames']][['members']] %in% var_dimnames)) {
      old_members_dimname <- work_piece[['dimnames']][['members']]
      work_piece[['dimnames']][['members']] <- 'lev'
    }
    if (work_piece[['dimnames']][['members']] %in% var_dimnames) {
      nmemb <- fnc$var[[namevar]][['dim']][[match(work_piece[['dimnames']][['members']], var_dimnames)]]$len
      expected_dims <- c(expected_dims, work_piece[['dimnames']][['members']])
    } else {
      nmemb <- 1
    }
    if (length(expected_dims) > 0) {
      dim_matches <- match(expected_dims, var_dimnames)
      if (any(is.na(dim_matches))) {
        if (!is.null(old_members_dimname)) {
          expected_dims[which(expected_dims == 'lev')] <- old_members_dimname
        }
        stop(paste("Error: the expected dimension(s)", 
                   paste(expected_dims[which(is.na(dim_matches))], collapse = ', '), 
                   "were not found in", filename))
      }
      time_dimname <- var_dimnames[-dim_matches]
    } else {
      time_dimname <- var_dimnames
    }
    if (length(time_dimname) > 0) {
      if (length(time_dimname) == 1) {
        nltime <- fnc$var[[namevar]][['dim']][[match(time_dimname, var_dimnames)]]$len
        expected_dims <- c(expected_dims, time_dimname)
        dim_matches <- match(expected_dims, var_dimnames)
      } else {
        if (!is.null(old_members_dimname)) {
          expected_dims[which(expected_dims == 'lev')] <- old_members_dimname
        }
        stop(paste("Error: the variable", namevar, 
                   "is defined over more dimensions than the expected (", 
                   paste(c(expected_dims, 'time'), collapse = ', '), 
                   "). It could also be that the members dimension is named incorrectly (in that case, either rename the dimension in the file or adjust Load() to recognize this name with the parameter 'dimnames'). See file", filename))
      }
    } else {
      nltime <- 1
    }

    if (work_piece[['is_2d_var']]) {
      ## We read the longitudes and latitudes from the file.
      lon <- ncvar_get(fnc, work_piece[['dimnames']][['longitudes']])
      lon[which(lon < 0)] <- lon[which(lon < 0)] + 360
      lon_indices <- 1:length(lon)
      lon_indices <- lon_indices[which((lon >= lonmin & lon <= lonmax) | 
                     (lonmax < lonmin & (lon >= 0 | lon <= 360)))]
      lon <- lon[lon_indices]
      lat <- ncvar_get(fnc, work_piece[['dimnames']][['latitudes']])
      lat_indices <- 1:length(lat)
      if (lat[1] < lat[length(lat)]) {
        lat_indices <- length(lat):1
        lat <- lat[lat_indices]
      }
      lat_indices <- lat_indices[which(lat >= latmin & lat <= latmax)]
      lat <- lat[which(lat >= latmin & lat <= latmax)]
      if (is.null(work_piece[['grid']])) {
        expected_grid_lons <- length(lon)
        expected_grid_lats <- length(lat)
      }
    } else {
      lon <- 0
      lat <- 0
    }

    if (explore_dims) {
      # If we're exploring the file we only want one time step, to 
      # regrid it and work out the number of longitudes and latitudes.
      members <- 1
      ltimes_list <- list(c(1))
    } else {
      # The data is arranged in the array 'tmp' with the dimensions in a 
      # common order:
      #   1) Longitudes 
      #   2) Latitudes
      #   3) Members (even if is not a file per member experiment)
      #   4) Lead-times
      if (work_piece[['is_file_per_dataset']]) {
        time_indices <- 1:nltime
        mons <- strsplit(system(paste('cdo showmon ', filein, 
                         ' 2>/dev/null'), intern = TRUE), split = ' ')
        years <- strsplit(system(paste('cdo showyear ', filein, 
                          ' 2>/dev/null'), intern = TRUE), split = ' ')
        mons <- as.integer(mons[[1]][which(mons[[1]] != "")])
        years <- as.integer(years[[1]][which(years[[1]] != "")])
        time_indices <- ts(time_indices, start = c(years[1], mons[1]), 
                           end = c(years[length(years)], mons[length(mons)]),
                           frequency = 12)
        ltimes_list <- list()
        for (sdate in work_piece[['startdates']]) {
          selected_time_indices <- window(time_indices, start = c(as.integer(
                                   substr(sdate, 1, 4)), as.integer(substr(sdate, 5, 6))), 
                                   end = c(3000, 12), frequency = 12, extend = TRUE)
          ltimes_list <- c(ltimes_list, list(selected_time_indices[work_piece[['leadtimes']]]))
        }
      } else {
        ltimes <- work_piece[['leadtimes']]
        #if (work_piece[['dataset_type']] == 'exp') {
          ltimes_list <- list(ltimes[which(ltimes <= nltime)])
        #}
      }
      ## TODO: Put, when reading matrices, this kind of warnings
      #  if (nmember < nmemb) {
      #    cat("Warning:
      members <- 1:work_piece[['nmember']]
      members <- members[which(members <= nmemb)]
    }

    found_disordered_dims <- FALSE
    for (ltimes in ltimes_list) {
      if (work_piece[['is_2d_var']]) {
        start <- c(min(lon_indices), min(lat_indices))
        end <- c(max(lon_indices), max(lat_indices))
        if (remap_needed) {
          subset_indices <- list(min(lon_indices):max(lon_indices) - min(lon_indices) + 1,
                                 lat_indices - min(lat_indices) + 1)
          dim_longitudes <- ncdim_def(work_piece[['dimnames']][['longitudes']], "degrees_east", lon)
          dim_latitudes <- ncdim_def(work_piece[['dimnames']][['latitudes']], "degrees_north", lat)
          ncdf_dims <- list(dim_longitudes, dim_latitudes)
        } else {
          subset_indices <- list(lon_indices - min(lon_indices) + 1,
                                 lat_indices - min(lat_indices) + 1)
          ncdf_dims <- list()
        }
        final_dims <- c(length(subset_indices[[1]]), length(subset_indices[[2]]), 1, 1)
      } else {
        start <- end <- c()
        subset_indices <- list()
        ncdf_dims <- list()
        final_dims <- c(1, 1, 1, 1)
      }
        
      if (work_piece[['dimnames']][['members']] %in% expected_dims) {
        start <- c(start, head(members, 1))
        end <- c(end, tail(members, 1))
        subset_indices <- c(subset_indices, list(members - head(members, 1) + 1))
        dim_members <- ncdim_def(work_piece[['dimnames']][['members']], "", members)
        ncdf_dims <- c(ncdf_dims, list(dim_members))
        final_dims[3] <- length(members)
      }
      if ((length(time_dimname) == 1) && (time_dimname %in% expected_dims)) {
        start <- c(start, head(ltimes, 1))
        end <- c(end, tail(ltimes, 1))
        subset_indices <- c(subset_indices, list(ltimes - head(ltimes, 1) + 1))
        dim_time <- ncdim_def(time_dimname, "", 1:length(ltimes), unlim = TRUE)
        ncdf_dims <- c(ncdf_dims, list(dim_time))
        final_dims[4] <- length(ltimes)
      }
      count <- end - start + 1
      start <- start[dim_matches]
      count <- count[dim_matches]
      subset_indices <- subset_indices[dim_matches]
      tmp <- take(ncvar_get(fnc, namevar, start, count, 
                  collapse_degen = FALSE), 
                  1:length(subset_indices), subset_indices)
      if (!all(dim_matches == sort(dim_matches))) {
        if (!found_disordered_dims && rev(work_piece[['indices']])[2] == 1 && rev(work_piece[['indices']])[3] == 1) {
          found_disordered_dims <- TRUE
          cat(paste0("* Warning: the dimensions for the variable ", namevar, " in the files of the experiment with index ", tail(work_piece[['indices']], 1), " are not in the optimal order for loading with Load(). The optimal order would be '", paste(expected_dims, collapse = ', '), "'. One of the files of the experiment is stored in ", filename))
        }
        tmp <- aperm(tmp, dim_matches)
      }
      dim(tmp) <- final_dims

      # The data is regridded if it corresponds to an atmospheric variable. When
      # the chosen output type is 'areave' the data is not regridded to not 
      # waste computing time unless the user specified a common grid.
      if (work_piece[['is_2d_var']]) {
        if (lonlat_subsetting_requested && remap_needed) {
          filein <- tempfile(pattern = "loadRegridded", fileext = ".nc")
          filein2 <- tempfile(pattern = "loadRegridded2", fileext = ".nc")
          nc_close(fnc)
          ncdf_var <- ncvar_def(namevar, "", ncdf_dims)
          fnc <- nc_create(filein2, list(ncdf_var))
          ncvar_put(fnc, ncdf_var, tmp)
          nc_close(fnc)
          system(paste0("cdo -sellonlatbox,", lonmin, ",", lonmax, ",", latmin, ",", latmax, 
                 " -", work_piece[['remap']], ",", work_piece[['grid']], 
                 " -selname,", namevar, " ", filein2, " ", filein, 
                 " 2>/dev/null", sep = ""))
          file.remove(filein2)
          fnc <- nc_open(filein)
          ## We read the longitudes and latitudes from the file.
          lon <- ncvar_get(fnc, 'lon')
          lon[which(lon < 0)] <- lon[which(lon < 0)] + 360
          lon_indices <- 1:length(lon)
          lon_indices <- lon_indices[which((lon >= lonmin & lon <= lonmax) | 
                         (lonmax < lonmin & (lon >= lonmin | lon <= lonmax)))]
          lon <- lon[lon_indices]
          lat <- ncvar_get(fnc, 'lat')
          lat_indices <- 1:length(lat)
          if (lat[1] < lat[length(lat)]) {
            lat_indices <- length(lat):1
            lat <- lat[lat_indices]
          }
          lat_indices <- lat_indices[which(lat >= latmin & lat <= latmax)]
          lat <- lat[which(lat >= latmin & lat <= latmax)]
          final_dims[c(1, 2)] <- c(length(lon_indices), length(lat_indices))

          tmp <- take(ncvar_get(fnc, namevar), c(1, 2), list(lon_indices, lat_indices))
          dim(tmp) <- final_dims
        }

        if (!is.null(work_piece[['mask']])) {
          if (!identical(dim(work_piece[['mask']]), c(expected_grid_lons, expected_grid_lats))) {
            stop(paste("Error: the mask of the dataset with index ", tail(work_piece[['indices']], 1), " in '", work_piece[['dataset_type']], "' is wrong. It must be on the common grid if the selected output type is 'lonlat', 'lon' or 'lat', or 'areave' and 'grid' has been specified. It must be on the grid of the corresponding dataset if the selected output type is 'areave' and no 'grid' has been specified. For more information check ?Load and see help on parameters 'grid', 'maskmod' and 'maskobs'.", sep = ""))
          }
        }
        # Each latitude of each grid point is associated to its center. When cdo crops a region, if the limits of the
        # defined region do not contain the center of a cell, it is not included in the result.
        #full_mask_lons <- dim(work_piece[['mask']])[1]
        #full_mask_lats <- dim(work_piece[['mask']])[2]
        #lons_mask <- seq(0 + 360 / full_mask_lons / 2, 360 - 360 / full_mask_lons / 2, length.out = full_mask_lons)
        #lats_mask <- seq(-90 + 180 / full_mask_lats / 2, 90 - 180 / full_mask_lats / 2, length.out = full_mask_lats)
        #the mask values are selected below with lon_indices and lat_indices. I think its fine.
        #The purpose is to make sure that the cropped mask matches the cropped tmp so that it doesnt crash below
      }

      if (explore_dims) {
        if (work_piece[['is_file_per_member']]) {
          ## TODO: When the exp_full_path contains asterisks and is file_per_member
          ##       members from different datasets may be accounted.
          ##       Also if one file member is missing the accounting will be wrong.
          ##       Should parse the file name and extract number of members.
          if (is_url) {
            nmemb <- NULL
          } else {
            nmemb <- length(files)
          }
        }
        dims <- list(nmemb = nmemb, nltime = nltime, lon = lon, lat = lat)
      } else {
        if (work_piece[['is_2d_var']]) {
          mask <- work_piece[['mask']][lon_indices, lat_indices]
          tmp <- apply(tmp, c(3, 4), function(x) {
            # Disable of large values.
            if (!is.na(work_piece[['var_limits']][2])) {
              x[which(x > work_piece[['var_limits']][2])] <- NA
            }
            if (!is.na(work_piece[['var_limits']][1])) {
              x[which(x < work_piece[['var_limits']][1])] <- NA
            }
            if (!is.null(mask)) {
              x[which(mask < 0.5)] <- NA
            }

            if (output == 'areave' || output == 'lon') {
              weights <- InsertDim(cos(lat * pi / 180), 1, length(lon))
              weights[which(is.na(x))] <- NA
              if (output == 'areave') {
                weights <- weights / mean(weights, na.rm = TRUE)
                mean(x * weights, na.rm = TRUE) 
              } else {
                weights <- weights / InsertDim(Mean1Dim(weights, 2, narm = TRUE), 2, length(lat))
                Mean1Dim(x * weights, 2, narm = TRUE)
              }
            } else if (output == 'lat') {
              Mean1Dim(x, 1, narm = TRUE)
            } else if (output == 'lonlat') {
              signif(x, 5)
            }
          })
          if (output == 'areave') {
            dim(tmp) <- c(1, 1, final_dims[3:4])
          } else if (output == 'lon') {
            dim(tmp) <- c(final_dims[1], 1, final_dims[3:4])
          } else if (output == 'lat') {
            dim(tmp) <- c(1, final_dims[c(2, 3, 4)])
          } else if (output == 'lonlat') {
            dim(tmp) <- final_dims
          }
        }

        var_data <- attach.big.matrix(work_piece[['out_pointer']])
        if (work_piece[['dims']][['nmember']] > 1 && nmemb > 1 && work_piece[['dims']][['nltime']] > 1 && nltime < work_piece[['dims']][['nltime']]) {
          work_piece[['indices']][2] <- work_piece[['indices']][2] - 1
          for (jmemb in 1:nmemb) {
            work_piece[['indices']][2] <- work_piece[['indices']][2] + 1
            out_position <- arrayIndex2VectorIndex(work_piece[['indices']], work_piece[['dims']])
            out_indices <- out_position:(out_position + length(tmp[, , jmemb, ]) - 1)
            var_data[out_indices] <- as.vector(tmp[, , jmemb, ])
          }
          work_piece[['indices']][2] <- work_piece[['indices']][2] - nmemb + 1
        } else {
          out_position <- arrayIndex2VectorIndex(work_piece[['indices']], work_piece[['dims']])
          out_indices <- out_position:(out_position + length(tmp) - 1)
          a <- aperm(tmp, c(1, 2, 4, 3))
          as.vector(a)
          var_data[out_indices] <- as.vector(aperm(tmp, c(1, 2, 4, 3)))
        }
        work_piece[['indices']][3] <- work_piece[['indices']][3] + 1
      }
    }

    nc_close(fnc)    
    if (work_piece[['is_2d_var']] && !is.null(work_piece[['grid']]) && remap_needed) {
      file.remove(filein)
    }
  }
  if (explore_dims) {
    dims
  } else {
    file_found
  }
}

.LoadSampleData <- function(var, exp = NULL, obs = NULL, sdates, 
                            nmember = NULL, nmemberobs = NULL, 
                            nleadtime = NULL, leadtimemin = 1, 
                            leadtimemax = NULL, storefreq = 'monthly', 
                            sampleperiod = 1, lonmin = 0, lonmax = 360, 
                            latmin = -90, latmax = 90, output = 'areave', 
                            method = 'conservative', grid = NULL, 
                            maskmod = vector("list", 15), 
                            maskobs = vector("list", 15), 
                            configfile = NULL, suffixexp = NULL, 
                            suffixobs = NULL, varmin = NULL, varmax = NULL, 
                            silent = FALSE, nprocs = NULL) {
  ## This function loads and selects sample data stored in sampleMap and 
  ## sampleTimeSeries and is used in the examples instead of Load() so as
  ## to avoid nco and cdo system calls and computation time in the stage 
  ## of running examples in the CHECK process on CRAN.
  selected_start_dates <- match(sdates, c('19851101', '19901101', '19951101', 
                                          '20001101', '20051101'))
  start_dates_position <- 3
  lead_times_position <- 4

  if (output == 'lonlat') {
    sampleData <- s2dverification::sampleMap
    if (is.null(leadtimemax)) {
      leadtimemax <- dim(sampleData$mod)[lead_times_position]
    }
    selected_lead_times <- leadtimemin:leadtimemax

    dataOut <- sampleData
    dataOut$mod <- sampleData$mod[, , selected_start_dates, selected_lead_times, , ]
    dataOut$obs <- sampleData$obs[, , selected_start_dates, selected_lead_times, , ]
  }
  else if (output == 'areave') {
    sampleData <- s2dverification::sampleTimeSeries
    if (is.null(leadtimemax)) {
      leadtimemax <- dim(sampleData$mod)[lead_times_position]
    }
    selected_lead_times <- leadtimemin:leadtimemax

    dataOut <- sampleData
    dataOut$mod <- sampleData$mod[, , selected_start_dates, selected_lead_times]
    dataOut$obs <- sampleData$obs[, , selected_start_dates, selected_lead_times]
  }

  dims_out <- dim(sampleData$mod)
  dims_out[start_dates_position] <- length(selected_start_dates)
  dims_out[lead_times_position] <- length(selected_lead_times)
  dim(dataOut$mod) <- dims_out

  dims_out <- dim(sampleData$obs)
  dims_out[start_dates_position] <- length(selected_start_dates)
  dims_out[lead_times_position] <- length(selected_lead_times)
  dim(dataOut$obs) <- dims_out

  invisible(list(mod = dataOut$mod, obs = dataOut$obs, 
                 lat = dataOut$lat, lon = dataOut$lon))
}

.ConfigGetDatasetInfo <- function(matching_entries) {
  # This function obtains the information of a dataset and variable pair,
  # applying all the entries that match in the configuration file.
  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
}
