#' Create MoinMoin Page to Document Package
#'
#' See \url{http://moinmo.in} for details on \special{MoinMoin} markup.
#' @param x a filename or Rd object to use as input for single Rd file conversion, or a package name, a path to package top level directory for combined file conversion. Default is working directory.
#' @param out out a filename (with or without a path) or connection object to which to write the output. If there is no path, then the file will written to the current directory. If \code{out} is not specified, a file will be written to the current directory as "name.moin", where name is the package name.
#' @param silent logical: if \code{TRUE}, messages are suppressed.
#' @param internals logical: if \code{TRUE}, Rd files with a keyword set to "internal" are also included, rather than being skipped by default.
#' @param ... additional paramaters to pass to the \code{Rd2moin} converter. See \code{\link[tools:Rd2HTML]{Rd2HTML}} and/or \code{\link[rhelium:Rd2moin]{Rd2moin}} for furhter details.
#' @author Ian Riley (ian@riley.asia)
#' @return Documentation in \special{MoinMoin} format for either an entire R package or a single Rd file.
#' @examples
#' moinDoc("./rhelium-package")
#' @export
moinDoc <- function(x = ".", out = "", silent = FALSE, internals = FALSE, ...)
{
	if (is.null(x) || is.na(x) || x == "") stop('argument "x" is missing, with no default')

	moinDoc_out = NULL
	if ("connection" %in% class(out)) {
		if (!isOpen(out, "write")) stop("connection is not write enabled")
		else {
			 moinDoc_out = summary(out)
			 switch(class(out)[[1]],
			 	"file" = {
			 		out <- moinDoc_out$description
			 	},
			 	"terminal" = {
			 		out < stdout()
			 	},
			 	stop("cannot handle connection type:", class(out)[[1]])
			 )
		}
	} else {
		if (class(out) != "character") {
			stop('class "', class(out), '" is not sutable for opening output connection')
		}
	}
		
	#desc <- list()
	fromInstalled <- FALSE
	pgk_path <- path.package(x, quiet = TRUE)
	if (length(pgk_path)) {
		fromInstalled <- TRUE
		x <- pgk_path[1]
	}
	if (!fromInstalled && !has.Rd_man(x)) {
		stop('argument "x" is neither an existing package nor a "man" folder with Rd files')
	} 
	base_path <- normalizePath(x)

	local::as.object(get_pkg_docs(path = base_path), envir = environment())
	pkg <- desc['Package']
		
	options(moinDoc_pkg = pkg)
	options(moinDoc_path = base_path)
	options(moinDoc_silent = silent)
	options(moinDoc_internals = internals)
	options(moinDoc_fromInstalled = fromInstalled)
	on.exit(options(moinDoc_pkg = NULL), add = TRUE)
	on.exit(options(moinDoc_path = NULL), add = TRUE)
	on.exit(options(moinDoc_silent = NULL), add = TRUE)
	on.exit(options(moinDoc_internals = NULL), add = TRUE)
	on.exit(options(moinDoc_fromInstalled = NULL), add = TRUE)

	if (is.null(moinDoc_out) && out == "") {
		if (fromInstalled) out <- file.path('.', paste0(pkg, ".moin"))
		else out <- file.path(base_path, paste0(pkg, ".moin"))
	}
		
	if (out == stdout()) writeMessage(out, "Constructing MoinMoin page: stdout")
	else writeMessage(out, "Constructing MoinMoin page: ", out)

	.moinDoc <- pageHead(desc, out)

	for (doc in docs[docOrder(docs)])
		.moinDoc <- paste(.moinDoc, pageEntry(doc, out, ...), sep = "\n", collapse = "")

	.moinDoc <- paste0(.moinDoc, pageFoot(desc, out), collapse = "")
	
}

get_pkg_docs <- function(pkg = "", path = NULL) {
	
	unpack <- function(desc) {
		fields <- desc$fields()
		vapply(fields, desc$get, "")
	}
	
	if (is.null(path)) {
		desc <- unpack(desc::desc(package = pkg))
		docs <- Rd_db(package = pkg)
	} else {
		desc <- unpack(desc::desc(file = path))
		docs <- tools::Rd_db(package = desc['Package'], dir = path)
	}
	list(desc = desc, docs = docs)	
}

