##' bar of range (HPD, range etc) to present uncertainty of evolutionary inference
##'
##'
##' @title geom_range
##' @param range range, e.g. "height_0.95_HPD"
##' @param center center of the range, mean, median or auto (default, the center of the range)
##' @param ... additional parameter, e.g. color, size, alpha
##' @return ggplot layer
##' @importFrom ggplot2 aes_string
##' @export
##' @author Guangchuang Yu
geom_range <- function(range, center = "auto", ...) {
    structure(list(range = range, center = center, ...), class = "geom_range")
}


geom_range_internal <- function(range, center, ...) {
    position = "identity"
    show.legend = NA
    na.rm = TRUE
    inherit.aes = FALSE

    default_aes <- aes_(x=~x, y=~y, xend=~x, yend=~y)

    lower <- paste0('range_lower(', range, ')')
    upper <- paste0('range_upper(', range, ')')
    if (center == "auto") {
        center <- paste0('range_center(', range, ')')
    }


    mapping <- modifyList(default_aes, aes_string(center=center, lower=lower, upper=upper))

    layer(
        stat = StatRange,
        mapping = mapping,
        data = NULL,
        geom = GeomSegment,
        position = position,
        show.legend=show.legend,
        inherit.aes = inherit.aes,
        params = list(na.rm = na.rm,
                      ...),
        check.aes = FALSE
    )

}



StatRange <- ggproto("StatRange", Stat,
                     compute_group = function(self, data, scales, params) {
                         df <- data[!is.na(data[["lower"]]),]
                         df[["lower"]] <- df[["lower"]] + df[["x"]] - as.numeric(df[["center"]])
                         df[["upper"]] <- df[["upper"]] + df[["x"]] - as.numeric(df[["center"]])

                         data.frame(x = df[["lower"]],
                                    xend = df[["upper"]],
                                    y = df[["y"]],
                                    yend = df[["y"]])
                     },
                     required_aes = c("x", "y", "xend", "yend")
                     )

range_center <- function(range) {
    vapply(range, function(x) {
        if (length(x) == 0) 
            return(NA) 
        if (length(x) == 1 && is.na(x))
            return(NA)
        (as.numeric(x[1]) + as.numeric(x[2]))/2
    }, numeric(1))
}

range_lower <- function(range) {
    vapply(range, function(x) {
        ## length(x) == 0 for x is NULL
        ## see https://groups.google.com/d/msg/bioc-ggtree/yNzjtioVVGU/MCh3MPl_CwAJ
        if (length(x) == 0)
            return(NA)
        as.numeric(x[1])
    }, numeric(1))
}

range_upper <- function(range) {
    vapply(range, function(x) {
        if (length(x) == 0) 
            return(NA) 
        if (length(x) == 1 && is.na(x))
            return(NA)
        as.numeric(x[2])
    }, numeric(1))
}