# utils ----
#' @importFrom utils getAnywhere getFromNamespace
get_fun <- function(x){
  if( grepl("::", x, fixed = TRUE) ){
    coumpounds <- strsplit(x, split = "::", x, fixed = TRUE)[[1]]
    z <- getFromNamespace(coumpounds[2], ns = coumpounds[1] )
  } else {
    z <- getAnywhere(x)
    if(length(z$objs) < 1){
      stop("could not find any function named ", shQuote(z$name), " in loaded namespaces or in the search path. If the package is installed, specify name with `packagename::function_name`.")
    }
  }
  z
}

file_with_meta_ext <- function(file, meta_ext, ext = tools::file_ext(file)) {
  paste(tools::file_path_sans_ext(file),
        ".", meta_ext, ".", ext,
        sep = ""
  )
}

# tables_default_values ----
tables_default_values <- list(
  style = "Table",
  layout = "autofit",
  width = 1,
  caption = list(
    style = "Table Caption",
    pre = "Table ", sep = ": "
  ),
  conditional = list(
    first_row = TRUE,
    first_column = FALSE,
    last_row = FALSE,
    last_column = FALSE,
    no_hband = FALSE,
    no_vband = TRUE
    )
)

# plots_default_values ----
plots_default_values <- list(
  style = "Figure",
  align = "center",
  caption = list(
    style = "Image Caption",
    pre = "Figure ", sep = ": "
  )
)

# lists_default_values ----
lists_default_values <- list(
  ol.style = NULL,
  ul.style = NULL
)

# memoise reference_docx ----

#' @importFrom officer get_reference_value
get_docx_uncached <- function() {
  ref_docx <- read_docx(get_reference_value(format = "docx"))
  ref_docx
}

#' @importFrom memoise memoise forget
get_reference_rdocx <- memoise(get_docx_uncached)


