#  File src/library/utils/R/changedFiles.R
#  Part of the R package, https://www.R-project.org
#
#  Copyright (C) 2013 The R Core Team
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  A copy of the GNU General Public License is available at
#  https://www.R-project.org/Licenses/

fileSnapshot <- function(path = ".", file.info = TRUE, timestamp = NULL, 
			 md5sum = FALSE, digest = NULL, 
			 full.names = length(path) > 1, ...) {
			 
    if (length(path) > 1 && !full.names)
	stop("'full.names' must be TRUE for multiple paths.")

    if (length(timestamp) == 1)
	file.create(timestamp)

    path <- normalizePath(path)
    args <- list(...)
    
    fullnames <- names <- character(0)
    for (i in seq_along(path)) {
	newnames <- do.call(list.files, c(path = path[i], full.names = full.names, args))
	names <- c(names, newnames)
	if (full.names) fullnames <- names
	else fullnames <- c(fullnames, file.path(path[i], newnames))
    }
    
    if (file.info) {
	info <- file.info(fullnames)
	if (!full.names)
	    rownames(info) <- names
    } else
	info <- data.frame(row.names = names)
	
    if (md5sum)
	info <- data.frame(info, md5sum = suppressWarnings(tools::md5sum(fullnames)), 
			   stringsAsFactors = FALSE)
	
    if (!is.null(digest))
	info <- data.frame(info, digest = digest(fullnames), stringsAsFactors = FALSE)
   
    structure(list(info = info, path = path, timestamp = timestamp, 
         file.info = file.info, md5sum = md5sum, digest = digest, 
         full.names = full.names, args = args), class = "fileSnapshot")
}
    
changedFiles <- function(before, after, path = before$path, timestamp = before$timestamp, 
                         check.file.info = c("size", "isdir", "mode", "mtime"), 
			 md5sum = before$md5sum, digest = before$digest, 
			 full.names = before$full.names, ...) {
			 
    stopifnot(inherits(before, "fileSnapshot"))

    if (missing(after)) {
        get.file.info <- length(check.file.info) > 0 && before$file.info
	
	args <- before$args
	newargs <- list(...)
	args[names(newargs)] <- newargs
	
	after <- do.call(fileSnapshot, c(list(path = path, timestamp = NULL, 
	                 file.info = get.file.info, md5sum = md5sum, 
	                 digest = digest, full.names = full.names), args))
    }
    stopifnot(inherits(after, "fileSnapshot"))
	      
    preinfo <- before$info
    postinfo <- after$info
    prenames <- rownames(preinfo)
    postnames <- rownames(postinfo)
    
    added <- setdiff(postnames, prenames)
    deleted <- setdiff(prenames, postnames)
    common <- intersect(prenames, postnames)
    
    if (!before$file.info || !after$file.info) 
	check.file.info <- NULL
    
    if (length(check.file.info)) {
        pre <- preinfo[common, check.file.info, drop = FALSE]
        post <- postinfo[common, check.file.info, drop = FALSE]
        changes <- pre != post
    }
    else changes <- matrix(logical(0), nrow = length(common), ncol = 0, 
                           dimnames = list(common, character(0)))
			   
    if (length(timestamp))
	if (file.exists(timestamp)) {
	    fullnames <- if (after$full.names) common else file.path(after$path, common)
	    changes <- cbind(changes, Newer = file_test("-nt", fullnames, timestamp))
	} else
	    warning("Timestamp file no longer exists.")
	
    if (md5sum) {
        pre <- preinfo[common, "md5sum"]
        post <- postinfo[common, "md5sum"]
	changes <- cbind(changes, md5sum = pre != post)
    }
    
    if (!is.null(digest)) {
        pre <- preinfo[common, "digest"]
        post <- postinfo[common, "digest"]
	changes <- cbind(changes, digest = pre != post)
    }
    changed <- rownames(changes)[rowSums(changes, na.rm = TRUE) > 0]
    structure(list(added = added, deleted = deleted, changed = changed, 
        unchanged = setdiff(common, changed), changes = changes), 
	class = "changedFiles")
}
  
print.fileSnapshot <- function(x, verbose = FALSE, ...) {
    cat("File snapshot:\n path = ", x$path, 
        "\n timestamp = ", x$timestamp, 
	"\n file.info = ", x$file.info, 
	"\n md5sum = ", x$md5sum, 
	"\n digest = ", deparse(x$digest, control = NULL),
	"\n full.names = ", x$full.names,
	"\n args = ", deparse(x$args, control = NULL), 
	"\n ", nrow(x$info), " files recorded.\n", sep="")
    if (verbose) {
	if (ncol(x$info)) print(x$info)
	else cat("Files:", rownames(x$info), sep="\n ")
    }
    invisible(x)
}

print.changedFiles <- function(x, verbose = FALSE, ...) {
    if (length(x$added)) 
    	cat("Files added:\n",  paste0("  ", x$added, collapse="\n"), "\n", sep="")
    if (length(x$deleted)) 
    	cat("Files deleted:\n",  paste0("  ", x$deleted, collapse="\n"), "\n", sep="")
    changes <- x$changes
    if (!verbose) {
	changes <- changes[rowSums(changes, na.rm = TRUE) > 0, , drop=FALSE]
	changes <- changes[, colSums(changes, na.rm = TRUE) > 0, drop=FALSE]
    }	
    if (verbose || nrow(changes)) {
        cat("File changes:\n")
        print(changes)
    }
    invisible(x)
}
