#' Convert to an ioslides Presentation
#'
#' Format for converting from R Markdown to an
#' \href{https://code.google.com/archive/p/io-2012-slides/}{ioslides} presentation.
#'
#' @inheritParams html_document
#' @param logo Path to file that includes a logo for use in the presentation
#'   (should be square and at least 128x128).
#' @param slide_level Header level to consider as slide separator (Defaults to
#'   header 2).
#' @param incremental \code{TRUE} to render slide bullets incrementally.
#'   Note that if you want to reverse the default incremental behavior for an
#'   individual bullet you can preceded it with \code{>}.
#'   For example: \emph{\code{> - Bullet Text}}.
#' @param widescreen Display presentation with wider dimensions.
#' @param smaller Use smaller text on all slides. You can also enable this for
#'   individual slides by adding the \code{.smaller} attribute to the slide
#'   header (see \emph{Presentation Size} below for details).
#' @param transition Speed of slide transitions. This can be "default",
#'   "slower", "faster", or a numeric value with a number of seconds (e.g. 0.5).
#' @param analytics A Google analytics property ID.
#'@param smart Produce typographically correct output, converting straight
#'  quotes to curly quotes, \code{---} to em-dashes, \code{--} to en-dashes, and
#'  \code{...} to ellipses.
#' @return R Markdown output format to pass to \code{\link{render}}.
#' @details
#'   See the \href{https://bookdown.org/yihui/rmarkdown/ioslides-presentation.html}{
#'   online documentation} for additional details on using the
#'   \code{ioslides_presentation} format.
#'
#'   Note that, if a \code{before_body} include is specified in \code{includes},
#'   then it will replace the standard title slide entirely.
#' @section Slide Basics:
#'   You can create a slide show broken up into sections by using the # and ##
#'   heading tags (you can also create a new slide without a header using a
#'   horizontal rule (\code{----------}). For example here's a simple slide show:
#'   \preformatted{
#' ---
#' title: "Habits"
#' author: John Doe
#' date: March 22, 2005
#' output: ioslides_presentation
#' ---
#'
#' # In the morning
#'
#' ## Getting up
#'
#' - Turn off alarm
#' - Get out of bed
#'
#' ## Breakfast
#'
#' - Eat eggs
#' - Drink coffee
#'
#' # In the evening
#'
#' ## Dinner
#'
#' - Eat spaghetti
#' - Drink wine
#'
#' ----------
#'
#' ![picture of spaghetti](images/spaghetti.jpg)
#'
#' ## Going to sleep
#'
#' - Get in bed
#' - Count sheep
#'
#' }
#'   You can add a subtitle to a slide or section by including text after the
#'   pipe (|) character. For example:
#'   \preformatted{## Getting up | What I like to do first thing}
#' @section Display Modes:
#'   The following single character keyboard shortcuts enable alternate display
#'   modes:
#'   \itemize{
#'     \item{\code{'f'}      }{enable fullscreen mode}
#'     \item{\code{'w'}      }{toggle widescreen mode}
#'     \item{\code{'o'}      }{enable overview mode}
#'     \item{\code{'h'}      }{enable code highlight mode}
#'     \item{\code{'p'}      }{show presenter notes}
#'   }
#'   Pressing \code{Esc} exits all of these modes. See the sections below on
#'   \emph{Code Highlighting} and \emph{Presenter Mode} for additional
#'   detail on those modes.
#'
#' @section Incremental Bullets:
#'   You can render bullets incrementally by adding the \code{incremental}
#'   option:
#'   \preformatted{
#' ---
#' output:
#'   ioslides_presentation:
#'     incremental: true
#' ---
#' }
#'   If you want to render bullets incrementally for some slides but not
#'   others you can use this syntax:
#'   \preformatted{
#' > - Eat eggs
#' > - Drink coffee
#' }
#' @section Presentation Size:
#'   You can display the presentation using a wider form factor using the
#'   \code{widescreen} option. You can specify that smaller text be used with
#'   the \code{smaller} option. For example:
#'   \preformatted{
#' ---
#' output:
#'   ioslides_presentation:
#'     widescreen: true
#'     smaller: true
#' ---
#' }
#'   You can also enable the \code{smaller} option on a slide-by-slide basis
#'   by adding the \code{.smaller} attibute to the slide header:
#'   \preformatted{## Getting up {.smaller}}
#' @section Adding a Logo:
#'   You can add a logo to the presentation using the \code{logo} option (the
#'   logo should be square and at least 128x128). For example:
#'   \preformatted{
#' ---
#' output:
#'   ioslides_presentation:
#'     logo: logo.png
#' ---
#' }
#'   A 128x128 version of the logo graphic will be added to the title slide and
#'   an icon version of the logo will be included in the bottom-left footer of
#'   each slide.
#' @section Build Slides:
#'   Slides can also have a \code{.build} attribute that indicate that their
#'   content should be displayed incrementally. For example:
#'   \preformatted{## Getting up {.build}}
#'   Slide attributes can be combined if you need to specify more than one,
#'   for example:
#'   \preformatted{## Getting up {.smaller .build}}
#' @section Code Highlighting:
#'   It's possible to select subsets of code for additional emphasis by adding a
#'   special "highlight" comment around the code. For example:
#'   \preformatted{
#' ### <b>
#' x <- 10
#' y <- x * 2
#' ### </b>
#' }
#'   The highlighted region will be displayed with a bold font. When you want to
#'   help the audience focus exclusively on the highlighted region press the
#'   \code{'h'} key and the rest of the code will fade away.
#' @section Tables:
#'   The ioslides template has an attractive default style for tables so you
#'   shouldn't hesitate to add tables for presenting more complex sets of
#'   information. Pandoc markdown supports several syntaxes for defining
#'   tables which are described in the
#'   \href{https://pandoc.org/MANUAL.html}{pandoc online documentation}.
#' @section Advanced Layout:
#'   You can center content on a slide by adding the \code{.flexbox}
#'   and \code{.vcenter} attributes to the slide title. For example:
#'   \preformatted{## Dinner {.flexbox .vcenter}}
#'   You can horizontally center content by enclosing it in a \code{div} tag
#'   with class \code{centered}. For example:
#'   \preformatted{
#' <div class="centered">
#' This text is centered.
#' </div>
#' }
#'   You can do a two-column layout using the \code{columns-2} class.
#'   For example:
#'   \preformatted{
#' <div class="columns-2">
#'   ![Image](image.png)
#'
#'   - Bullet 1
#'   - Bullet 2
#'   - Bullet 3
#' </div>
#' }
#'   Note that content will flow across the columns so if you want to
#'   have an image on one side and text on the other you should make
#'   sure that the image has sufficient height to force the text to
#'   the other side of the slide.
#' @section Text Color:
#'   You can color content using base color classes red, blue, green, yellow,
#'   and gray (or variations of them e.g. red2, red3, blue2, blue3, etc.).
#'   For example:
#'   \preformatted{
#' <div class="red2">
#' This text is red
#' </div>
#' }
#' @section Presenter Mode:
#'   A separate presenter window can also be opened (ideal for when you are
#'   presenting on one screen but have another screen that's private to you).
#'   The window stays in sync with the main presentation window and also
#'   shows presenter notes and a thumbnail of the next slide. To enable
#'   presenter mode add \code{?presentme=true} to the URL of the presentation,
#'   for example:
#'   \preformatted{mypresentation.html?presentme=true}
#'   The presenter mode window will open and will always re-open with the
#'   presentation until it's disabled with:
#'   \preformatted{mypresentation.html?presentme=false}
#'   To add presenter notes to a slide you include it within a "notes"
#'   \code{div}. For example:
#'   \preformatted{
#' <div class="notes">
#' This is my *note*.
#'
#' - It can contain markdown
#' - like this list
#'
#' </div>
#' }
#' @section Printing and PDF Output:
#'   You can print an ioslides presentation from within browsers that have
#'   good support for print CSS (i.e. as of this writing Google Chrome
#'   has the best support). Printing maintains most of the visual styles
#'   of the HTML version of the presentation.
#'
#'   To create a PDF version of a presentation you can use Print to PDF
#'   from Google Chrome.
#' @export
ioslides_presentation <- function(number_sections = FALSE,
                                  logo = NULL,
                                  slide_level = 2,
                                  incremental = FALSE,
                                  fig_width = 7.5,
                                  fig_height = 4.5,
                                  fig_retina = 2,
                                  fig_caption = TRUE,
                                  dev = 'png',
                                  df_print = "default",
                                  smart = TRUE,
                                  self_contained = TRUE,
                                  widescreen = FALSE,
                                  smaller = FALSE,
                                  transition = "default",
                                  mathjax = "default",
                                  analytics = NULL,
                                  template = NULL,
                                  css = NULL,
                                  includes = NULL,
                                  keep_md = FALSE,
                                  lib_dir = NULL,
                                  md_extensions = NULL,
                                  pandoc_args = NULL,
                                  extra_dependencies = NULL,
                                  ...) {

  # base pandoc options for all output
  args <- c()

  # widescreen
  if (widescreen)
    args <- c(args, "--variable", "widescreen");

  # pagedtables
  if (identical(df_print, "paged")) {
    extra_dependencies <- append(extra_dependencies,
                                 list(html_dependency_pagedtable()))

  }

  # transition
  if (is.numeric(transition))
    transition <- as.character(transition)
  else if (transition %in% c("default", "faster", "slower"))
    transition <- switch(transition,
                         "default" = "0.4",
                         "faster" = "0.2",
                         "slower" = "0.6")
  else
    stop('transition must be "default", "faster", "slower" or a ',
         'numeric value (representing seconds)', call. = FALSE)
  args <- c(args, pandoc_variable_arg("transition", transition))

  # additional css
  for (css_file in css)
    args <- c(args, "--css", pandoc_path_arg(css_file, backslash = FALSE))

  # content includes
  args <- c(args, includes_to_pandoc_args(includes))

  # template path and assets
  if (is.null(template) || !file.exists(template))
    template <- pkg_file("rmd/ioslides/default.html")
  args <- c(args, "--template", pandoc_path_arg(template))

  # html dependency for ioslides
  extra_dependencies <- append(extra_dependencies,
                               list(html_dependency_ioslides()))

  # analytics
  if (!is.null(analytics))
    args <- c(args, pandoc_variable_arg("analytics", analytics))

  # pre-processor for arguments that may depend on the name of the
  # the input file (e.g. ones that need to copy supporting files)
  pre_processor <- function(metadata, input_file, runtime, knit_meta, files_dir,
                            output_dir) {

    # use files_dir as lib_dir if not explicitly specified
    if (is.null(lib_dir))
      lib_dir <- files_dir

    # extra args
    args <- c()

    # create the files dir if it doesn't exist
    if (!dir_exists(files_dir))
      dir.create(files_dir)

    # logo
    if (!is.null(logo)) {
      logo_path <- logo
      if (!self_contained) {
        # use same extension as specified logo (default is png if unspecified)
        logo_ext <- xfun::file_ext(logo)
        if (nchar(logo_ext) < 1)
          logo_ext <- "png"
        logo_path <- file.path(files_dir, paste("logo", logo_ext, sep = "."))
        file.copy(from = logo, to = logo_path)
        logo_path <- normalized_relative_to(output_dir, logo_path)
      } else {
        logo_path <- pandoc_path_arg(logo_path)
      }
      args <- c(args, "--variable", paste("logo=", logo_path, sep = ""))
    }

    # return additional args
    args
  }

  # post processor that renders our markdown using our custom lua
  # renderer and then inserts it into the main file
  post_processor <- function(metadata, input_file, output_file, clean, verbose) {

    # setup args
    args <- c()

    # add any custom pandoc args
    args <- c(args, pandoc_args)

    # number sections
    if (number_sections)
      args <- c(args, pandoc_lua_filter_args(pkg_file_lua("number-sections.lua")))

    lua_writer <- file.path(dirname(input_file), "ioslides_presentation.lua")
    # The input directory may not be writable (on e.g. Shiny Server), so write
    # to the output directory in this case. We don't always do this since
    # supplying a fully qualified path to the writer can trigger a bug on some
    # Linux configurations.
    if (!file.create(lua_writer, showWarnings = FALSE))
      lua_writer <- file.path(dirname(output_file), basename(lua_writer))
    on.exit(unlink(lua_writer), add = TRUE)

    # determine whether we need to run citeproc
    run_citeproc <- citeproc_required(metadata, read_utf8(input_file))

    # write settings to file
    settings <- c()
    add_setting <- function(name, value) {
      settings <<- c(settings, paste("local", name, "=",
                                    ifelse(value, "true", "false")))
    }
    add_setting("fig_caption", fig_caption)
    add_setting("incremental", incremental)
    add_setting("smaller", smaller)
    add_setting("smart", smart)
    add_setting("mathjax", !is.null(mathjax))

    # Set level of slide header (used by ioslides_presentation.lua)
    settings <- c(settings, sprintf("local slide_level = %s", slide_level))
    write_utf8(settings, lua_writer)

    # For consistency add as pandoc argument
    args <- c(args, "--slide-level", as.character(slide_level))

    # append main body of script
    file.append(lua_writer,
                pkg_file("rmd/ioslides/ioslides_presentation.lua"))

    output_tmpfile <- tempfile("ioslides-output", fileext = ".html")
    on.exit(unlink(output_tmpfile), add = TRUE)

    # on Windows, cache the current codepage and set it to 65001 (UTF-8) for the
    # duration of the Pandoc command. Without this, Pandoc fails when attempting
    # to hand UTF-8 encoded non-ASCII characters over to the custom Lua writer.
    # See https://github.com/rstudio/rmarkdown/issues/134
    if (is_windows()) {
      # 'chcp' returns e.g., "Active code page: 437"; strip characters and parse
      # the number
      codepage <- as.numeric(gsub("\\D", "", system2("chcp", stdout = TRUE)))

      if (!is.na(codepage)) {
        # if we got a valid codepage, restore it on exit
        on.exit(system2("chcp", args = codepage, stdout = TRUE), add = TRUE)

        # change to the UTF-8 codepage
        system2("chcp", args = 65001, stdout = TRUE)
      }
    }

    pandoc_convert(input = input_file,
                   to = relative_to(dirname(input_file), lua_writer),
                   from = from_rmarkdown(fig_caption),
                   output = output_tmpfile,
                   options = args,
                   citeproc = run_citeproc,
                   verbose = verbose)

    # read the slides
    slides_lines <- read_utf8(output_tmpfile)

    # base64 encode if needed
    if (self_contained) {
      slides_lines <- base64_encode_images(slides_lines)
    }

    # read the output file
    output_lines <- read_utf8(output_file)

    # substitute slides for the sentinel line
    sentinel_line <- grep("^RENDERED_SLIDES$", output_lines)
    if (length(sentinel_line) == 1) {
      preface_lines <- c(output_lines[1:sentinel_line[1] - 1])
      suffix_lines <- c(output_lines[-(1:sentinel_line[1])])
      output_lines <- c(preface_lines, slides_lines, suffix_lines)
      write_utf8(output_lines, output_file)
    } else {
      stop("Slides placeholder not found in slides HTML", call. = FALSE)
    }

    output_file
  }

  # return format
  output_format(
    knitr = knitr_options_html(fig_width, fig_height, fig_retina, keep_md, dev),
    pandoc = pandoc_options(to = "html",
                            from = from_rmarkdown(fig_caption, md_extensions),
                            args = args),
    keep_md = keep_md,
    clean_supporting = self_contained,
    df_print = df_print,
    pre_processor = pre_processor,
    post_processor = post_processor,
    base_format = html_document_base(lib_dir = lib_dir,
                                     self_contained = self_contained,
                                     mathjax = mathjax,
                                     pandoc_args = pandoc_args,
                                     extra_dependencies = extra_dependencies,
                                     bootstrap_compatible = TRUE, ...))
}


html_dependency_ioslides <- function() {
  htmlDependency(
    name = "ioslides",
    version = "13.5.1",
    src = pkg_file("rmd/ioslides/ioslides-13.5.1"),
    script = c(
      "js/modernizr.custom.45394.js",
      "js/prettify/prettify.js",
      "js/prettify/lang-r.js",
      "js/prettify/lang-yaml.js",
      "js/hammer.js",
      "js/slide-controller.js",
      "js/slide-deck.js"
    ),
    stylesheet = c(
      "fonts/fonts.css",
      "theme/css/default.css",
      "theme/css/phone.css")
    )
}