# main ----
#' @export
#' @title Advanced R Markdown Word Format
#' @description Format for converting from R Markdown to an MS Word
#' document. The function comes also with improved output options.
#' @param base_format a scalar character, format to be used as a base document for
#' officedown. default to [word_document][rmarkdown::word_document] but
#' can also be word_document2 from bookdown
#' @param tables a list that can contain few items to style tables and table captions.
#' Missing items will be replaced by default values. Possible items are the following:
#'
#' * `style`: the Word stylename to use for tables.
#' * `layout`: 'autofit' or 'fixed' algorithm. See \code{\link[officer]{table_layout}}.
#' * `width`: value of the preferred width of the table in percent (base 1).
#' * `caption`; caption options, i.e.:
#'   * `style`: Word stylename to use for table captions.
#'   * `pre`: prefix for numbering chunk (default to "Table ").
#'   * `sep`: suffix for numbering chunk (default to ": ").
#' * `conditional`: a list of named logical values:
#'   * `first_row` and `last_row`: apply or remove formatting from the first or last row in the table
#'   * `first_column`  and `last_column`: apply or remove formatting from the first or last column in the table
#'   * `no_hband` and `no_vband`: don't display odd and even rows or columns with alternating shading for ease of reading.
#'
#' Default value is (in R format):
#' ```
#' list(
#'    style = "Table", layout = "autofit", width = 1,
#'    caption = list(
#'      style = "Table Caption", pre = "Table ", sep = ": "),
#'    conditional = list(
#'      first_row = TRUE, first_column = FALSE, last_row = FALSE,
#'      last_column = FALSE, no_hband = FALSE, no_vband = TRUE
#'    )
#' )
#' ```
#'
#' Default value is (in YAML format):
#' ```
#' style: Table
#' layout: autofit
#' width: 1.0
#' caption:
#'   style: Table Caption
#'   pre: 'Table '
#'   sep: ': '
#' conditional:
#'   first_row: true
#'   first_column: false
#'   last_row: false
#'   last_column: false
#'   no_hband: false
#'   no_vband: true
#' ```
#'
#' @param plots a list that can contain few items to style figures and figure captions.
#' Missing items will be replaced by default values. Possible items are the following:
#'
#' * `style`: the Word stylename to use for plots.
#' * `align`: alignment of figures in the output document (possible values are 'left',
#' 'right' and 'center').
#' * `caption`; caption options, i.e.:
#'   * `style`: Word stylename to use for figure captions.
#'   * `pre`: prefix for numbering chunk (default to "Figure ").
#'   * `sep`: suffix for numbering chunk (default to ": ").
#'
#' Default value is (in R format):
#' ```
#' list(
#'   style = "Normal", align = "center",
#'   caption = list(
#'     style = "Image Caption",
#'     pre = "Figure ",
#'     sep = ": "
#'    )
#'  )
#'  ```
#'
#' Default value is (in YAML format):
#' ```
#' style: Normal
#' align: center
#' caption:
#'   style: Image Caption
#'   pre: 'Figure '
#'   sep: ': '
#' ```
#' @param lists a list containing two named items `ol.style` and
#' `ul.style`, values are the stylenames to be used to replace the style of ordered
#' and unordered lists created by pandoc. If NULL, no replacement is made.
#'
#' Default value is `list(ol.style = NULL, ul.style = NULL)`:
#'
#' ```
#' ol.style: null
#' ul.style: null
#' ```
#' @param mapstyles a named list of style to be replaced in the generated
#' document. `list("Normal" = c("Author", "Date"))` will result in a document where
#' all paragraphs styled with stylename "Date" and "Author" will be then styled with
#' stylename "Normal".
#' @param reference_num if TRUE, text for references to sections will be
#' the section number (e.g. '3.2'). If FALSE, text for references to sections
#' will be the text (e.g. 'section title').
#' @param ... arguments used by [word_document][rmarkdown::word_document]
#' @return R Markdown output format to pass to [render][rmarkdown::render]
#' @section Finding stylenames:
#'
#' You can access them in the Word template used. Function
#' [styles_info()][officer::styles_info] can let you read these
#' styles.
#'
#' You need officer to read the stylenames (to get information
#' from a specific "reference_docx", change `ref_docx_default`
#' in the example below.
#'
#' ```
#' library(officer)
#' docx_file <- system.file(package = "officer", "template", "template.docx")
#' doc <- read_docx(docx_file)
#' ```
#'
#' To read `paragraph` stylenames:
#' ```
#' styles_info(doc, type = "paragraph")
#' ```
#'
#' To read `table` stylenames:
#' ```
#' styles_info(doc, type = "table")
#' ```
#'
#' To read `list` stylenames:
#' ```
#' styles_info(doc, type = "numbering")
#' ```
#'
#'
#' @section R Markdown yaml:
#' The following demonstrates how to pass arguments in the R Markdown yaml:
#'
#' ```
#' ---
#' output:
#'   officedown::rdocx_document:
#'     reference_docx: pandoc_template.docx
#'     tables:
#'       style: Table
#'       layout: autofit
#'       width: 1.0
#'       caption:
#'         style: Table Caption
#'         pre: 'Table '
#'         sep: ': '
#'       conditional:
#'         first_row: true
#'         first_column: false
#'         last_row: false
#'         last_column: false
#'         no_hband: false
#'         no_vband: true
#'     plots:
#'       style: Normal
#'       align: center
#'       caption:
#'         style: Image Caption
#'         pre: 'Figure '
#'         sep: ': '
#'     lists:
#'       ol.style: null
#'       ul.style: null
#'     mapstyles:
#'       Normal: ['First Paragraph', 'Author', 'Date']
#'     reference_num: true
#' ---
#' ```
#' @examples
#' library(rmarkdown)
#' run_ok <- pandoc_available() &&
#'   pandoc_version() >= numeric_version("2.0")
#'
#' if(run_ok){
#'
#' # minimal example -----
#' example <- system.file(package = "officedown",
#'   "examples/minimal_word.Rmd")
#' rmd_file <- tempfile(fileext = ".Rmd")
#' file.copy(example, to = rmd_file)
#'
#' docx_file_1 <- tempfile(fileext = ".docx")
#' render(rmd_file, output_file = docx_file_1, quiet = TRUE)
#'
#' # bookdown example -----
#' if(require("bookdown")){
#'
#' bookdown_loc <- system.file(package = "officedown", "examples/bookdown")
#'
#' temp_dir <- tempfile()
#' # uncomment next line to get the result in your working directory
#' # temp_dir <- "./bd_example"
#'
#' dir.create(temp_dir, showWarnings = FALSE, recursive = TRUE)
#' file.copy(
#'   from = list.files(bookdown_loc, full.names = TRUE),
#'   to = temp_dir,
#'   overwrite = TRUE, recursive = TRUE)
#'
#' render_site(
#'   input = temp_dir, encoding = 'UTF-8',
#'   envir = new.env(), quiet = TRUE)
#'
#' docx_file_2 <- file.path(temp_dir, "_book", "bookdown.docx")
#'
#' if(file.exists(docx_file_2)){
#'   message("file ", docx_file_2, " has been written.")
#' }
#' }
#'
#' }
#' @importFrom officer change_styles
#' @importFrom utils modifyList
rdocx_document <- function(base_format = "rmarkdown::word_document",
                           tables = list(), plots = list(), lists = list(),
                           mapstyles = list(),
                           reference_num = TRUE,
                           ...) {

  args <- list(...)
  if(is.null(args$reference_docx)){
    args$reference_docx <- system.file(
      package = "officedown", "examples",
      "bookdown", "template.docx"
    )
  }

  base_format_fun <- get_fun(base_format)
  output_formats <- do.call(base_format_fun, args)


  tables <- modifyList(tables_default_values, tables)
  plots <- modifyList(plots_default_values, plots)
  lists <- modifyList(lists_default_values, lists)

  output_formats$knitr$opts_chunk <- append(
    output_formats$knitr$opts_chunk,
    list(tab.cap.style = tables$caption$style,
         tab.cap.pre = tables$caption$pre,
         tab.cap.sep = tables$caption$sep,
         tab.lp = "tab:",
         tab.style = tables$style,
         tab.width = tables$width,

         first_row = tables$conditional$first_row,
         first_column = tables$conditional$first_column,
         last_row = tables$conditional$last_row,
         last_column = tables$conditional$last_column,
         no_hband = tables$conditional$no_hband,
         no_vband = tables$conditional$no_vband,

         fig.cap.style = plots$caption$style,
         fig.cap.pre = plots$caption$pre,
         fig.cap.sep = plots$caption$sep,
         fig.align = plots$align,
         fig.style = plots$style,
         fig.lp = "fig:"
         )
    )
  if(is.null(output_formats$knitr$knit_hooks)){
    output_formats$knitr$knit_hooks <- list()
  }
  output_formats$knitr$knit_hooks$plot <- plot_word_fig_caption

  output_formats$post_knit <- function(metadata, input_file, runtime, ...){
    output_file <- file_with_meta_ext(input_file, "knit", "md")
    content <- readLines(output_file)

    content <- post_knit_table_captions(content,
      tab.cap.pre = tables$caption$pre, tab.cap.sep = tables$caption$sep,
      style = tables$caption$style)
    content <- post_knit_caption_references(content, lp = "tab:")
    content <- post_knit_caption_references(content, lp = "fig:")
    content <- post_knit_std_references(content, numbered = reference_num)
    content <- block_macro(content)
    writeLines(content, output_file)
  }

  output_formats$post_processor <- function(metadata, input_file, output_file, clean, verbose) {
    x <- officer::read_docx(output_file)
    x <- process_images(x)
    x <- process_links(x)
    x <- process_embedded_docx(x)
    x <- process_par_settings(x)
    x <- process_list_settings(x, ul_style = lists$ul.style, ol_style = lists$ol.style)
    x <- change_styles(x, mapstyles = mapstyles)
    forget(get_reference_rdocx)
    print(x, target = output_file)
    output_file
  }
  output_formats$bookdown_output_format = 'docx'
  output_formats

}
