*! version 0.0.3 2011-04-26 bubblecoord
* version 0.0.2 2011-04-26 bubblecoord
* version 0.0.1 2011-04-26 bubblecoord

*? how will gapevery() work?
*? trap error if gapvar and gapevery?
*? add itemorder(list); typeorder(list)
*? how does itemstrvar get used

set trace off
capture clear programs
program define bubblecoord, rclass

    version 11.2

//  =========
//  #1 syntax

syntax , [debug]                ///
	gen(name)                   /// create this variable with y-coor
	itemnumvar(varname)         /// numeric variable for item
    itemstrvar(varname)         /// string name for item
    [   ///
    xcoord                      /// if xcoord, don't create graph returns
    itemorder(string)           /// ascend, descend, list; nosort default
    typenumvar(varname)         /// type variable as a number
    typeorder(string)           /// ascend, descend, list; nosort default
    typestrleft(varname)        /// type string on left
    typestrright(varname)       /// type string on right
    gapvar(varname)             /// if gapvar changes, add gapvalue
    gapvalue(integer 1)         /// size of gap when gapvar changes
    gapevery(integer 0)         /// gap every # items.
    ylineoffset(real .75)       /// offset for ylines from gap start
    yscaleoffset(real .25)      /// offset for yscale from min and max
    textleftoffset(real -10)    /// offset for left text string
    textrightoffset(real .1)    /// offset for right text string
    textupoffset(real .2)       /// offset for up down text string
    textleftoptions(string)     /// options for left string
    textrightoptions(string)    /// options for right string
    ]

//  default options

if "`textleftoptions'"=="" {
    local textleftoptions "place(e) just(left) margin(zero)"
}

if "`textrightoptions'"=="" {
    local textleftoptions "place(w) just(right) margin(zero)"
}

if "`itemorder'"=="" local itemorder "nosort"
if "`typeorder'"=="" local typeorder "nosort"

//  trap errors

if "`gapvar'"!="" & `gapevery'!=0 {
    di as error "gapevery() and gapvar() cannot be used together"
    exit
}

if "`debug'"=="debug" {
    di
    di "gen:            `gen'"
    di "itemnumvar:     `itemnumvar'"
    di "itemorder:      `itemorder'"
    di "itemstrvar:     `itemorder'"
    di "typenumvar:     `typenumvar'"
    di "typeorder:      `typeorder'"
    di "typestrleft:    `typestrleft'"
    di "typestrright:   `typestrright'"
    di "gapvar:         `gapvar'"
    di "gapvalue:       `gapvalue'"
    di "gapevery:       `gapevery'"
}

//  ================
//  #2 program setup

* variable holding coordinates
gen `gen' = .
    label var `gen' "axis coordinate for `itemnumvar'"
    note `gen': created by bubblecoord.ado.

* is a type variable given?
local istype = 0
if "`typenumvar'"!="" local istype = 1

* is a gap variable given?
local isgapvar = 0
if "`gapvar'"!="" local isgapvar = 1

* is gapevery being used
local isgapevery = 0
if "`gapevery'"!="" local isgapevery = 1

* how many rows in current dataset?
_nobs `itemnumvar'
local nobs = r(N)

//  ====================
//  #3 sort items & type

if "`itemorder'"=="ascending" | "`itemorder'"=="ascend"  {
    local itemsort "+"
}
else if "`itemorder'"=="descending" | "`itemorder'"=="descend" {
    local itemsort "-"
}
else if "`itemorder'"=="list" {
    di in red "itemorder(list) does not work yet..."
    exit
}
else if "`itemorder'"=="nosort" {
}
else {
    di in red "Invalid itemorder() option"
    exit
}

if "`typeorder'"=="ascending" | "`typeorder'"=="ascend" {
    local typesort "+"
}
else if "`typeorder'"=="descending" | "`typeorder'"=="descend" {
    local typesort "-"
}
else if "`typeorder'"=="list" {
    di in red "typeorder(list) does not work yet..."
    exit
}
else if "`typeorder'"=="nosort" {
}
else {
    di in red "Invalid typeorder() option"
    exit
}

if "`debug'"=="debug" {
    di in red _new ">> #3 item before sort"
    list `itemnumvar', clean abbrev(14)
}

*   no type        item sort
if `istype'==0 & "`itemorder'"!="nosort" {
    * sort only item
    gsort `itemsort' `itemnumvar'
}

*       type           type sort                  item sort
else if `istype'==1 & "`typeorder'"!="nosort" & "`itemorder'"!="nosort" {
    * sort by type then item
    gsort `typesort' `typenumvar' `itemsort' `itemnumvar'
}

if "`debug'"=="debug" {
    di in red _new ">> #3 item after sort"
    list `typenumvar' `itemnumvar', clean abbrev(14)
}

//  ==================================================
//  #4 process gapvar and generate coordinate variable

