#' Dumbbell charts
#'
#' The dumbbell geom is used to create dumbbell charts.
#'
#' Dumbbell dot plots — dot plots with two or more series of data — are an
#' alternative to the clustered bar chart or slope graph.
#'
#' @section Aesthetics:
#' \Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "segment")}
#' @inheritParams ggplot2::layer
#' @param na.rm If \code{FALSE} (the default), removes missing values with
#'    a warning.  If \code{TRUE} silently removes missing values.
#' @param ... other arguments passed on to \code{\link{layer}}. These are
#'   often aesthetics, used to set an aesthetic to a fixed value, like
#'   \code{color = "red"} or \code{size = 3}. They may also be parameters
#'   to the paired geom/stat.
#' @param size_x the size of the start point
#' @param colour_x the colour of the start point
#' @param size_xend the size of the end point
#' @param colour_xend the colour of the end point
#' @param dot_guide if \code{TRUE}, a leading dotted line will be placed before the left-most dumbbell point
#' @param dot_guide_size,dot_guide_colour singe-value aesthetics for \code{dot_guide}
#' @param position Position adjustment, either as a string, or the result of a
#'   call to a position adjustment function.
#' @inheritParams ggplot2::layer
#' @export
#' @examples
#' library(ggplot2)
#'
#' df <- data.frame(trt=LETTERS[1:5], l=c(20, 40, 10, 30, 50), r=c(70, 50, 30, 60, 80))
#'
#' ggplot(df, aes(y=trt, x=l, xend=r)) +
#'   geom_dumbbell(size=3, color="#e3e2e1",
#'                 colour_x = "#5b8124", colour_xend = "#bad744",
#'                 dot_guide=TRUE, dot_guide_size=0.25) +
#'   labs(x=NULL, y=NULL, title="ggplot2 geom_dumbbell with dot guide") +
#'   theme_minimal() +
#'   theme(panel.grid.major.x=element_line(size=0.05))
#'
#' ## with vertical dodging
#' df2 <- data.frame(trt = c(LETTERS[1:5], "D"),
#'                  l = c(20, 40, 10, 30, 50, 40),
#'                  r = c(70, 50, 30, 60, 80, 70))
#'
#' ggplot(df2, aes(y=trt, x=l, xend=r)) +
#'   geom_dumbbell(size=3, color="#e3e2e1",
#'                 colour_x = "#5b8124", colour_xend = "#bad744",
#'                 dot_guide=TRUE, dot_guide_size=0.25,
#'                 position=position_dodgev(height=0.4)) +
#'   labs(x=NULL, y=NULL, title="ggplot2 geom_dumbbell with dot guide") +
#'   theme_minimal() +
#'   theme(panel.grid.major.x=element_line(size=0.05))
geom_dumbbell <- function(mapping = NULL, data = NULL, ...,
                          colour_x = NULL, size_x = NULL,
                          colour_xend = NULL, size_xend = NULL,
                          dot_guide = FALSE, dot_guide_size = NULL,
                          dot_guide_colour = NULL,
                          na.rm = FALSE, show.legend = NA, inherit.aes = TRUE,
                          position = "identity") {

  layer(
    data = data,
    mapping = mapping,
    stat = "identity",
    geom = GeomDumbbell,
    position = position,
    show.legend = show.legend,
    inherit.aes = inherit.aes,
    params = list(
      na.rm = na.rm,
      colour_x = colour_x,
      size_x = size_x,
      colour_xend = colour_xend,
      size_xend = size_xend,
      dot_guide = dot_guide,
      dot_guide_size = dot_guide_size,
      dot_guide_colour = dot_guide_colour,
      ...
    )
  )
}

#' @rdname ggalt-ggproto
#' @format NULL
#' @usage NULL
#' @export
GeomDumbbell <- ggproto("GeomDumbbell", Geom,
  required_aes = c("x", "xend", "y"),
  non_missing_aes = c("size", "shape",
                      "colour_x", "size_x",
                      "colour_xend", "size_xend",
                      "dot_guide", "dot_guide_size", "dot_guide_colour"),
  default_aes = aes(
    shape = 19, colour = "black", size = 0.5, fill = NA,
    alpha = NA, stroke = 0.5
  ),

  setup_data = function(data, params) {
    transform(data, yend = y)
  },

  draw_group = function(data, panel_scales, coord,
                        colour_x = NULL, size_x = NULL,
                        colour_xend = NULL, size_xend = NULL,
                        dot_guide = NULL, dot_guide_size = NULL,
                        dot_guide_colour = NULL) {

    points.x <- data
    points.x$colour <- colour_x %||% data$colour
    points.x$xend <- NULL
    points.x$size <- size_x %||% (data$size * 1.2)

    points.xend <- data
    points.xend$x <- points.xend$xend
    points.xend$xend <- NULL
    points.xend$colour <- colour_xend %||% data$colour
    points.xend$size <- size_xend %||% (data$size * 1.25)

    dot_df <- data
    dot_df$xend <- ifelse(data$xend < data$x, data$xend, data$x)
    dot_df$x <- -Inf
    dot_df$linetype <- "11"
    dot_df$size <- dot_guide_size %||% (data$size * 0.5)
    dot_df$colour <- dot_guide_colour %||% "#5b5b5b"

    if (is.null(dot_guide) | !dot_guide) {

      gList(
        ggplot2::GeomSegment$draw_panel(data, panel_scales, coord),
        ggplot2::GeomPoint$draw_panel(points.x, panel_scales, coord),
        ggplot2::GeomPoint$draw_panel(points.xend, panel_scales, coord)
      )

    } else {

      gList(
        ggplot2::GeomSegment$draw_panel(dot_df, panel_scales, coord),
        ggplot2::GeomSegment$draw_panel(data, panel_scales, coord),
        ggplot2::GeomPoint$draw_panel(points.x, panel_scales, coord),
        ggplot2::GeomPoint$draw_panel(points.xend, panel_scales, coord)
      )

    }

  },

  draw_key = draw_key_point
)


