*! version 0.3 20240606 - LSMS Team, World Bank - lsms@worldbank.org

cap program drop   ad_publish
    program define ad_publish

qui {

    version 14.1

    * Update the syntax. This is only a placeholder to make the command run
    syntax, ADFolder(string) [UNDocumented(string) nogen_sthlp ssczip debug]

    ****
    ** Test folder input input

    * Standardize folder
    local folderstd	= subinstr(`"`adfolder'"',"\","/",.)
    local srcfolder	= `"`adfolder'/src"'
    local adofolder	= `"`srcfolder'/ado"'
    local sthfolder	= `"`srcfolder'/sthlp"'
    local sscfolder	= `"`srcfolder'/dev/ssc"'

    * Test for adodown folders expected in the folder
    foreach ad_fld in folderstd srcfolder adofolder sthfolder {
      mata : st_numscalar("r(dirExist)", direxists("``ad_fld''"))
      if `r(dirExist)' == 0  {
        local folder_error "TRUE"
        local missing_flds `"`missing_flds' "``ad_fld''" "'
      }
    }
    * Output errors and list missing folders
    if ("`folder_error'" == "TRUE") {
      noi di as error "{pstd}The folder in option {inp:adfolder()} is not valid adodown folder. The following folders were expected but not found:{p_end}"
      foreach miss_fold of local missing_flds {
        noi di as text `"{pstd}- `miss_fold'/{p_end}"'
      }
      error 99
      exit
    }

    ********************************************************
    ** Get Meta data

    * Get meta data from package file
    ad_pkg_meta, adfolder(`"`folderstd'"')
    local pkgname    "`r(pkgname)'"
    local stata_vnum "`r(stata_version)'"
    local pkg_vnum   "`r(package_version)'"
    local vdate      "`r(date)'"
    local author     "`r(author)'"
    local contact    "`r(contact)'"
    local pkg_ados  `"`r(adofiles)'"'
    local pkg_hlps  `"`r(hlpfiles)'"'
    local pkg_ancs  `"`r(ancfiles)'"'

    local ado_v_header "*! version `pkg_vnum' `vdate' - `author' - `contact'"

    ********************************************************
    ** Generate the sthlp files

    if ("`gen_sthlp'" != "nogen_sthlp") {
      cap ad_sthlp, adfolder(`"`folderstd'"')
      if _rc {
        noi di as error "{pstd}Error when rendering the .sthlp files from .mdhlp files. Run command {cmd:ad_sthlp()} and fix the error or use option {opt:nogen_sthlp} to not render the files and see if package can be published anyway.{p_end}"
        * Run it again without cap to return the error
        noi ad_sthlp, adfolder(`"`folderstd'"') vnum("`pkg_vnum'") vdate("`vdate'")
      }
    }


    ********************************************************
    ** List all ado-files and helpfiles to make sure required files exists

    * List ado-files
    local adofiles : dir `"`adofolder'"' files "*.ado"	, respectcase
    foreach adofile of local adofiles {
      local cmds = "`cmds' " + subinstr("`adofile'",".ado","",1)
    }

    * List sthlp files
    local sthfiles : dir `"`sthfolder'"' files "*.sthlp"	, respectcase
    foreach sthfile of local sthfiles {
      local hlps = "`hlps' " + subinstr("`sthfile'",".sthlp","",1)
    }

    local ado_sthlp_error 0

    * Test if cmd in undocumented is not a command
    local invalid_undocumented : list undocumented - cmds
    if !missing("`invalid_undocumented'") {
      noi di as error "{pstd}The command(s) {inp:`invalid_undocumented'} used in {res:undocumented(`undocumented')} is not a command in the ado-folder.{p_end}"
      local ado_sthlp_error 1
    }

    * Test for helpfiles without ado-files
    local sthlp_without_ado : list hlps - cmds
    if !missing("`sthlp_without_ado'") {
      noi di as error "{pstd}The help-files(s) {inp:`sthlp_without_ado'} in the {inp:`sthfolder'} folder does not have any command(s) in the {inp:`adofolder'} folder.{p_end}"
      local ado_sthlp_error 1
    }

    * Test if any commands listed as undocummented commmands are not found
    local ado_without_sthlp : list cmds - hlps
    local ado_without_sthlp : list ado_without_sthlp - undocumented
    if !missing("`ado_without_sthlp'") {
      noi di as error "{pstd}The command(s) {inp:`ado_without_sthlp'} in the {inp:`adofolder'} folder does not have any help-file(s) in the {inp:`sthfolder'} folder.{p_end}"
      local ado_sthlp_error 1
    }

    * Throw error for missing files
    if (`ado_sthlp_error' == 1) {
      error 99
      exit
    }

    ********************************************************
    ** pdate versioning meta data in adofiles

    foreach ado of local cmds {
      local pkgcommand ""
      if ("`ado'" == "`pkgname'") local pkgcommand "pkgcommand"

      noi update_ado_version,           ///
        vhead("`ado_v_header'")         ///
        pkg_vnum("`pkg_vnum'")          ///
        stata_vnum("`stata_vnum'")      ///
        file(`"`adofolder'/`ado'.ado"') ///
        vdate("`vdate'") ///
        `pkgcommand'
    }

    ********************************************************
    ** generate the zip-file for ssc

    if !missing("`ssczip'") {

      local old_pwd `c(pwd)'

      * Create the ssc-version folder
      cap mkdir "`sscfolder'"
      local vfldr "`sscfolder'/v`pkg_vnum'"
      cap mkdir "`vfldr'"

      * Copy all files in the pkg file to the ssc-version folder
      foreach ado of local pkg_ados {
        split_file_extentsion, file("`ado'")
        copy `"`adofolder'/`r(file_name)'"' `"`vfldr'/`r(file_name)'"', replace
      }
      foreach hlp of local pkg_hlps {
        split_file_extentsion, file("`hlp'")
        copy `"`sthfolder'/`r(file_name)'"' `"`vfldr'/`r(file_name)'"', replace
      }
      foreach anc of local pkg_ancs {
        split_file_extentsion, file("`anc'")
        copy `"`srcfolder'/`anc'"' `"`vfldr'/`r(file_name)'"', replace
      }

      local zipfile "`vfldr'/`pkgname'.zip"
      cap rm "`zipfile'"

      * Change pwd to ssc-version folder
      cd "`vfldr'"

      * Compress the package the way ssc wants it
      zipfile * , saving("`zipfile'", replace)
      noi di as text "{pstd}Zip-archive ready to send to SSC was saved at  {res:`zipfile'}.{p_end}"

      * Restore the pwd
      cd "`old_pwd'"
    }

    // Remove when command is no longer in beta
    noi adodown "beta ad_publish"
}
end

cap program drop   update_ado_version
    program define update_ado_version

    syntax, vhead(string) pkg_vnum(string) stata_vnum(string) file(string) vdate(string) [pkgcommand]

    * Open template to read from and new tempfile to write to
    tempname ado_old ado_new
    tempfile new_adofile
    file open `ado_old' using `"`file'"', read
    file open `ado_new' using `new_adofile' , write

    local syntax_over = 0
    local version_header_used  = 0
    local version_setting_used = 0

    * Write remaining file
    file read `ado_old' line
    while r(eof)==0 {

        * Escape all characters that will mess with str end or start
        local line : subinstr local line `"""' `"" _char(34) ""', all
        local line : subinstr local line "'"   `"" _char(39) ""', all
        local line : subinstr local line "`"   `"" _char(96) ""', all
        local line : subinstr local line "$"   `"" _char(36) ""', all

        * This command only make changes to items before the first "syntax"
        * So do not perform any replacements after the initial syntax
        if (`syntax_over' != 1) {

          * Test if this line is the syntax line
          if (substr(ustrtrim("`line'"),1,6) == "syntax") {
            * If it is, set local to 1 and just write the line
            local syntax_over 1
            file write `ado_new' "`line'" _n
          }

          * Test if line is version header
          else if (substr(ustrtrim("`line'"),1,10) == "*! version") {
            * Update the version header
            file write `ado_new' "`vhead'" _n
            * Indicate that version header is used
            local version_header_used  = 1
          }

          * Test if line is stata version seting
          else if (substr(ustrtrim("`line'"),1,7) == "version") {
            file write `ado_new' "    version `stata_vnum'" _n
            * Indicate that version header is used
            local version_setting_used  = 1
          }

          * Test if line is stata version seting
          else if (!missing("`pkgcommand'") & substr(ustrtrim("`line'"),1,14) == "local version") {
            file write `ado_new' `"    local version "`pkg_vnum'" "' _n
            * Indicate that version header is used
            local version_setting_used  = 1
          }

          * Test if line is stata version seting
          else if (!missing("`pkgcommand'") & substr(ustrtrim("`line'"),1,17) == "local versionDate") {
            file write `ado_new' `"    local versionDate "`vdate'" "' _n
            * Indicate that version header is used
            local version_setting_used  = 1
          }

          * Before syntax but wite line as is
          else file write `ado_new' "`line'" _n
        }
        * Before syntax but wite line as is
        else file write `ado_new' "`line'" _n

        * REad next line
        file read `ado_old' line
    }

    file close `ado_old'
    file close `ado_new'

    ************************
    * Test that required items were found

    if (`version_header_used' == 0) {
      noi di as error `"{pstd}The ado-file {inp:`file'} does not have a version header.{p_end}"'
      error 99
      exit
    }

    if (`version_setting_used' == 0) {
      noi di as error `"{pstd}The ado-file {inp:`file'} does not set Stata version before syntax.{p_end}"'
      error 99
      exit
    }

    ************************
    * Overwrite org fine with tempfile

    copy "`new_adofile'" `"`file'"', replace

end


* Splits a file name into its name and its extension
cap program drop 	split_file_extentsion
	program define	split_file_extentsion, rclass

	syntax, file(string)

  local file	= subinstr(`"`file'"',"\","/",.)

	**Find the last / in the file path
  local slash_index = strpos(strreverse("`file'"),"/")

	** Find file name and extension based on the last / in the file
	return local file_name  = substr("`file'",1-`slash_index',.)
end