
  # -------------------------------------------------------------------------------------------------------------------------  #
  #  -----------------------------------------------------------------------------------------------------------------------   #
  #                                                                                                                            #
  #           File Name              :  Inspect.r                                                                              #
  #           Last Updated Funclist  :  08 Feb 2015,  5:12 AM (Sunday)                                                         #
  #                                                                                                                            #
  #           Author Name            :  Rick Saporta                                                                           #
  #           Author Email           :  RickSaporta@gmail.com                                                                  #
  #           Author URL             :  www.github.com/rsaporta                                                                #
  #                                                                                                                            #
  #           Packages Called        :  stringr                                                                                #
  #           Packages Used via NS   :  NA                                                                                     #
  #                                                                                                                            #
  #  -----------------------------------------------------------------------------------------------------------------------   #
  #                                                                                                                            #
  #   parseInspectLine1  ( line, nms )                                                                                         #
  #   Inspect            ( x, linesToOutput=3, cropInfo=TRUE, simple=TRUE, inspect.max.length=7, dontIterate=FALSE             #
  #                        , pos=1, en=parent.frame(), silent=FALSE )                                                          #
  #   Inspectall         ( ... )                                                                                               #
  #                                                                                                                            #
  #                                                                                                                            #
  #                                                     <END FUNCS>                                                            #
  #  -----------------------------------------------------------------------------------------------------------------------   #
  # -------------------------------------------------------------------------------------------------------------------------  #

# Inspect.r

parseInspectLine1 <- function(line, nms) {
  require(stringr)
  # grab relevant info
  L1 <- str_trim(line[[1]])
  mem <- substr(L1, 1, str_locate(L1, " ")[, "start"]-1)
  
  len  <- as.numeric(str_extract(str_extract(L1, "len=\\d+"), "\\d+"))
  tlen <- as.numeric(str_extract(str_extract(L1, "tl=\\d+"), "\\d+"))
  
  # check the names
  if (missing(nms) || length(nms) != 1)
      nms <- paste0(as.expression(substitute(x)), collapse="")

  return(data.table(mem=mem, len=len, tlen=tlen, obj=nms))
}


Inspect <- function(x, linesToOutput=3, cropInfo=TRUE, simple=TRUE, inspect.max.length=7, dontIterate=FALSE, pos=1, en=parent.frame(), silent=FALSE) {

##  Outputs helpful memory information, generally used with data.tables
##  Returns the full output of `.Internal(inspect(x, max.length=inspect.max.length))`
##    as a single, collapsed string. 
##
##  cropInfo : Whether or not to DISPLAY the "lines cropped" information.
##             If FALSE, will not display how many lines were omitted
##             This is useful in interactive, when comparing two objects, eg: 
##             { Inspect(DT1, cropInfo=FALSE); Inspect(DT2, cropInfo=FALSE) }
##  dontIterate : Useuful for when x is a list and we want to inspect the list itself, not its elements
##                If FALSE (default) will iterate over the elements of X when X is a list. 
## Wrapper function to .Internal(inspect(x)) usually called on a data.table (or parttherof)
##   to see whats going on with modify in place
##
## 

  if (!cropInfo && missing(linesToOutput))
    linesToOutput <- 1

  CO <-  eval(capture.output(.Internal(inspect(x, max.length=inspect.max.length))), envir=en)
  ret <- paste(CO, collapse="\n")

  ## Gather the names.  Generally, just substitute(x), however, x might be list(DT1, DT2), in which case, we want to drop "list"
  nms <- as.character(substitute(x))
  if (is.call(match.call()[["x"]]) )
    nms <- nms[-1]

  ## If x is a list, iterate
  if (inherits(x, "list")) {
    warning("x is a list, and will be treated as a single object.\nFunction `Inspect` does not vectorize well.\n\nDid you mean to use `Inspectall(...)` instead?\n")
    # todo:     ret <- lapply(x, Inspect, 
    # todo:                       linesToOutput=linesToOutput, cropInfo=cropInfo, simple=simple, inspect.max.length=inspect.max.length, en=en)
    # todo:     if (!simple)
    # todo:       return(invisible(ret))
    # todo:     ret <- rbindlist(ret)[, x := nms]
    # todo:     groupingCodes <- lapply(seq_along(x), pasteR, x="#")
    # todo:    # browser()
    # todo:     ## add an indicator showing when elements share the same memory
    # todo:     ret[, sameMem := ifelse(.N>1, groupingCodes[[.Expr]], ""), by=mem]
    # todo:     return(ret)
  }


  if (simple)
    return(parseInspectLine1(line=CO[[1]], nms=nms))
  # ELSE

  if (linesToOutput + 1 <= length(CO))
    CO <- c(CO[seq(linesToOutput)], if (cropInfo) c(paste0("     --- < ", length(CO)-linesToOutput ," more lines cropped > --- ")))

  if (!silent)
    cat(paste(CO, collapse="\n"), "\n")

  return(invisible(ret))
}


Inspectall <- function(...) {
  L <- length(list(...))
  ret <- list()

  for (i in seq(L)) {
    obj.nm <- as.list(substitute(list(...)))[-1] [[i]]
    ret[[i]] <- capture.output( 
                  .Internal(inspect(
                      eval(obj.nm, envir=parent.frame(1))
                     # get(as.character(
                     #       as.list(substitute(list(...)))[-1]
                     #              [[i]]
                     #  ), envir=parent.frame(1))
                    )) # // closing .Inte(insp( . )) 
                  )[[1]]
  }
  
  # Combine results into a single DT
  ret <- rbindlist(lapply(ret, parseInspectLine1))

  ## Make fancy
  # adding names
  nms <- as.character(as.list(substitute(list(...)))[-1L])
  ret[, obj := nms]

  # Add similarity indicators
  # note that the grouping codes have no actual significance. Just used as a quick visual to see if two objects have the same memory.  
  groupingCodes <- c(" (✓) ", " o_o ", " +++ ", " .~. ", " :#: ", " xxx ", " --- ") 
  if (L > length(groupingCodes))
   groupingCodes <- c(groupingCodes, lapply(seq_along(x), pasteR, x="#"))
  ret[, sameMem := ifelse(.N>1, groupingCodes[[.Expr]], ""), by=mem]

  ## add dim info
  rows <- lapply(list(...), nrow)
  cols <- lapply(list(...), ncol)

  ret[, c("rows", "cols") := list(rows, cols)]

  ret
}

# ----------------------- END OF INSPECT FUNCTIONS ----------------------- #
