  # -------------------------------------------------------------------------------------------------------------------------  #
  #  -----------------------------------------------------------------------------------------------------------------------   #
  #                                                                                                                            #
  #           File Name              :  ggBoxplotWithDots.r                                                                    #
  #           Last Updated Funclist  :  19 Feb 2015,  1:05 PM (Thursday)                                                       #
  #                                                                                                                            #
  #           Author Name            :  Rick Saporta                                                                           #
  #           Author Email           :  RickSaporta@gmail.com                                                                  #
  #           Author URL             :  www.github.com/rsaporta                                                                #
  #                                                                                                                            #
  #           Packages Called        :  NA                                                                                     #
  #           Packages Used via NS   :  NA                                                                                     #
  #                                                                                                                            #
  #  -----------------------------------------------------------------------------------------------------------------------   #
  #                                                                                                                            #
  #   ggBoxplotWithDots  ( DT, x, y, color=NULL, outliers=FALSE, colDotgroup, values.colDotgroup=NULL                          #
  #                        , values.otherDotgroup=NULL, dots_size=1, dots_color=colDotgroup, dots_alpha=0.8                    #
  #                        , others_alpha=dots_alpha, allOthers=TRUE, others_color="#444444", jwidth=0.025                     #
  #                        , safety.x_length=20, title=NULL, ... )                                                             #
  #                                                                                                                            #
  #                                                                                                                            #
  #                                                     <END FUNCS>                                                            #
  #  -----------------------------------------------------------------------------------------------------------------------   #
  # -------------------------------------------------------------------------------------------------------------------------  #

# ggBoxplotWithDots.r

