#' Alter the saving generic
#'
#' Allow alabaster applications to divert to a different saving generic instead of \code{\link{saveObject}}.
#'
#' @param x,path,... Further arguments to pass to \code{\link{saveObject}} or an equivalent generic.
#' @param generic Generic function that can serve as a drop-in replacement for \code{\link{saveObject}}. 
#'
#' @return 
#' For \code{altSaveObject}, files are created at the specified location, see \code{\link{saveObject}} for details.
#'
#' For \code{altSaveObjectFunction}, the alternative generic (if any) is returned if \code{generic} is missing.
#' If \code{generic} is provided, it is used to define the alternative, and the previous alternative is returned.
#'
#' @details
#' By default, \code{altSaveObject} is just a wrapper around \code{\link{saveObject}}.
#' However, if \code{altSaveObjectFunction} is called, \code{altSaveObject} calls the replacement \code{generic} instead.
#' This allows alabaster applications to inject wholesale or class-specific customizations into the saving process,
#' e.g., to save more metadata whenever an instance of a particular class is encountered.
#' Developers of alabaster extensions should use \code{altSaveObject} to save child objects when implementing \code{saveObject} methods,
#' to ensure that application-specific customizations are respected for the children.
#'
#' To motivate the use of \code{altSaveObject}, consider the following scenario.
#' \enumerate{
#' \item We have created a staging method for class X, defined for the \code{\link{saveObject}} generic.
#' \item An alabaster application Y requires the addition of some custom metadata during the staging process for X.
#' It defines an alternative staging generic \code{saveObject2} that, upon encountering an instance of X, redirects to an application-specific method (i.e., \code{saveObject2,X-method}).
#' For example, the \code{saveObject2} method for X could call X's \code{saveObject} method and add the necessary metadata to the result.
#' \item When operating in the context of application Y, the \code{saveObject2} generic is used to set \code{altSaveObjectFunction}.
#' Any calls to \code{altSaveObject} in Y's context will subsequently call \code{saveObject2}.
#' \item So, when writing a \code{saveObject} method for any objects that might contain an instance of X as a child, 
#' we call \code{\link{altSaveObject}} on that X object instead of directly using \code{\link{saveObject}}. 
#' This ensures that, if a child instance of X is encountered \emph{and} we are operating in the context of application Y, 
#' we correctly call \code{saveObject2} and then ultimately the application-specific method.
#' }
#'
#' The application-specific \code{generic} is free to do anything it wants as long as the custom representation is understood by the application-specific reader in \code{\link{altReadObject}}.
#' However, it is usually most convenient to re-use the existing representations created by \code{\link{saveObject}}.
#' This means that any customizations should not interfere with the validity of those representations, as defined by the \pkg{takane} specifications and enforced by \code{\link{validateObject}}.
#' We recommend that any customizations should manifest as new files starting with an underscore, as this will not interfere by any \pkg{takane} file specification.
#' 
#' @author Aaron Lun
#' @examples
#' old <- altSaveObjectFunction()
#'
#' # Creating a new generic for demonstration purposes:
#' setGeneric("superSaveObject", function(x, path, ...)
#'     standardGeneric("superSaveObject"))
#'
#' setMethod("superSaveObject", "ANY", function(x, path, ...) {
#'     print("Falling back to the base method!")
#'     saveObject(x, path, ...)
#' })
#' 
#' altSaveObjectFunction(superSaveObject)
#'
#' # Staging an example DataFrame. This should print our message.
#' library(S4Vectors)
#' df <- DataFrame(A=1:10, B=LETTERS[1:10])
#' tmp <- tempfile()
#' altSaveObject(df, tmp)
#'
#' # Restoring the old loader:
#' altSaveObjectFunction(old)
#'
#' @export
#' @aliases .stageObject .altStageObject altStageObject altStageObjectFunction
altSaveObject <- function(x, path, ...) {
    FUN <- altSaveObjectFunction()

    if (is.null(FUN)) {
        FUN <- saveObject
    } else {
        # Setting up the save environment.
        sfuns <- registerSaveEnvironment()
        on.exit({ 
            sfuns$write(path)
            sfuns$restore()
        }, add=TRUE, after=FALSE)
    }

    FUN(x, path, ...)
}

#' @export
#' @rdname altSaveObject
altSaveObjectFunction <- (function() {
    cur.env <- new.env()
    cur.env$generic <- NULL
    function(generic) {
        prev <- cur.env$generic
        if (missing(generic)) {
            prev
        } else {
            cur.env$generic <- generic
            invisible(prev)
        }
    }
})()

#######################################
########### OLD STUFF HERE ############
#######################################

#' @export
altStageObject <- function(...) {
    FUN <- altStageObjectFunction()
    if (is.null(FUN)) {
        FUN <- stageObject
    }
    FUN(...)
}

#' @export
altStageObjectFunction <- (function() {
    cur.env <- new.env()
    cur.env$generic <- NULL
    function(generic) {
        prev <- cur.env$generic
        if (missing(generic)) {
            prev
        } else {
            cur.env$generic <- generic
            invisible(prev)
        }
    }
})()

# Soft-deprecated back-compatibility fixes.

#' @export
.stageObject <- function(...) altStageObject(...)

#' @export
.altStageObject <- function(...) altStageObjectFunction(...)

