#' @title Add a bar to a map
#' 
#' @details
#' Correct some things in sbar from terra to add scale bar to plots.
#' 
#' @param bar_d integer, size of the bar in km.
#' @param bar_xy either a character or a vector giving the position of the bar in
#'  the plot.
#' @param bar_adj numeric of length 2, adjustment for text placement.
#' @param bar_lon numeric of length 1, may be adjusted for some plots with
#'  trial-and-error strategy.
#' @param ... some unused parameters.
#'
#' @return NULL.
#'
#' @export
#'
#' @encoding UTF-8
#' 
#' @examples
#'
#' \dontrun{
#' library(sf)
#' plot(so_ii_collectivity[0])
#' add_bar(bar_d = 10, bar_xy = c(3.55, 43.47), bar_adj = c(0.5, -1))
#' }

add_bar = function(
    bar_d = NULL,
    bar_xy = NULL,
    bar_adj = NULL,
    bar_lon = 0.4788987,
    ...
) {
    d = adjust_terra_scale(bar_d)
    xy = calculate_terra_xy(bar_xy)
    adj = bar_adj
    if (is.null(adj)) {
        # Magical adjustment when scope vary to have text readable
        # 0.4788987 should correspond to longitude range for so.ii scope
        adj = diff(graphics::par()$usr[3:4])
        adj = c(
            0.5,
            -1 * exp(bar_lon / adj - 1)^.024
        )
    }

    terra::sbar(
        d = d[1],
        xy = xy,
        type = "bar",
        below = "km",
        label = c(0, d[2]/2, d[2]),
        lonlat = FALSE,
        adj = adj,
        cex = .8
    )
}
        
adjust_terra_scale = function(d = NULL, lonlat = TRUE) {
    destpoint = function (p, d, b = 90, r = 6378137) {
        toRad = pi/180
        lon1 = p[, 1] * toRad
        lat1 = p[, 2] * toRad
        b = b * toRad
        lat2 = asin(sin(lat1) * cos(d/r) + cos(lat1) * sin(d/r) * 
            cos(b))
        lon2 = lon1 + atan2(sin(b) * sin(d/r) * cos(lat1), cos(d/r) - 
            sin(lat1) * sin(lat2))
        lon2 = (lon2 + pi)%%(2 * pi) - pi
        cbind(lon2, lat2)/toRad
    }

    graph_box = graphics::par()[["usr"]]
    if (lonlat) {
        lat = mean(graph_box[3:4])
        if (is.null(d)) {
            dx = (graph_box[2] - graph_box[1])/6
            d = as.vector(terra::distance(cbind(0, lat), cbind(dx, 
                lat), TRUE))
            d = max(1, 5 * round(d/5000))
        }
        p = cbind(0, lat)
        dd = destpoint(p, d * 1000)[1, 1]
    }
    else {
        if (is.null(d)) {
            d = (graph_box[2] - graph_box[1])/6
            digits = floor(log10(d)) + 1
            d = round(d, -(digits - 1))
        }
        dd = d
    }
    return(c(dd, d))
}

calculate_terra_xy = function (xy, dx = 0, dy = 0, defpos = "bottomleft")
{
    graph_box = graphics::par()[["usr"]]
    if (is.null(xy)) {
        xy = defpos
    }
    if (!is.character(xy)) {
        return(cbind(xy[1], xy[2]))
    }
    xy = tolower(xy)
    parrange = c(graph_box[2] - graph_box[1], graph_box[4] - graph_box[3])
    pad = c(5, 5)/100
    if (xy == "bottom") {
        xy = c(graph_box[1] + 0.5 * parrange[1] - 0.5 * dx, graph_box[3] + 
            (pad[2] * parrange[2])) + c(0, dy)
    }
    else if (xy == "bottomleft") {
        xy = c(graph_box[1] + (pad[1] * parrange[1]), graph_box[3] + 
            (pad[2] * parrange[2])) + c(0, dy)
    }
    else if (xy == "bottomright") {
        xy = c(graph_box[2] - (pad[1] * parrange[1]), graph_box[3] + 
            (pad[2] * parrange[2])) - c(dx, -dy)
    }
    else if (xy == "topright") {
        xy = c(graph_box[2] - (pad[1] * parrange[1]), graph_box[4] - 
            (pad[2] * parrange[2])) - c(dx, dy)
    }
    else if (xy == "top") {
        xy = c(graph_box[1] + 0.5 * parrange[1] - 0.5 * dx, graph_box[4] - 
            (pad[2] * parrange[2])) - c(0, dy)
    }
    else if (xy == "topleft") {
        xy = c(graph_box[1] + (pad[1] * parrange[1]), graph_box[4] - 
            (pad[2] * parrange[2])) - c(0, dy)
    }
    else if (xy == "left") {
        xy = c(graph_box[1] + (pad[1] * parrange[1]), graph_box[3] + 
            0.5 * parrange[2] - 0.5 * dy)
    }
    else if (xy == "right") {
        xy = c(graph_box[2] - (pad[1] * parrange[1]) - dx, graph_box[3] + 
            0.5 * parrange[2] - 0.5 * dy)
    }
    else {
        stop("xy must be a coordinate pair (two numbers) or one of \"bottomleft\", \"bottom\", \"bottomright\", topleft\", \"top\", \"topright\"")
    }
    xy
}