#' Install a local development package.
#'
#' Uses \code{R CMD INSTALL} to install the package. Will also try to install
#' dependencies of the package from CRAN, if they're not already installed.
#'
#' By default, installation takes place using the current package directory.
#' If you have compiled code, this means that artefacts of compilation will be
#' created in the \code{src/} directory. If you want to avoid this, you can
#' use \code{local = FALSE} to first build a package bundle and then install
#' it from a temporary directory. This is slower, but keeps the source
#' directory pristine.
#'
#' If the package is loaded, it will be reloaded after installation. This is
#' not always completely possible, see \code{\link{reload}} for caveats.
#'
#' To install a package in a non-default library, use \code{\link[withr]{with_libpaths}}.
#'
#' @param pkg package description, can be path or package name.  See
#'   \code{\link{as.package}} for more information
#' @param reload if \code{TRUE} (the default), will automatically reload the
#'   package after installing.
#' @param quick if \code{TRUE} skips docs, multiple-architectures,
#'   demos, and vignettes, to make installation as fast as possible.
#' @param local if \code{FALSE} \code{\link{build}}s the package first:
#'   this ensures that the installation is completely clean, and prevents any
#'   binary artefacts (like \file{.o}, \code{.so}) from appearing in your local
#'   package directory, but is considerably slower, because every compile has
#'   to start from scratch.
#' @param args An optional character vector of additional command line
#'   arguments to be passed to \code{R CMD install}. This defaults to the
#'   value of the option \code{"devtools.install.args"}.
#' @param quiet if \code{TRUE} suppresses output from this function.
#' @param dependencies \code{logical} indicating to also install uninstalled
#'   packages which this \code{pkg} depends on/links to/suggests. See
#'   argument \code{dependencies} of \code{\link{install.packages}}.
#' @param upgrade_dependencies If \code{TRUE}, the default, will also update
#'   any out of date dependencies.
#' @param build_vignettes if \code{TRUE}, will build vignettes. Normally it is
#'   \code{build} that's responsible for creating vignettes; this argument makes
#'   sure vignettes are built even if a build never happens (i.e. because
#'   \code{local = TRUE}).
#' @param keep_source If \code{TRUE} will keep the srcrefs from an installed
#'   package. This is useful for debugging (especially inside of RStudio).
#'   It defaults to the option \code{"keep.source.pkgs"}.
#' @param threads number of concurrent threads to use for installing
#'   dependencies.
#'   It defaults to the option \code{"Ncpus"} or \code{1} if unset.
#' @param force_deps whether to force installation of dependencies even if their
#'   SHA1 reference hasn't changed from the currently installed version.
#' @param metadata Named list of metadata entries to be added to the
#'   \code{DESCRIPTION} after installation.
#' @param out_dir Directory to store installation output in case of failure.
#' @param skip_if_log_exists If the \code{out_dir} is defined and contains
#'   a file named \code{package.out}, no installation is attempted.
#' @param ... additional arguments passed to \code{\link{install.packages}}
#'   when installing dependencies. \code{pkg} is installed with
#'   \code{R CMD INSTALL}.
#' @export
#' @family package installation
#' @seealso \code{\link{with_debug}} to install packages with debugging flags
#'   set.
install <-
  function(pkg = ".", reload = TRUE, quick = FALSE, local = TRUE,
           args = getOption("devtools.install.args"), quiet = FALSE,
           dependencies = NA, upgrade_dependencies = TRUE,
           build_vignettes = FALSE,
           keep_source = getOption("keep.source.pkgs"),
           threads = getOption("Ncpus", 1),
           force_deps = FALSE,
           metadata = remote_metadata(as.package(pkg)),
           out_dir = NULL,
           skip_if_log_exists = FALSE,
           ...) {

  pkg <- as.package(pkg)
  check_build_tools(pkg)

  # Forcing all of the promises for the current namespace now will avoid lazy-load
  # errors when the new package is installed overtop the old one.
  # https://stat.ethz.ch/pipermail/r-devel/2015-December/072150.html
  if (is_loaded(pkg)) {
    eapply(ns_env(pkg), force, all.names = TRUE)
  }

  root_install <- is.null(installing$packages)
  if (root_install) {
    on.exit(installing$packages <- NULL, add = TRUE)
  }

  if (pkg$package %in% installing$packages) {
    if (!quiet) {
      message("Skipping ", pkg$package, ", it is already being installed.")
    }
    return(invisible(FALSE))
  }

  if (!is.null(out_dir)) {
    out_file <- file.path(out_dir, paste0(pkg$package, ".out"))
    if (skip_if_log_exists && file.exists(out_file)) {
      message("Skipping ", pkg$package, ", installation failed before, see log in ", out_file)
      return(invisible(FALSE))
    }
  } else {
    out_file <- NULL
  }

  installing$packages <- c(installing$packages, pkg$package)
  if (!quiet) {
    message("Installing ", pkg$package)
  }

  # If building vignettes, make sure we have all suggested packages too.
  if (build_vignettes && missing(dependencies)) {
    dependencies <- standardise_dep(TRUE)
  } else {
    dependencies <- standardise_dep(dependencies)
  }

  initial_deps <- dependencies[dependencies != "Suggests"]
  final_deps <- dependencies[dependencies == "Suggests"]

  # cache the Remote: dependencies here so we don't have to query them each
  # time we call install_deps
  installing$remote_deps <- remote_deps(pkg)
  on.exit(installing$remote_deps <- NULL, add = TRUE)

  install_deps(pkg, dependencies = initial_deps, upgrade = upgrade_dependencies,
    threads = threads, force_deps = force_deps, quiet = quiet, ...,
    out_dir = out_dir, skip_if_log_exists = skip_if_log_exists)

  # Build the package. Only build locally if it doesn't have vignettes
  has_vignettes <- length(tools::pkgVignettes(dir = pkg$path)$docs > 0)
  if (local && !(has_vignettes && build_vignettes)) {
    built_path <- pkg$path
  } else {
    built_path <- build(pkg, tempdir(), vignettes = build_vignettes, quiet = quiet)
    on.exit(unlink(built_path), add = TRUE)
  }

  opts <- c(
    paste("--library=", shQuote(.libPaths()[1]), sep = ""),
    if (keep_source) "--with-keep.source",
    "--install-tests"
  )
  if (quick) {
    opts <- c(opts, "--no-docs", "--no-multiarch", "--no-demo")
  }
  opts <- paste(paste(opts, collapse = " "), paste(args, collapse = " "))

  built_path <- normalizePath(built_path, winslash = "/")
  R(paste("CMD INSTALL ", shQuote(built_path), " ", opts, sep = ""),
    fun = system2_check,
    quiet = quiet || !is.null(out_file), out_file = out_file)

  # Remove immediately upon success
  unlink(out_file)

  install_deps(pkg, dependencies = final_deps, upgrade = upgrade_dependencies,
    threads = threads, force_deps = force_deps, quiet = quiet, ...,
    out_dir = out_dir, skip_if_log_exists = skip_if_log_exists)

  if (length(metadata) > 0) {
    add_metadata(inst(pkg$package), metadata)
  }

  if (reload) {
    reload(pkg, quiet = quiet)
  }
  invisible(TRUE)
}