if "`xcoord'"=="" {

    local tmpgaploc "" // hold list of gap locations

    if `isgapvar' {
        local gap_prior = .
        local coord_value = 0

        forvalues irow = 1(1)`nobs' {

            local gap_current = `gapvar'[`irow']

            if `gap_prior'==`gap_current' {
                local coord_value = `coord_value' + 1
            }

            if `gap_prior'!=`gap_current' {
                local tmp = `coord_value' + `gapvalue'
                local tmpgaploc "`tmpgaploc' `tmp'"
                local coord_value = `coord_value' + `gapvalue' + 1
            }

            local gap_prior = `gap_current'
            qui replace `gen' = `coord_value' in `irow'
        }
    } // is gapvar

    if "`debug'"=="debug" {
        di in red _new ">> #4" _new
        list `gen' `typenumvar' `itemnumvar' `itemstrvar', clean abbrev(14)
    }

    // invert values so top items have largest values

    qui sum `gen'
    local maxis = r(max)
    qui replace `gen' = 1 + `maxis' - `gen'

    local gapstartloc ""
    foreach v in `tmpgaploc' {
        local vi = 1 + `maxis' - `v'
        local gapstartloc "`gapstartloc' `vi'"
    }

    local gapsN : word count `gapstartloc'

    if "`debug'"=="debug" {
        di in red _new ">> #4 gapvar" _new
        list `gen' `typenumvar' `itemnumvar' `itemstrvar', clean abbrev(14)
        di
        di in red _new ">> #6 gapstartloc" _new
        di "gapstartloc: |`gapstartloc'|"
    }

} // "`xcoord'"==""

//  ==========================================
//  #5 create ylabel, yline and yrange strings

if "`xcoord'"=="" {

    //  ylabel
    local ylabelstr ""
    forvalues irow = `nobs'(-1)1 {
        local coordis = `gen'[`irow']
        local ylabelstr "`ylabelstr' `coordis'"
    }
    return local ylabel "`ylabelstr'"


    // yline
    local ylinestr ""
    forvalues i = `gapsN'(-1)2 { // don't need line at first gap
        local gapval : word `i' of `gapstartloc'
        local gapval = `gapval' + `ylineoffset'
        local ylinestr "`ylinestr' `gapval'"
    }
    return local yline "`ylinestr'"

    // yscale range string
    qui sum `gen'
    local rngmin = r(min) - `yscaleoffset'
    local rngmax = r(max) + `yscaleoffset'
    local yrangestr "`rngmin' `rngmax'"
    return local yrange "`yrangestr'"

    if "`debug'"=="debug" {
        di in red _new ">> #5" _new
        di "ylabelstr: |`ylabelstr'|"
        di
        di "ylinestr: |`ylinestr'|"
        di
        di "yrangestr: |`yrangestr'|"
        di
        di "gapsN:     `gapsN'"
    }

} // "`xcoord'"==""

//  ======================================================
//  #6 create text label strings if gap, type & not xcoord

if `isgapvar'==1 & `istype'==1 & "`xcoord'"=="" {

    local textall ""

    //  put strings into gapstrL# and gapstrR#

    * gap 1 is in row 1
    local gap_prior = `gapvar'[1]
    local gapstrL1 = `typestrleft'[1]
    local gapstrR1 = `typestrright'[1]

    * look for gaps starting with 2nd
    local igap = 2 // start with 2nd gap
    forvalues irow = 2(1)`nobs' {

        local gap_current = `gapvar'[`irow']
        if `gap_prior'!=`gap_current' {
            local gapstrL`igap' = `typestrleft'[`irow']
            local gapstrR`igap' = `typestrright'[`irow']
            local igap = `igap' + 1
        }

        local gap_prior = `gap_current'
    }

    // build text commands in locals txtLstr# and txtRstr#

    forvalues i = `gapsN'(-1)1 {

        local ytxtloc : word `i' of `gapstartloc'
        local ytxtloc = `ytxtloc' + `textupoffset'
        local ytxtloc = string(`ytxtloc',"%11.2f") // round

        local l1 `"text(`ytxtloc' `textleftoffset'"'
        local l2 `""`gapstrL`i''""'
        local l3 `", `textleftoptions')"'
        local txtLstr`i' `"`l1' `l2'`l3'"'

        local r1 `"text(`ytxtloc' `textrightoffset'"'
        local r2 `""`gapstrR`i''""'
        local r3 `", `textrightoptions')"'
        local txtRstr`i' `"`r1' `r2'`r3'"'

        local txtall `"`txtall' `txtLstr`i'' `txtRstr`i''"'
    }

    return local text `"`txtall'"'

    if "`debug'"=="debug" {
        di in red _new ">> #6 creating text" _new
        forvalues i = `gapsN'(-1)1 {
            di "Gap: `i'" _new
            di `"`macval(txtLstr`i')'"'
            di `"`macval(txtRstr`i')'"'
            di
        }
        di `"`macval(txtall)'"'
    }

} // is there a gap variable?

//  ===========
//  #7 close up

end // bubbleycoord
exit