#' Control interactive plot point events
#'
#' These functions give control over the `click`, `dblClick` and
#' `hover` events generated by [imageOutput()] and [plotOutput()].
#'
#' @param id Input value name. For example, if the value is `"plot_click"`,
#'   then the event data will be available as `input$plot_click`.
#' @param clip Should the click area be clipped to the plotting area? If
#'   `FALSE`, then the server will receive click events even when the mouse is
#'   outside the plotting area, as long as it is still inside the image.
#' @param delay For `dblClickOpts()`: the maximum delay (in ms) between a
#'   pair clicks for them to be counted as a double-click.
#'
#'   For `hoverOpts()`: how long to delay (in ms) when debouncing or throttling
#'   before sending the mouse location to the server.
#' @param delayType The type of algorithm for limiting the number of hover
#'   events. Use `"throttle"` to limit the number of hover events to one
#'   every `delay` milliseconds. Use `"debounce"` to suspend events
#'   while the cursor is moving, and wait until the cursor has been at rest for
#'   `delay` milliseconds before sending an event.
#' @seealso [brushOpts()] for brushing events.
#' @export
#' @keywords internal
clickOpts <- function(id, clip = TRUE) {
  if (is.null(id))
    stop("id must not be NULL")

  list(
    id = id,
    clip = clip
  )
}


#' @export
#' @rdname clickOpts
dblclickOpts <- function(id, clip = TRUE, delay = 400) {
  if (is.null(id))
    stop("id must not be NULL")

  list(
    id = id,
    clip = clip,
    delay = delay
  )
}

#' @param nullOutside If `TRUE` (the default), the value will be set to
#'   `NULL` when the mouse exits the plotting area. If `FALSE`, the
#'   value will stop changing when the cursor exits the plotting area.
#' @export
#' @rdname clickOpts
hoverOpts <- function(id, delay = 300,
                      delayType = c("debounce", "throttle"), clip = TRUE,
                      nullOutside = TRUE) {
  if (is.null(id))
    stop("id must not be NULL")

  list(
    id = id,
    delay = delay,
    delayType = match.arg(delayType),
    clip = clip,
    nullOutside = nullOutside
  )
}

#' Create an object representing brushing options
#'
#' This generates an object representing brushing options, to be passed as the
#' `brush` argument of [imageOutput()] or
#' [plotOutput()].
#'
#' @param id Input value name. For example, if the value is `"plot_brush"`,
#'   then the coordinates will be available as `input$plot_brush`. Multiple
#'   `imageOutput`/`plotOutput` calls may share the same `id`
#'   value; brushing one image or plot will cause any other brushes with the
#'   same `id` to disappear.
#' @param fill Fill color of the brush. If `'auto'`, it derives from the link
#'   color of the plot's HTML container (if **thematic** is enabled, and `accent`
#'   is a non-`'auto'` value, that color is used instead).
#' @param stroke Outline color of the brush. If `'auto'`, it derives from the
#'   foreground color of the plot's HTML container (if **thematic** is enabled,
#'   and `fg` is a non-`'auto'` value, that color is used instead).
#' @param opacity Opacity of the brush
#' @param delay How long to delay (in milliseconds) when debouncing or
#'   throttling, before sending the brush data to the server.
#' @param delayType The type of algorithm for limiting the number of brush
#'   events. Use `"throttle"` to limit the number of brush events to one
#'   every `delay` milliseconds. Use `"debounce"` to suspend events
#'   while the cursor is moving, and wait until the cursor has been at rest for
#'   `delay` milliseconds before sending an event.
#' @param clip Should the brush area be clipped to the plotting area? If FALSE,
#'   then the user will be able to brush outside the plotting area, as long as
#'   it is still inside the image.
#' @param direction The direction for brushing. If `"xy"`, the brush can be
#'   drawn and moved in both x and y directions. If `"x"`, or `"y"`,
#'   the brush wil work horizontally or vertically.
#' @param resetOnNew When a new image is sent to the browser (via
#'   [renderImage()]), should the brush be reset? The default,
#'   `FALSE`, is useful if you want to update the plot while keeping the
#'   brush. Using `TRUE` is useful if you want to clear the brush whenever
#'   the plot is updated.
#' @seealso [clickOpts()] for clicking events.
#' @export
brushOpts <- function(id, fill = "#9cf", stroke = "#036",
                      opacity = 0.25, delay = 300,
                      delayType = c("debounce", "throttle"), clip = TRUE,
                      direction = c("xy", "x", "y"),
                      resetOnNew = FALSE) {
  if (is.null(id))
    stop("id must not be NULL")

  if (identical(fill, "auto")) {
    fill <- getThematicOption("accent", "auto")
  }
  if (identical(stroke, "auto")) {
    stroke <- getThematicOption("fg", "auto")
  }

  list(
    id = id,
    fill = fill,
    stroke = stroke,
    opacity = opacity,
    delay = delay,
    delayType = match.arg(delayType),
    clip = clip,
    direction = match.arg(direction),
    resetOnNew = resetOnNew
  )
}

getThematicOption <- function(name = "", default = NULL, resolve = FALSE) {
  if (isNamespaceLoaded("thematic")) {
    # TODO: use :: once thematic is on CRAN
    tgo <- utils::getFromNamespace("thematic_get_option", "thematic")
    tgo(name = name, default = default, resolve = resolve)
  } else {
    default
  }
}
