source("incl/start.R")
library("listenv")
oopts <- c(oopts, options(
  future.debug = FALSE
))

message("*** Globals inside local() environments ...")

for (strategy in supportedStrategies()) {
  message(sprintf("- plan('%s') ...", strategy))
  plan(strategy)

  message("- Missing global variable")
  g <- function() a
  h <- function() {
    a <- 1
    g()
  }
  res <- tryCatch({
    f <- future(h())
    v <- value(f)
  }, error = identity)
  stopifnot(inherits(res, "error"))

  message("- Missing global variable (v2)")
  g <- function() a
  h <- function(g) {
    a <- 1
    g()
  }
  res <- tryCatch({
    f <- future(h(g))
    v <- value(f)
  }, error = identity)
  stopifnot(inherits(res, "error"))

  message("- Missing global variable (v3)")
  g <- function() a
  res <- tryCatch({
    f <- future(local({
      a <- 1
      g()
    }))
    v <- value(f)
  }, error = identity)
  stopifnot(inherits(res, "error"))

  message("- Non-missing global variable")
  a <- 2
  g <- function() a
  f <- future(local({
    a <- 1
    g()
  }), lazy = TRUE)
  rm(list = "a")

  if (isTRUE(as.logical(Sys.getenv("R_CHECK_IDEAL")))) {
    v <- value(f)
    stopifnot(v == 2)
  } else {
    if (packageVersion("future") > "1.25.0-9017") {
      v <- value(f)
      stopifnot(v == 2)
    } else {
      res <- tryCatch(v <- value(f), error = identity)
      print(res)
      stopifnot(inherits(res, "error"))
    }
  }


  message("- Non-missing global variable (inside local())")
  ## FIXME: This works and fails in non-expected ways /HB 2022-05-26
  local({
    a <- 2
    g <- function() a
    f <- future(local({
      a <- 1
      g()
    }), lazy = TRUE)
    rm(list = "a")

    truth <- 2
    message("truth: ", truth)
    
    res <- tryCatch({ v <- value(f) }, error = identity)
    print(res)
    
    if (isTRUE(as.logical(Sys.getenv("R_CHECK_IDEAL")))) {
      stopifnot(identical(v, truth))
    } else if (isTRUE(getOption("future.globals.keepWhere", FALSE))) {
      if (isTRUE(getOption("future.globals.globalsOf.locals", TRUE))) {
        if (strategy %in% c("sequential", "multicore")) {
          stopifnot(inherits(res, "error"))
        } else {
          stopifnot(identical(v, truth))
        }
      } else {
        stopifnot(identical(v, truth))
      }
    } else {
      stopifnot(identical(v, truth))
    }
  })


  message("- Name clashing of globals across local() environments")

  ## Closures with local globals of the same name
  g <- local({ a <- 2; function() a })
  h <- local({ a <- 1; function() a })
  truth <- g() + h()
  message("truth: ", truth)

  ## FIXME: This works and fails in non-expected ways /HB 2022-05-26
  ## With:
  ##
  ##   f <- future(g() + h())
  ##
  ## there is a risk that global 'a' of g() overwrites global 'a' of h(),
  ## resulting in h() == 2, rather than h() == 1. Vice versa, with:
  ##
  ##   f <- future(h() + g())
  ##
  ## 'a' of h() might overwride 'a' of g() so that g() == 1.
  ## This has to be fixed.
  ##
  ## See also https://github.com/HenrikBengtsson/future/issues/608
  
  f <- future(g() + h())
  v <- tryCatch(value(f), error = identity)

  utils::str(list(strategy = strategy, v = v))
  
  if (isTRUE(as.logical(Sys.getenv("R_CHECK_IDEAL")))) {
    stopifnot(identical(v, truth))
  } else if (isTRUE(getOption("future.globals.keepWhere", FALSE))) {
    if (isTRUE(getOption("future.globals.globalsOf.locals", TRUE))) {
      stopifnot(identical(v, truth))
    } else {
      stopifnot(identical(v, truth))
    }
  } else {
    if (isTRUE(getOption("future.globals.globalsOf.locals", TRUE))) {
      if (strategy %in% c("sequential", "multicore")) {
        stopifnot(identical(v, 4))    ## <= SERIOUS BUG!
      } else {
        stopifnot(identical(v, truth))
      }
    } else {
      if (strategy %in% c("sequential", "multicore")) {
        stopifnot(inherits(v, "error"))
      } else {
        stopifnot(identical(v, truth))
      }
    }
  }
} ## for (strategy ...)

message("*** Globals inside local() environments ... DONE")

source("incl/end.R")