docOrder <- function(x, type = ".Rd") {
	entries <- sort(names(x))
	pkg <- options()$moinDoc_pkg
	pkgDoc1 <- paste0(pkg, type)
	pkgDoc2 <- paste0(pkg, "-package", type)
	if (pkgDoc1 %in% entries)
		entries <- c(pkgDoc1, entries[entries != pkgDoc1])
	else if (pkgDoc2 %in% entries) 
		entries <- c(pkgDoc2, entries[entries != pkgDoc2])
	return(entries)
}

writeContent <- function(out, content, add=TRUE) 
{
	if (out == stdout()) {
		cat(content)
		cat("\n")
	} else {
		output <- file(out, open = ifelse(add, "at", "wt"))
		writeLines(content, output)
		close(output)
	}
}

writeMessage <- function(out, ...) {
	if (!options()$moinDoc_silent && out != stdout()) message(...)
}

pageHead <- function(x, out)
{
	local::as.object(x, envir = environment())
	moinUser <- options()$moinUser
	if (is.null(moinUser)) moinUser <- "UserName"
	content <- paste(
		"## Generated by ‘rhelium’ from Rd files: do not edit here",
		sprintf("## Edit documentation in ‘%s’ source files", Package),
		"#format wiki",
		"#language en",
		"#pragma section-numbers off",
		sprintf("#acl %s:read,write,delete,revert,admin All:read", moinUser),
		sprintf("#pragma keywords %s R-project package rhelium roxygen2 devtools", moinUser),
		"#pragma supplementation-page on\n",
		sep = "\n", collapse = "")
	writeContent(out, content, add = FALSE)
	content <- paste(
		markup(sprintf("R Package ‘%s’", Package), "heading", 1L),
		sprintf("Documentation for package ‘%s’ version %s. [[attachment:desc.txt|DESCRIPTION|target='_blank']]",
			Package, Version),
		"<<TableOfContents(2)>>",
		markup("", "hozline", 3L),
		sep = "\n\n", collapse = "")
	writeContent(out, content, add = TRUE)
	writeMessage(out, "\t- page processing instructions, heading and table of contents added")
}

pageEntry <- function(x, out, ...)
{
	if (!options()$moinDoc_internals && 
		"internal" %in% .Rd_get_metadata(x, "keyword"))
		 return()

	pkg <- options()$moinDoc_pkg
	docName <- "package documentation"
	entry <- x[[2L]][[1L]][1]
	if (entry == pkg) {
		x[[2L]][[1L]][1] <- paste0(entry, "-package")
	} else {
		docName <- entry
	}	

	x <<- x
	Rd2moin(x, out = out, append = TRUE, depth = 2, ...)
	content <- "------\n"
	writeContent(out, content, add = TRUE)
	
	sections <- RdTags(x)
	sections <- sections[!(sections %in% c("\\alias", "\\concept", "\\encoding", "\\keyword"))]
	writeMessage(out, sprintf("\t- %s section added...", docName))
	writeMessage(out, "\t\t-- with subsections: ", toString(sub("\\\\", "", sections)))
}

pageFoot <- function(x, out)
{
	local::as.object(x, envir = environment())
	path <- options()$moinDoc_path
	if (!options()$moinDoc_fromInstalled) {
		if (!has.R_code(path)) {
			writeMessage(out, "\t- warning: no code links written: no R folder or code found")
		} else {
			files <- get.R_files(path)
			files <- docOrder(setNames(files, files), type = ".R")
			links <- paste(
				vapply(files, markup, "linkAttach", FUN.VALUE = ""),
				sep = "", collapse = "\n\n")
			content <- paste(
				markup("Code", "heading" , 1L),
				"",
				links,
				"",
				sep = "\n", collapse = "")
			writeContent(out, content, add = TRUE)
		}
	} else {
		writeMessage(out, "\t- warning: no code links written: request is for installed package")
	}
	
	content <- paste(
		markup("References", "heading", 1L),
		markup(citation(Package)$textVersion, "itemNumeric"),
		markup(citation("roxygen2")$textVersion, "itemNumeric"),
		markup(citation("tools")$textVersion, "itemNumeric"),
		markup("", "hozline", 3L),
		"<<ViewLog()>>",
		markup("", "hozline", 3L),
		"CategoryRcoding CategoryRpackage",
		sep = "\n\n", collapse = "")
	writeContent(out, content, add = TRUE)
	writeMessage(out, "\t- page footer added")
}

