// Program file to manage specification curves


// ---------------------------------------------------------------------------------------------
// Main command
cap prog drop specc
prog def specc
  version 13.1

  // Syntax setup
  syntax [anything] [using/]  ///
    [if] ///
    , ///
    [*]

  // Default specc directory
  if "`using'" == "" local using "specc"

  // Parse subcommand
  gettoken subcommand anything : anything

  // Allow abbreviations
  if "`subcommand'" == "init" local subcommand = "initialize"
  if "`subcommand'" == "drop" local subcommand = "remove"

  // Make sure some subcommand is specified
  if !inlist("`subcommand'","initialize","remove","new","report","set","run") {
    di as err "{bf:specc} requires [initialize], [remove], [report], [new], [set], [run] to be specified. Type {bf:help specc} for details."
    error 197
  }

  specc_`subcommand' `anything' using "`using'" `if', `options'

end
// ---------------------------------------------------------------------------------------------

// ---------------------------------------------------------------------------------------------
// Initialization subcommand
cap prog drop specc_initialize
prog def specc_initialize

  // Syntax setup
  syntax using/ , ///
    [*]

  // Create empty dataset for specc storage
  mkdir `"`using'"' , public
  preserve
    clear
    save `"`using'/specc.dta"' , emptyok
  restore

  // Set up model class and main method
  specc new model main Main Specification ///
    using "`using'" ///
    , skipcheck

end
// ---------------------------------------------------------------------------------------------

// ---------------------------------------------------------------------------------------------
// NEW METHOD subcommand
cap prog drop specc_new
prog def specc_new

  // Syntax setup
  syntax anything using/ , ///
    [Code(string asis)] ///
    [replace] [skipcheck] [*]

  // Get info
  gettoken class anything : anything
  gettoken method anything : anything

  // Make sure no conflicts
  if "`skipcheck'" == "" {
    preserve
      use `"`using'/specc.dta"' , clear
      qui count if class == "`class'" & method == "`method'"
      if (`r(N)' > 0) & ("`replace'" == "") {
        di as err "The `method' method already exists in the `class' class."
        error 110
      }
    restore
  }

  // Append new method dataset for specc storage
  preserve
  qui {
  clear
    set obs 1
    gen class = "`class'"
    gen method = "`method'"
    gen dofile = "/`class'/`method'.do"
    gen timestamp = "`c(current_date)' `c(current_time)'"
    gen description = "`anything'"

    append using `"`using'/specc.dta"'
      save `"`using'/specc.dta"' , replace
  }

  // Set up method dofile
  cap mkdir `"`using'/`class'/"' , public
    if "`replace'" != "" erase `"`using'/`class'/`method'.do"'
    cap file close main
    file open main using `"`using'/`class'/`method'.do"' , write
    file write main "// `anything'" _n _n
    file write main `code' _n _n
    if class == "model" {
      file write main "local b =" _n
      file write main "local ll =" _n
      file write main "local ul =" _n
      file write main "local p =" _n
      file write main "cap mat drop _specc_results" _n
      file write main "mat _specc_results = [\`b',\`ll',\`ul',\`p']" _n
      file write main `"mat colnames _specc_results = "b" "ll" "ul" "p" "' _n _n
    }
    file write main "// End of `anything'" _n
    file close main

end
// ---------------------------------------------------------------------------------------------

// ---------------------------------------------------------------------------------------------
// Removal subcommand
cap prog drop specc_remove
prog def specc_remove

  // Syntax setup
  syntax anything using/ , ///
    [*]

  gettoken class anything : anything
  gettoken method anything : anything

  // Remove marked method
  preserve
    use `"`using'/specc.dta"' `if', clear
    qui drop if class == "`class'" & method == "`method'"
    qui save `"`using'/specc.dta"' , replace
  restore
  !rm "`using'/`class'/`method'.do"

end
// ---------------------------------------------------------------------------------------------

