
  # -------------------------------------------------------------------------------------------------------------------------  #
  #  -----------------------------------------------------------------------------------------------------------------------   #
  #                                                                                                                            #
  #           File Name              :  findAllDTsInFile.r                                                                     #
  #           Last Updated Funclist  :  19 Feb 2015, 12:52 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                                                                                     #
  #                                                                                                                            #
  #  -----------------------------------------------------------------------------------------------------------------------   #
  #                                                                                                                            #
  #   closeTheBrackets   ( raw, matches, pat="\\n", brackets=c(`@`="@", `{`="}", `(`=")", `[`="]"), verbose=FALSE )            #
  #   matchesInRange     ( counts, min=-Inf, max=Inf )                                                                         #
  #   findAllDTsInFile   ( f, pat.DT.nm="\\bDT[\\.|_|[:alnum:]]+?", pat.assign_value="( |\\t)*<-(.)*?(\\n|$)"                  #
  #                        , remove.comments=TRUE, path.to.remove=srcDir, verbose=FALSE )                                      #
  #                                                                                                                            #
  #                                                                                                                            #
  #                                                     <END FUNCS>                                                            #
  #  -----------------------------------------------------------------------------------------------------------------------   #
  # -------------------------------------------------------------------------------------------------------------------------  #

## findAllDTsInFile.r

closeTheBrackets <- function(raw, matches, pat="\\n", brackets=c("@"="@", "{"="}", "("=")", "["="]"), verbose=FALSE) {
## returns an updated matches, where the match.length attribute is extended to close the bracket

  if (is.list(matches)) {
    return(lapply(matches, closeTheBrackets, raw=raw, pat=pat, brackets=brackets, verbose=verbose))
  }

  openers <- escapeRegEx(names(brackets))
  closers <- escapeRegEx(brackets)

  ## find the locations of pat, openers, closers
  pat_locs    <- gregexpr(pat=pat, raw)[[1]]
  open_count  <- sapply(openers, gregexpr, text=raw)
  close_count <- sapply(closers, gregexpr, text=raw)

  matchesInRange <- function(counts, min=-Inf, max=Inf) {
    if (is.list(counts))
      sapply(counts, function(ct) ct[ct>= min & ct <= max])
    else 
      counts[counts >= min & counts <= max]
  }

  ml <- attr(matches, "match.length")

  ## DEBUG
  browser(expr=inDebugMode("brackets"), text="in closeTheBrackets, before for loop. check ml.")

  for (i in seq_along(matches)) {
    ## Initialize for while loop
    AreAllClosed <- FALSE
    
    while (any(!AreAllClosed) && is.finite(ml[[i]]) ) {
        m.s <- matches[i]     ## match.start
        m.e <- m.s + ml[[i]] - 1  ## match.end
        s   <- substr(raw, m.s, m.e)

        ## The length of open and close counts should match for the given min/max range
        AreAllClosed <- mapply(function(st, en) length(st) == length(en) && if (length(st)) all(st < en) else TRUE
                                  , matchesInRange(open_count,  m.s, m.e)
                                  , matchesInRange(close_count, m.s, m.e)
                              )

        if (!(all(AreAllClosed)))
           ml[[i]] <-  min(pat_locs[pat_locs > m.e]) - (m.s) + 1
        
    } ## // end while-loop

  } ## // End for-loop

  ## put the lengths attribute back
  if (verbose && !identical(ml, attr(matches, "match.length"))) {
    cat("original lengths were:\n")
    print(attr(matches, "match.length"))
    cat("new lengths are:\n")
    print(ml)
  }

  attr(matches, "match.length") <- ml

  return(matches)
}

findAllDTsInFile <- function(f, pat.DT.nm="\\bDT[\\.|_|[:alnum:]]+?", pat.assign_value="( |\\t)*<-(.)*?(\\n|$)", remove.comments=TRUE, path.to.remove=srcDir, verbose=FALSE) {

  if (length(f) > 1) {
    args <- as.list(match.call()) [-1L]
    args <- args[names(args) != "f"]
    return(rbindlist(lapply(f, function(.f) do.call(findAllDTsInFile,  c(list(f=.f), args)))))
  }


  f <- path.expand(f)

  ## Dont let a missing srcDir cause the script to fail
  if (isErr(path.to.remove))
    path.to.remove <- ""
  else 
    path.to.remove <- path.expand(path.to.remove)

  rawfile <- readLines(f, warn=FALSE)

  if (remove.comments) 
    rawfile <- gsub("#.*$", "", rawfile)

  ## remove the nothing-but-whitespace
  rawfile <- gsub("^\\s+$", "", rawfile)
  ## remove blank lines
  rawfile <- rawfile[rawfile != ""]

  ## FLATTEN
  rawfile_flat <- pasteC(rawfile, C="\n")

  ## FIND matches
  matches <- gregexpr(pattern=paste0(pat.DT.nm, pat.assign_value), rawfile_flat)

  ## DEBUG 
  browser(expr=inDebugMode("find"), text="in findAllDTsInFile, after taking matches, before cleaning matches for brackets")


  ## Check for brackets spanning more than one line
  matches <- closeTheBrackets(raw=rawfile_flat, matches=matches)
  ## CONFIRM: Changes should not need to take place a second time ... unless doesnt close at all
  stopifnot(identical(matches, closeTheBrackets(rawfile_flat, matches, verbose=FALSE)))

  ## extract
  extracted_DT_strings <- regmatches(rawfile_flat, matches)[[1]]

  ## cleanup
  extracted_DT_strings <- trim(gsub("\\s+", " ", gsub("\n", " ", extracted_DT_strings)))

  ## pull out the just string name
  names_DT <- gsub(sprintf("^(%s)(\\s*<-.*)", pat.DT.nm), "\\1", extracted_DT_strings)
  # names_DT <- trim(names_DT)

  ## ALTERNATE
  splat <- strsplit(extracted_DT_strings, "\\s*<-\\s*")
  ret   <- as.data.table(do.call(rbind, splat))

  ## If no DTs found, it will be empty
  if (!nrow(ret)) {
    ret <- data.table(DT=NA_character_, definition=NA_character_, ordering=NA_integer_)
  } else {
    setnames(ret, c("DT", "definition"))
    ret[, ordering := seq(.N)]  
  }

  ## Add in file info
  ret[, file := gsub(paste0("^", path.to.remove, "/?"), "", f)]

  return(ret)
}