# A environment to hold which packages are being installed so packages with
# circular dependencies can be skipped the second time.
installing <- new.env(parent = emptyenv())

#' Install package dependencies if needed.
#'
#' \code{install_deps} is used by \code{install_*} to make sure you have
#' all the dependencies for a package. \code{install_dev_deps()} is useful
#' if you have a source version of the package and want to be able to
#' develop with it: it installs all dependencies of the package, and it
#' also installs roxygen2.
#'
#' @inheritParams install
#' @inheritParams package_deps
#' @param ... additional arguments passed to \code{\link{install.packages}}.
#' @export
#' @examples
#' \dontrun{install_deps(".")}
install_deps <- function(pkg = ".", dependencies = NA,
                         threads = getOption("Ncpus", 1),
                         repos = getOption("repos"),
                         type = getOption("pkgType"),
                         ...,
                         upgrade = TRUE,
                         quiet = FALSE,
                         force_deps = FALSE) {

  pkg <- dev_package_deps(pkg, repos = repos, dependencies = dependencies,
    type = type)
  update(pkg, ..., Ncpus = threads, quiet = quiet, upgrade = upgrade)
  invisible()
}

#' @rdname install_deps
#' @export
install_dev_deps <- function(pkg = ".", ...) {
  update_packages("roxygen2")
  install_deps(pkg, ..., dependencies = TRUE, upgrade = FALSE,
    bioc_packages = TRUE)
}
