# Copyright 2010-2014 Meik Michalke <meik.michalke@hhu.de>
#
# This file is part of the R package rkwarddev.
#
# rkwarddev 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 3 of the License, or
# (at your option) any later version.
#
# rkwarddev 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.
#
# You should have received a copy of the GNU General Public License
# along with rkwarddev.  If not, see <http://www.gnu.org/licenses/>.


#' Create XML document for RKWard plugins
#'
#' @param name Character string, the name of the plugin. Will be used for the names of the JavaScript and
#'    help files to be included, following the scheme \emph{"<name>.<ext>"}.
#' @param dialog An object of class \code{XiMpLe.node} to be pasted as the \code{<dialog>} section. See
#'    \code{\link[rkwarddev:rk.XML.dialog]{rk.XML.dialog}} for details.
#' @param wizard An object of class \code{XiMpLe.node} to be pasted as the \code{<wizard>} section. See
#'    \code{\link[rkwarddev:rk.XML.wizard]{rk.XML.wizard}} for details.
#' @param logic An object of class \code{XiMpLe.node} to be pasted as the \code{<logic>} section. See
#'    \code{\link[rkwarddev:rk.XML.logic]{rk.XML.logic}} for details.
#' @param snippets An object of class \code{XiMpLe.node} to be pasted as the \code{<snippets>} section. See
#'    \code{\link[rkwarddev:rk.XML.snippets]{rk.XML.snippets}} for details.
#' @param provides Character vector with possible entries of \code{"logic"}, \code{"dialog"} or \code{"wizard"}, defining what
#'    sections the document should provide even if \code{dialog}, \code{wizard} and \code{logic} are \code{NULL}.
#'    These sections must be edited manually and some parts are therefore commented out.
#' @param help Logical, if \code{TRUE} an include tag for a help file named \emph{"<name>.rkh"} will be added to the header.
#' @param include Character string or vector, relative path(s) to other file(s), which will then be included in the head of the GUI XML document.
#' @param label Character string, a text label for the plugin's top level, i.e. the window title of the dialog.
#'    Will only be used if \code{dialog} or \code{wizard} are \code{NULL}.
#' @param clean.name Logical, if \code{TRUE}, all non-alphanumeric characters except the underscore (\code{"_"}) will be removed from \code{name}.
#' @param about An object of class \code{XiMpLe.node} with descriptive information on the plugin and its authors,
#'    see \code{link[XiMpLe:rk.XML.about]{rk.XML.about}} for details. Only useful for information that differs from the \code{<about>}
#'    section of the \code{.pluginmap} file. Skipped if \code{NULL}.
#' @param dependencies An object of class \code{XiMpLe.node} to be pasted as the \code{<dependencies>} section,
#'    See \code{\link[rkwarddev:rk.XML.dependencies]{rk.XML.dependencies}} for details. Skipped if \code{NULL}.
#'    This is only useful for information that differs from the \code{<dependencies>} section of the \code{.pluginmap} file.
#' @param gen.info Logical, if \code{TRUE} a comment note will be written into the document,
#'    that it was generated by \code{rkwarddev} and changes should be done to the script.
#'    You can also provide a character string naming the very rkwarddev script file that generates this plugin,
#'    which will then also be added to the comment.
#' @param i18n Either a character string or a named list with the optional elements \code{context}
#'    or \code{comment}, to give some \code{i18n_context} information for this node. If set to \code{FALSE},
#'    the attribute \code{label} will be renamed into \code{noi18n_label}.
#' @return An object of class \code{XiMpLe.doc}.
#' @export
#' @seealso \href{help:rkwardplugins}{Introduction to Writing Plugins for RKWard}
#' @examples
#' \dontrun{
#' test.checkboxes <- rk.XML.row(rk.XML.col(
#'   list(
#'     rk.XML.cbox(label="foo", val="foo1", chk=TRUE),
#'     rk.XML.cbox(label="bar", val="bar2"))))
#' test.dropdown <- rk.XML.dropdown("mydrop",
#'   options=list("First Option"=c(val="val1"),
#'   "Second Option"=c(val="val2", chk=TRUE)))
#' # combine the above into a tabbook
#' test.tabbook <- rk.XML.tabbook("My Tabbook", tabs=c(
#'   "First Tab"=test.checkboxes, "Second Tab"=test.dropdown))
#' # make a plugin with that tabbook
#' test.plugin <- rk.XML.plugin("My test", dialog=rk.XML.dialog(test.tabbook))
#' }

