*! version 1.2 20240222 - DIME Analytics & LSMS Team, The World Bank - dimeanalytics@worldbank.org, lsms@worldbank.org

cap program drop   reproot
    program define reproot, rclass

qui {

    version 14.1

    * Update the syntax. This is only a placeholder to make the command run
    syntax , Project(string) Roots(string) [prefix(string) clear]

    noi di _n "{hline}"

    * initiate locals
    local tot_time 0
    local tot_dirs 0
    local rootfiles ""

    local env_file  "~/reproot-env.yaml"
    local root_file "reproot.yaml"

    /***************************************************
      Test if all roots are already loaded
    ***************************************************/

    local roots_set ""
    local roots_notset ""

    * If clear is used, then add all roots to roots_notset,
    * and search for all of them again
    if !missing("`clear'") {
      local roots_notset "`roots'"
    }

    * If clear is not used, test what root globals are already set,
    * and search only for roots not already set in root globals
    else {
        * Test which roots if any are already loaded
      foreach root of local roots {
        * Test if root exists with prefix
        if missing("${`prefix'`root'}") {
          local roots_notset : list roots_notset | root
        }
        else local roots_set : list roots_set | root
      }
    }

    /***************************************************
      Output any roots are already set
    ***************************************************/

    if !missing("`roots_set'") {
      noi di as text _n "{pstd}These roots were already set in these globals:{p_end}"
      foreach root of local roots_set {
        local prefix_root "`prefix'`root'"
        noi di as text "{phang2}- Global: {result:`prefix_root'} - Root: {result:${`prefix_root'}}{p_end}"
      }
    }

    /***************************************************
      Output if all roots are already set
    ***************************************************/

    if missing("`roots_notset'") {
      noi di as result _n "{pstd}All required roots are already loaded. No search for roots will be done.{p_end}" _n _n "{hline}"

      ** The command ends here
    }

    * There are roots to search for
    else {

    /***************************************************
      Output that at least some roots were not loaded
    ***************************************************/

      noi di as text _n "{pstd}These required roots were not already loaded:{p_end}"
      foreach root of local roots_notset {
        noi di as text  "{pmore}- {bf:`root'}{p_end}"
      }
      noi di as text _n "{pstd}Starting search of file system.{p_end}" _n

      /***************************************************
        Read env file before search
      ***************************************************/

      * Get home dir
      local pwd = "`c(pwd)'"
      cd ~
      local homedir = "`c(pwd)'"
      cd "`pwd'"

      * Test if this location has a root file
      cap confirm file "`env_file'"
      if (_rc) {
        noi di as text `"{phang}No file {inp:reproot-env.yaml} found in home directory {it:`homedir'}. This file is required to set up once per computer to use {cmd:reproot}. See instructions on how to set up this file {browse "https://dime-worldbank.github.io/repkit/articles/reproot-files.html":here}.{p_end}"' _n
        error 601
        exit
      }

      * Get reprootpaths and skipdirs from env file
      reproot_parse env , file("`env_file'")
      local envpaths `"`r(envpaths)'"'
      local skipdirs `"`r(skipdirs)'"'

      /***************************************************
        Search each reprootpaths
      ***************************************************/

      foreach envpath of local envpaths {

        noi di as smcl  `"{hline}"' _n

        * Parse max recursion and search path from reprootpath
        gettoken maxrecs search_path : envpath, parse(":")
        local search_path = substr("`search_path'",2,.)

        * Search next folder
        noi di as result  `"{pstd}{ul:Searching folder: `search_path', with folder depth: `maxrecs'}{p_end}"'
        noi reproot_search, ///
          path(`"`search_path'"') skipdirs(`"`skipdirs'"') recsleft(`maxrecs')

        * Get time, dir_count, and roots found
        local time = `r(timer)'
        local dirs = `r(num_dir_searched)'
        local this_rootdirs = `"`r(rootdirs)'"'

        * Output this search
        noi di_search_results, ///
          time(`time') dcount(`dirs') rootdirs(`"`this_rootdirs'"')

        * Add these rootdirs to the list of all dirs
        local rootdirs = trim(`"`rootdirs' `this_rootdirs'"')

        * Update the time and dir_count to the grand totals
        local tot_time = `tot_time' + `time'
        local tot_dirs = `tot_dirs' + `dirs'
      }

      * Output the grand total
      noi di as smcl `"{hline}"'
      noi di_search_results, total ///
        time(`tot_time') dcount(`tot_dirs') rootdirs(`"`rootdirs'"')
      noi di as smcl `"{hline}"'


      /***************************************************
        Parse the root files
      ***************************************************/

      local found_roots ""

      foreach rootdir of local rootdirs {
        reproot_parse root, file("`rootdir'/`root_file'")
        local this_root         "`r(root)'"
        local this_root_global  "`prefix'`this_root'"
        local this_root_project "`r(project)'"

        * Test if this root belongs the relevant project
        if "`project'" == "`this_root_project'" {

          * Test if root was already found, if not then add to found_roots
          if (`: list this_root in found_roots') {
            noi di as error _n "{pstd}A second root called {result:`this_root)'} was found for this project found in folder {result:`rootdir'}.{p_end}"
            error 99
            exit
          }
          local found_roots : list found_roots | this_root
          noi di "found_roots `found_roots'"

          local found_str "Root {result:`this_root'} for project {result:`this_root_project'} found"


          if (`: list this_root in roots') {
            * Output that a relevant root has been found
            noi di _n as text "{pstd}`found_str'. Setting global {result:{c S|}{c -(}`this_root_global'{c )-}} to: {result:`rootdir'}{p_end}"

            global `this_root_global' "`rootdir'"
          }
          * Root not required - just skip it
          else {
            noi di _n as text "{pstd}`found_str', but root not required, so no global is set for this root.{p_end}"
          }
        }
      }
      noi di _n `"{hline}"'
    }

    * Return all root dires found regardless if they were for this project
    return local rootdirs "`rootdirs'"

    // Remove then command is no longer in beta
    noi repkit "beta reproot"

}
end


cap program drop   di_search_results
    program define di_search_results

  syntax, time(numlist) dcount(numlist) [rootdirs(string) total]

  local time: display %8.2f `time'
  local dcount: display %14.0fc `dcount'

  local rcount: list sizeof rootdirs

  local time   = trim("`time'")
  local dcount = trim("`dcount'")

  if missing("`total'") local intro_str "In this search directory"
  else local intro_str "In total"

  noi di as result _n `"{pstd}`intro_str', `dcount' directories were searched in `time' seconds, and `rcount' reproot   root(s) were found.{p_end}"' _n
end