*! version 0.0.9 2011-04-27 range for x and y
* version 0.0.8 2011-04-27 xlabel to label
* version 0.0.8 2011-04-27 housekeeping partial
* version 0.0.7 2011-04-27 itemlist and typelis
* version 0.0.6 2011-04-27 add value lables
* version 0.0.5 2011-04-27 bugs
* version 0.0.4 2011-04-27 revise names; split text returns; fix xcoord return
* version 0.0.3 2011-04-26 bubblecoord
* version 0.0.2 2011-04-26 bubblecoord
* version 0.0.1 2011-04-26 bubblecoord

* DO: test if lists have correct number of items

program define bubblecoord, rclass

    version 11.2

syntax , [debug]            ///
	gen(name)               /// create this variable with y-coor
	itemvar(varname)        /// numeric variable for item
    itemlabel(varname)      /// string name for item
    [   ///
    xcoord                  /// if xcoord, don't create graph returns
    itemsort(string)        /// ascend, descend, nosort default
    itemlist(numlist)       /// numeric list for arranging items
    typevar(varname)        /// type variable as a number
    typesort(string)        /// ascend, descend, list; nosort default
    typelist(numlist)       /// numeric list for arranging types
    typeLlabel(varname)     /// type string on left
    typeRlabel(varname)     /// type string on right
    gapvar(varname)         /// if gapvar changes, add gapvalue
    gapvalue(integer 1)     /// size of gap when gapvar changes
    gapevery(integer 1)     /// gap every # items; 1 does nothing
    ylineoffset(real .75)   /// offset for ylines from gap start
    scaleoffset(real .25)  /// offset for scale from min and max
    textLoffset(real -10)   /// offset for left text string
    textRoffset(real .1)    /// offset for right text string
    textupoffset(real .2)   /// offset for up down text string
    textLoptions(string)    /// options for left string
    textRoptions(string)    /// options for right string
    ]

//  #1 default options ==========================

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

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

if "`itemsort'"=="" local itemsort "nosort"

if "`typesort'"=="" local typesort "nosort"

local isgapevery = 0
if "`gapevery'"!="" local isgapevery = 1

local isgapvar = 0
if "`gapvar'"!="" local isgapvar = 1


local isitemlist = 0
if "`itemlist'"!="" local isitemlist = 1

local isitemsort = 0
if "`itemsort'"=="ascend"  | "`itemsort'"=="ascending" | "`itemsort'"=="a" {
    local itemsort "ascend"
    local isitemsort = 1
}

local isitemsort = 0
if "`itemsort'"=="descend"  | "`itemsort'"=="descending" | "`itemsort'"=="d" {
    local itemsort "descend"
    local isitemsort = 1
}

local isitemvar = 0
if "`itemvar'"!="" local isitemvar = 1

local istypesort = 0
if "`typesort'"=="ascend"  | "`typesort'"=="ascending" | "`typesort'"=="a" {
    local typesort "ascend"
    local istypesort = 1
}

local istypesort = 0
if "`typesort'"=="descend"  | "`typesort'"=="descending" | "`typesort'"=="d" {
    local typesort "descend"
    local istypesort = 1
}


local istypelist = 0
if "`typelist'"!="" local istypelist = 1

local istypevar = 0
if "`typevar'"!="" local istypevar = 1


if "`debug'"=="debug" {
    di
    di "gapevery:       `gapevery'"
    di "gen:            `gen'"
    di "itemvar:        `itemvar'"
    di "itemsort:       `itemsort'"
    di "itemlist:       `itemlist'"
    di "itemlabel:      `itemlabel'"
    di "typevar:        `typevar'"
    di "typesort:       `typesort'"
    di "typelist:       `typelist'"
    di "typeLlabel:     `typeLlabel'"
    di "typeRlabel:     `typeRlabel'"
    di "gapvar:         `gapvar'"
    di "gapvalue:       `gapvalue'"
    di "gapevery:       `gapevery'"
    di "istypesort:     `istypesort'"
    di "isitemsort:     `isitemsort'"
    di "istypelist:     `istypelist'"
    di "isitemlist:     `isitemlist'"
}

//  #2 trap errors ==============================

if `istypesort'==1 & `istypelist'==1 {
    di in red "typesort() and typelist() cannot be used together."
    exit
}
if `isitemsort'==1 & `isitemlist'==1 {
    di in red "itemsort() and itemlist() cannot be used together."
    exit
}

//  #3 initialize ===============================

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

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

//  #4 sort items & type ========================

tempvar TYPEsortvar ITEMsortvar