rk.XML.plugin <- function(name, dialog=NULL, wizard=NULL, logic=NULL, snippets=NULL, provides=NULL, help=TRUE, include=NULL,
  label=NULL, clean.name=TRUE, about=NULL, dependencies=NULL, gen.info=TRUE, i18n=NULL){
  if(isTRUE(clean.name)){
    name.orig <- name
    name <- clean.name(name)
  } else {}

  all.children <- list()

  if(isTRUE(gen.info)){
    all.children[[length(all.children)+1]] <- generator.info()
  } else if(is.character(gen.info)){
    all.children[[length(all.children)+1]] <- generator.info(script=gen.info)
  } else {}

  all.children[[length(all.children)+1]] <- rk.XML.code(file=paste0(name, ".js"))

  if(isTRUE(help)){
    all.children[[length(all.children)+1]] <- rk.XML.help(file=paste0(name, ".rkh"))
  } else {}

  if(!is.null(include)){
    for (thisInclude in include) {
      all.children[[length(all.children)+1]] <- rk.XML.include(file=thisInclude)
    }
  } else {}

  # check about and dependencies
  # result is a named list with "about" and "dependencies"
  about.dep.list <- dependenciesCompatWrapper(dependencies=dependencies, about=about, hints=FALSE)
  dependencies <- about.dep.list[["dependencies"]]
  about <- about.dep.list[["about"]]

  # dependencies section
  if(!is.null(dependencies)){
    all.children[[length(all.children)+1]] <- dependencies
  } else {}

  # about section
  if(!is.null(about)){
    all.children[[length(all.children)+1]] <- about
  } else {}

  if(!is.null(snippets)){
    # check if this is *really* a snippets section, otherwise quit and go dancing
    valid.parent("snippets", node=snippets, see="rk.XML.snippets")
    all.children[[length(all.children)+1]] <- snippets
  } else {}

  if(is.null(logic)){
    if("logic" %in% provides){
      lgc.children <- list(
          # add these as comments because they need editing
          XMLNode("!--", rk.XML.convert(sources="!edit!", mode=c(equals="!edit!"), id.name="!edit!")),
          XMLNode("!--", rk.XML.connect(governor="!edit!", client="!edit!"))
        )
      all.children[[length(all.children)+1]] <- XMLNode("logic", .children=lgc.children)
    } else {}
  } else {
    # check if this is *really* a logic section, otherwise quit and go dancing
    valid.parent("logic", node=logic, see="rk.XML.logic")
    all.children[[length(all.children)+1]] <- logic
  }

  if(is.null(dialog)){
    if("dialog" %in% provides){
      all.children[[length(all.children)+1]] <- rk.XML.dialog(label=label)
    } else {}
  } else {
    # check if this is *really* a dialog section
    valid.parent("dialog", node=dialog, see="rk.XML.dialog")
    all.children[[length(all.children)+1]] <- dialog
  }

  if(is.null(wizard)){
    if("wizard" %in% provides){
      # create a first page for the wizard section
      plugin.wizard.page <- rk.XML.page(id.name=auto.ids(label, prefix=ID.prefix("page")))
      plugin.wizard <- rk.XML.wizard(plugin.wizard.page, label=label)
      all.children[[length(all.children)+1]] <- plugin.wizard
    } else {}
  } else {
    # check if this is *really* a wizard section
    valid.parent("wizard", node=wizard, see="rk.XML.wizard")
    all.children[[length(all.children)+1]] <- wizard
  }

  # check for additional i18n info; if FALSE, "label" will be renamed to "noi18n_label"
  i18n.attrs <- check.i18n(i18n=i18n)

  top.doc <- check.i18n(
    i18n=i18n,
    node=XMLNode("document", attrs=i18n.attrs, .children=child.list(all.children)),
    comment=TRUE
  )

  plugin <- XMLTree(
      dtd=list(doctype="rkplugin"),
      .children=child.list(top.doc)
    )

  # check for duplicate IDs

  return(plugin)
}