ggBoxplotWithDots <- function(DT, x, y
  , color = NULL
  , outliers = FALSE
  , notch = FALSE

  ## dots arguments
  , colDotgroup = NULL
  , values.colDotgroup = NULL
  , values.otherDotgroup = NULL
  , dots_size  = 1
  , dots_size_scale_in_group = 1.2 ## Only applies to values in values.colDotgroup  (or all, if none are in "other")
  , dots_color = colDotgroup
  , dots_alpha = 0.8
  , dots_on = TRUE
  , violin_on = FALSE
  , others_alpha = dots_alpha
  , allOthers=TRUE
  , others_color="#444444"
  , others_text_value="All Others"
  , jwidth = 0.04
  , jheight = NULL
  , safety.x_length = 20
  , title = NULL
  , ...
  ) {

  lib(ggplot, quiet=TRUE)

  ## GENERIC ERROR TEST
  gg_confirm_aes_values(DT, c("x", "y", "color"))

  if ("dotsize" %in% names(list(...)))
    warning ("Did you mean to use argument 'dots_size' ?", call.=FALSE)

  if (isTRUE(outliers))
    outliers <- dots_size

  ## Check how many elements along the x axis
  if (lunique(DT[[x]]) > safety.x_length)
    stop ("There are more than ", safety.x_length, " unique values for ", x, "\n      HINT: Try using aggregateDT()", call.=FALSE)

  ## 2015-12-05.  I believe this must be TRUE.  If this safetu fails with older scripts, perhaps it is wrong
  stopifnot(colDotgroup %in% names(DT))

  ## Base function 
  P <- ggplot(data=DT) + aes_string(x = x, y=y)

  if (violin_on)
    P <- P + geom_violin(outlier.size=outliers, notch=notch)
  else 
    P <- P + geom_boxplot(outlier.size=outliers, notch=notch)

  ## --------------------------- ##
  ##            DOTS             ##
  ## --------------------------- ##
  if (dots_on)
  ## if not specified, dots are black
  if (is.null(colDotgroup)) {
    # tmp.color <- ifelseNULL(dots_color, yes="black", no=dots_color)
    P <- P + geom_point(position=position_jitter(jwidth), color=ifelseNULL(dots_color, yes="black", no=dots_color), alpha=dots_alpha, size=dots_size)
  } else {

      ## --- first do All Others --- ## 
      if (!is.null(values.colDotgroup)) {
        filter_DT <- ifelseNULL(values.colDotgroup, "", sprintf("[%s %s c%s]", colDotgroup, "%ni%", pasteQ(escapeSpecialChars(values.colDotgroup, "'"))))
        ## OLD: filter_DT <- ifelseNULL(values.colDotgroup, "", sprintf("[%s %s %s]", colDotgroup, "%ni%", values.colDotgroup))
        color_string <- ifelseNULL(others_color, "", {r <-sprintf("color = '%s'", others_color); ifelse(others_color %in% names(DT), sprintf(", aes_string(%s)", r), sprintf(", %s", r))})
        size_string  <- ifelseNULL(dots_size, "",    {r <-sprintf("size  = %s", dots_size);    ifelse(dots_size    %in% names(DT), sprintf(", aes(%s)", r), sprintf(", %s", r))})
        alpha_string <- sprintf(", alpha=%.03f", others_alpha)
        point.frmt   <- "geom_point(data=DT%s %s %s %s, position=position_jitter(width = %.04f, height=jheight))"
        point.expr_string <- sprintf(point.frmt, filter_DT, color_string, size_string, alpha_string, jwidth)
        # cat("    --- values.colDotgroup  geom_point function call --- \n", point.expr_string, "\n")
        P <- P + eval(parse(text=point.expr_string))
        rm(r)
      }

      ## --- next do standard --- ## 
      filter_DT <- ifelseNULL(values.colDotgroup, "", sprintf("[%s %s c%s]", colDotgroup, "%in%", pasteQ(escapeSpecialChars(values.colDotgroup, "'"))))
      ## OLD: filter_DT <- ifelseNULL(values.colDotgroup, "", sprintf("[%s %s %s]", colDotgroup, "%in%", values.colDotgroup))
      color_string <- ifelseNULL(dots_color, "", {r <-sprintf("color = '%s'", dots_color); ifelse(dots_color %in% names(DT), sprintf(", aes_string(%s)", r), sprintf(", %s", r))})
      size_string  <- ifelseNULL(dots_size, "",  {r <-sprintf("size  = %s", dots_size * dots_size_scale_in_group);  ifelse(dots_size  %in% names(DT), sprintf(", aes(%s)", r), sprintf(", %s", r))})
      alpha_string <- sprintf(", alpha=%.03f", dots_alpha)
      point.frmt   <- "geom_point(data=DT%s %s %s %s, position=position_jitter(width = %.04f, height=jheight))"
      point.expr_string <- sprintf(point.frmt, filter_DT, color_string, size_string, alpha_string, jwidth)
      P <- P + eval(parse(text=point.expr_string))
      rm(r)
  
      ## ALTERNATIVE TO THE ABOVE BREAKDOWN
      ## This has the advantage that it lists "All Others"
      ## But cannot adjust size
      ## ---------------------------------------------------------------------
      if (FALSE) {
          DT.dots <- copy(DT)
          if (!is.null(values.colDotgroup)) {
              if (others_text_value %in% values.colDotgroup)
                stop("others_text_value cannot be a value in values.colDotgroup")
              if (others_text_value %in% DT[[colDotgroup]])
                warning("others_text_value is already a value in ", colDotgroup, " -- this might conflate results")

            DT.dots[get(colDotgroup) %ni% values.colDotgroup, (colDotgroup) := others_text_value]
            DT.dots[, colDotgroup := toFactorWithExpectedLevels(x=get(colDotgroup), levels=unique(c(values.colDotgroup, others_text_value)))]
          }
          P <- P + geom_point(data=DT.dots, aes_string(color=dots_color), size=dots_size, alpha=dots_alpha, position=position_jitter(width=jwidth))
      }
      ## ---------------------------------------------------------------------
  }
  ## --------------------------- ##

  ## TODO  2015-02-17
  ## Something is up with collectArgs() and atop() type functions.  Do not have time to look into it now
  ## workaround: Convert title to a captured.string which will be parsed
  if(is.call(title)) {
    title <- pasteC(capture.output(title), C=" ")
    setattr(title, "needs_parsing", TRUE)
  }

  ## Pass the rest to gg_GenericProcessing first checking for any unsued arguments
  if (length(wh.unsure <- setdiff(names(list(...)), names(formals(gg_GenericProcessing)) ))) {
      cleanargs <- {. %>% tolower %>% removeText("(\\s|\\.|_)", .)}
      args.thisfunction <- names(formals())
      matched <- pmatch(cleanargs(wh.unsure), cleanargs(args.thisfunction)) %>% {args.thisfunction[.]} %>% removeNA
      warning(warningCols("Not sure what to do with the argument ", wh.unsure), "\nHINT:  ", ifelse(length(matched), sprintf("Did you mean to use argument: %s", pasteQand(matched)), "Check the spelling"))
  }
  do.call(gg_GenericProcessing, collectArgs())
}