* process sorting or list of type variable
gen `TYPEsortvar' = .
if `istypevar'==1 {
    qui replace `TYPEsortvar' = `typevar'
    if `istypelist'==1 {
        local typesort "ascending" // sort the recoded variable
        * recode according to list
        local listis "`typelist'"
        local listN : word count `listis'
        local recodeis ""
        forvalues i = 1(1)`listN' {
            local listval : word `i' of `listis'
            local recodeis "`recodeis'(`i'=`listval') "
        }
        recode `TYPEsortvar' `recodeis'
    }
}

* process sorting or list of item variable
gen `ITEMsortvar' = .
qui replace `ITEMsortvar' = `itemvar'
if `isitemlist'==1 {
    local itemsort "ascending" // sort the recoded variable
}

if "`itemsort'"=="ascend"  {
    local itemplus "+"
}
else if "`itemsort'"=="descend" {
    local itemplus "-"
}
else if "`itemsort'"=="nosort" {
}
else {
    di in red "Invalid itemsort() option"
    exit
}

if "`typesort'"=="ascend" {
    local typeplus "+"
}
else if "`typesort'"=="descend" {
    local typeplus "-"
}
else if "`typesort'"=="nosort" {
}
else {
    di in red "Invalid typesort() option"
    exit
}

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

*   no type        item sort
if `istypevar'==0 & "`itemsort'"!="nosort" {
    * sort only item
    gsort `itemplus' `ITEMsortvar'
}

*       type           type sort                  item sort
else if `istypevar'==1 & "`typesort'"!="nosort" & "`itemsort'"!="nosort" {
    * sort by type then item
    gsort `typeplus' `TYPEsortvar' `itemplus' `ITEMsortvar'
}

if "`debug'"=="debug" {
    di in red _new ">> #3 after sort"
    list `typevar' `TYPEsortvar' `itemvar' `ITEMsortvar', 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' + `gapevery'
            }

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

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

        local coord_value = 0
        forvalues irow = 1(1)`nobs' {

            local coord_value = `coord_value' + `gapevery'
            qui replace `gen' = `coord_value' in `irow'
        }
    } // no gapvar

    if "`debug'"=="debug" {
        di in red _new ">> #4" _new
        list `gen' `typevar' `itemvar' `itemlabel', clean abbrev(14)
    }

    // invert values so top items have largest values

    if "`xcoord'"=="" {

        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' `typevar' `itemvar' `itemlabel', clean abbrev(14)
        di
        di in red _new ">> #6 gapstartloc" _new
        di "gapstartloc: |`gapstartloc'|"
    }

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

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

    //  label for x or y
    local labelstr ""
    forvalues irow = `nobs'(-1)1 {
        local coordis = `gen'[`irow']
        local labelstr "`labelstr' `coordis'"
    }
    return local label "`labelstr'"

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

if "`xcoord'"=="" {

    // 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'"


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

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

    if "`debug'"=="debug" {
        di in red _new ">> #5" _new
        di "labelstr: |`labelstr'|"
        di
        di " rangestr: |`rangestr'|"
    }


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

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

    local textall ""

    //  put strings into gapstrL# and gapstrR#

    * gap 1 is in row 1
    local gap_prior = `gapvar'[1]
    local gapstrL1 = `typeLlabel'[1]
    local gapstrR1 = `typeRlabel'[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' = `typeLlabel'[`irow']
            local gapstrR`igap' = `typeRlabel'[`irow']
            local igap = `igap' + 1
        }

        local gap_prior = `gap_current'
    }

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

    local txtLall ""
    local txtRall ""

    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' `textLoffset'"'
        local l2 `""`gapstrL`i''""'
        local l3 `", `textLoptions')"'
        local txtLstr`i' `"`l1' `l2'`l3'"'

        local r1 `"text(`ytxtloc' `textRoffset'"'
        local r2 `""`gapstrR`i''""'
        local r3 `", `textRoptions')"'
        local txtRstr`i' `"`r1' `r2'`r3'"'

        local txtLall `"`txtLall' `txtLstr`i''"'
        local txtRall `"`txtRall' `txtRstr`i''"'

    }

    return local textR `"`txtLall'"'
    return local textL `"`txtRall'"'

    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 create value labels

* variable: `itemlabel'
* create labeldef B`itemlabel'

if "`debug'"=="debug" {
    di in red _new ">> #7" _new
}

capture label drop B`itemlabel'


forvalues irow = 1(1)`nobs' {
    local varval = `gen'[`irow']
    local varlbl = `itemlabel'[`irow']
    label define B`itemlabel' `varval' `"`varlbl'"', modify

    if "`debug'"=="debug" {
        di "varval:  `varval'"
        di "varlbl:  `varlbl'"
        di
    }

}

label value `gen' B`itemlabel'

end // bubbleycoord
exit