// ---------------------------------------------------------------------------------------------
// SET subcommand
cap prog drop specc_set
prog def specc_set

  // Syntax setup
  syntax anything using/ , ///
    reset [*]

  // Create empty dofile for specc storage
  cap rm `"`using'/specc.do"'

  cap file close main
  file open main using `"`using'/specc.do"' , write
  file write main "/* SPECC Runfile will iterate over:" _n
  file write main "`anything'" _n
  file write main "*/" _n _n

  foreach class in `anything' {
    file write main "\``class''" _n _n
  }

  file write main "// End of SPECC Runfile" _n
  file close main

  // Read out execution order
  file open main using `"`using'/specc.do"' , read
    file read main line
    forv i = 1/2 {
      display "`line'"
      if `i' == 1 file read main line
    }
  file close main

end
// ---------------------------------------------------------------------------------------------

// ---------------------------------------------------------------------------------------------
// Report subcommand
cap prog drop specc_report
prog def specc_report

  // Syntax setup
  syntax [anything] using/ [if], ///
    [sort] [*]

  // Load and display report
  if "`anything'" == "" {
    preserve
      use `"`using'/specc.dta"' `if', clear
      if "`sort'" != "" sort class method
      li
    restore

    cap confirm file `"`using'/specc.do"'
    if _rc == 0 {
      di "SPECC Runfile detected at {browse `using'/specc.do}."
      cap file close main
      file open main using `"`using'/specc.do"' , read
      file read main line
      forv i = 1/2 {
      	display "`line'"
      	if `i' == 1 file read main line
      }
    }

    local params = "`line'"
    local n_params: word count `params'

    forv i = 1/`n_params' {
      local c`i' : word `i' of `params'

      preserve
        use `"`using'/specc.dta"' `if', clear
        qui levelsof method if class == "`c`i''" , local(m`i')
        qui levelsof description if class == "`c`i''" , local(d`i')
      restore

      di `" `c`i'' :: `d`i''   "'
    }
  }

  // Display contents if requested
  if "`anything'" != "" {
    gettoken class anything : anything
    gettoken method anything : anything
    cap file close main
    file open main using `"`using'/`class'/`method'.do"' , read
    file read main line
    while r(eof)==0 {
    	display "`line'"
    	file read main line
    }
    file close main
  }

end
// ---------------------------------------------------------------------------------------------

// ---------------------------------------------------------------------------------------------
// Run subcommand
cap prog drop specc_run
prog def specc_run

  // Syntax setup
  syntax using/ , ///
    [sort] [save] [*]

  // Read out execution order
  cap file close main
  file open main using `"`using'/specc.do"' , read
    di "SPECC Runfile detected at {browse `using'/specc.do}."
    cap file close main
    file open main using `"`using'/specc.do"' , read
    file read main line
    forv i = 1/2 {
      display "`line'"
      if `i' == 1 file read main line
    }
  file close main
  local params = "`line'"

  // Create iteration loop
  local n_params: word count `params'
  tempname current max

    mat `current' = J(1,`n_params',1)
      mat colnames `current' = `params'
    mat `max' = J(1,`n_params',1)

    forv i = 1/`n_params' {
      local c`i' : word `i' of `params'

      preserve
        use `"`using'/specc.dta"' `if', clear
        qui levelsof method if class == "`c`i''" , local(m`i')
          local max_`c`i'' : word count `m`i''
          mat `max'[1,`i'] = `max_`c`i'''
        qui levelsof description if class == "`c`i''" , local(d`i')
      restore

      di `" `c`i'' :: `d`i''   "'
    }

  // Loop over combinations

    // Calculate total combinations
    local total = 1
    forv i = 1/`n_params' {
      local next = `max'[1,`i']
      local total = `total'*`next'
      local lab`i' = `"0 "                ""'
    }

    // Set up to loop over all differences
    tempname diffmat

    local diff = 1
    local counter = 0
    cap mat drop _all_results
    while `diff' != 0 {
      local ++counter
      di " "
      di "(`counter'/`total') Running:"

      // Get the dofile list
      forv i = 1/`n_params' {
        local theIndex = `current'[1,`i']
        local theClass = "`c`i''"
        local theMethod : word `theIndex' of `m`i''
        preserve
          use `"`using'/specc.dta"' ///
            if class == "`theClass'" & method == "`theMethod'" , clear
          local theDesc = description[1]
          local lab`i' = `"`lab`i'' `theIndex' "`theDesc'" "'
        restore
        di " `theDesc' (`using'/`theClass'/`theMethod'.do)"
        run `"`using'/`theClass'/`theMethod'.do"'
        if "`theClass'" == "model" {
          mat _all_results = nullmat(_all_results) ///
            \ `current' , _specc_results
        }
      }

      // Quit if this was the max iteration
      mat `diffmat' = (`current' - `max')*(`current' - `max')'
      local diff = `diffmat'[1,1] // Diff becomes zero when matrices are equal

      // Increment first unmaxed param
      local increment = 1
      forv i = 1/`n_params' {
        local theCurrent = `current'[1,`i']
        local theMax = `max'[1,`i']
        if (`theCurrent' < `theMax') & (`increment' == 1) {
          mat `current'[1,`i'] = `theCurrent' + 1
          local increment = 0
        }
        else if (`theCurrent' == `theMax') & (`increment' == 1) {
          mat `current'[1,`i'] = 1
        }
      }
    } // End looping over combinations

  // Build graph
  preserve
    qui clear
    qui svmat _all_results , n(col)
    sort b
    if "`sort'" != "" sort `line'
    if "`save'" != "" save `"`using'/results.dta"' , replace
    gen n = _n

    local tw_opts ///
    	graphregion(color(white) lc(white)) bgcolor(white) ///
    	ylab(,angle(0) nogrid) legend(off)

    forv i = 1/`n_params' {
      qui tw ///
        (function 0 , range(1 `total') lc(black) lw(thin)) ///
        (scatter `c`i'' n , msize(medlarge) m(X) mc(black)) ///
      , yscale(noline) xscale(noline) xlab(none,notick)  ///
        ylab(`lab`i'' , labsize(tiny) notick) ytit(" ") ///
        nodraw saving(`"`using'/`c`i''.gph"' , replace) `tw_opts' ///
        title("`c`i''", justification(left) color(black) span pos(11) size(small))

      local graphs `"`graphs' "`using'/`c`i''.gph""'
    }

    qui tw ///
      (rspike ul ll n , lc(gs12)) ///
      (scatter b n if p >= 0.05, mlc(black) mc(white) msize(medium)) ///
      (scatter b n if p < 0.05 , mc(black) lc(none) msize(small)) ///
    , xtit(" ") xlab(1 " " , notick) yscale(noline r(0)) ylab(#6 , notick) ///
      yline(0) fysize(66)  ytit("Coefficient") `tw_opts' ///
      nodraw saving(`"`using'/results.gph"' , replace)

    graph combine ///
      `"`using'/results.gph"' `graphs' ///
    , c(1) xcom imargin(t=0 b=0) `options' ///
      graphregion(color(white) lc(white))

    !rm "`using'/results.gph" `graphs'


  restore

end
// ---------------------------------------------------------------------------------------------

// End of adofile