#' Find Functions used in a Function or Functions in a List of Function Names
#'
#' @param func A function object (no quote) or
#' a list/vector of the function object names.
#' @param nonative = 0 to print the R native functions.
#' @param noglobal = 1 not to print the functions defined in the global env.
#' @param sort = 0 not to sort the result
#' @param unlist = 0 to output the result in a list when func is a
#'  list or vector.
#' @param packages A character vector containing the names of the packages.
#' \cr The functions included in those packages will not be printed.
#'
#' @details
#' \cr
#' This function  requires "codetools" package.
#' \cr
#'
#' @examples
#' \dontrun{
#' library(codetools)
#' library(lazy.tools)
#'
#' find_functions( is.varname )
#'
#' res1 <- find_functions( ls("package:lazy.tools") )
#' res2 <- find_functions( ls("package:lazy.tools"), unlist=0 )
#' res3 <- find_functions(
#'          ls("package:lazy.tools"), unlist=0, packages="lazy.tools" )
#' Print(res1,res2,res3)
#'
#' # funclist <- ls("package:lazy.tools")
#' # res <- sort( unique( unlist( lapply( funclist, find_functions ) ) ) )
#' # res <- unique( unlist( lapply(
#'             funclist, find_functions, packages="lazy.tools" )
#'              ) )
#' }
#'
#' @return
#' A character vector containing the function names.
#'
#' @references
#' The answer by kohske (answered Aug 9 '12 at 7:51) at: \cr\cr
#'   http://stackoverflow.com/questions/11872879/
#'    finding-out-which-functions-are-called-within-a-given-function
#'
#' @export
#'

find_functions <- function( func, nonative=1, noglobal=0
                            , sort=1, packages=NULL, unlist=1 ) {
 # Find all the functions called from a function
 # Shin-ichi Mayekawa
 # 20161106c,07
 # list or vector input: 20161119,20,21
 # roxgen2: 20180203
 #
 # This is the modified version
 # of the the code posted by kohske (answered Aug 9 '12 at 7:51) at: \cr\cr
 #  http://stackoverflow.com/questions/11872879/
 #        finding-out-which-functions-are-called-within-a-given-function
 #

 # library(codetools)
 #

 # recursive call
 if( length(func) > 1 ){

  # func is a list or vector
  res=lapply( func, find_functions, unlist=1
      , nonative=nonative, noglobal=noglobal, packages=packages )
  names(res)=func

  # remove charactre(0)
  loc=which( unlist(lapply( res, length )) == 0 |
              unlist( lapply( res, is.null ) ) )
  if( length(loc) > 0 ) res=res[-loc]

  if( length(res) == 0 ) return( NULL )

  # sort and unlist
  if( sort ) res=lapply( res, sort )
  if( unlist ){
   res=unique( unlist( res ) )
   if( sort ) res=sort(res)
  }
  return( res )

 } # end of list/vector input


 # unlist
 if( is.list(func) ) func=unlist(func)

 # check if func is a func.
 if( mode(func) != "function" ){
  # func is not a function
  if( mode(func) %in% c("numeric","logical","list") ){
   # func is not a character
   return( NULL )
  }
  else{
   # func is a character
   if( !exists( func, envir=parent.frame() ) ){
    # no object named func
    return( NULL )
   }
   # an object named func exists
   func=get( func, envir=parent.frame() )
   if( mode(func) != "function" ){
    # object named func is not a function
    return( NULL )
   }
  }
 } # end of mode(func) != "function"

 # Here, func contains the body of a function


 ##### coded by kohske starts here. #####
 leaf <- function (e, w) {
  r <- try(eval(e), silent = TRUE)
  if(!is.null(r) && is.function(r)) ret <<- c(ret, as.character(e))
 }
 call <- function (e, w) {
  walkCode(e[[1]], w)
  for (a in as.list(e[-1])) if (!missing(a)) walkCode(a, w)
 }
 ret <- c()
 walkCode(body(func), makeCodeWalker(call = call, leaf = leaf, write = cat))
 res=unique(ret)
 ##### coded by kohske ends here. #####


 # locations (packages) to exclude
 exclude_loc_list=NULL
 if( nonative ){
  exclude_loc_list=c( "package:base",  "package:stats", "package:graphics"
              , "package:grDevices", "package:utils", "package:datasets"
              , "package:methods" )
 }
 if( noglobal == 1 ) exclude_loc_list=c(exclude_loc_list,".GlobalEnv")
 if( !is.null(packages) ){
  exclude_loc_list=c(exclude_loc_list, paste("package:",packages,sep=""))
 }

 # list of functions to be excluded defined in exclude_loc_list
 exclude_func_list=lapply( exclude_loc_list, ls )
 names(exclude_func_list)=exclude_loc_list
 exclude_func_list=unlist(exclude_func_list)

 # exclude
 res=setdiff( res, exclude_func_list )

 # null?
 if( length( res) == 0 ) return( NULL )

 # sort
 if( sort ) res=sort(res)
 names(res)=NULL

 return( res )

} # end of find_functions



