*! version 3.0.3 Timothy Mak Mar2015.
* Version 3.0.3: Corrected bug in that weights are not passed to collapse in the presence of errortype.
* Version 3.0.3: Corrected bug in that one needs to put in an extra || if adding options under addplot.
* Version 3.0.2: Corrected bug in the err(`ci') option introduced in version 3.0.0
program define lgraph, rclass
version 9

// Known bug: If more than 1 axis is used, the legend may not be correct

// Parse
syntax varlist(num min=2) [if] [in] [aweight pweight iweight fweight] [, Statistic(string) ERRortype(string) ///
	LOPtions(string) EOPtions(string) LEOPtions(string) FOPtions(string) ///
	fit(string) COLORGradient(string) SEParate(real 0) by(varname numeric) ///
	bw noMarker noLegend nopreserve ALSOcollapse(string) wide PLOTorderoptions swap ///
	addplot(string asis) SCHeme(passthru) ///
	median Quantile(numlist min=2 max=2) minmax /* Obsolete options - included for backward compatibility
	*/ *]

// Save weights
local WeightExp `weight' `exp'

// Preserve original data
if "`preserve'" != "nopreserve" preserve

// Selecting subsample
marksample touse, novarlist

// Deal with the old options
if "`quantile'" != "" local errortype quantile(`quantile')
if "`median'" == "median" local statistic median
if "`minmax'" == "minmax" local errortype minmax
cap display `errortype' > 0
if _rc == 0 local errortype ci(`errortype')

// Wide format
if "`wide'" == "wide" {
	qui keep if `touse'
	local nvars : word count `varlist'
	local xvar : word `nvars' of `varlist'
	local nvars2 = `nvars' - 1
	forval i=1/`nvars2' {
		local var : word `i' of `varlist'
		qui gen _dummyvar`i' = `var'
		local label`i' : variable label `var'
		if `"`label`i''"' == "" local label`i' `var'
		local labeldefine `labeldefine' `i' `"`label`i''"'
	}
	tempvar index
	qui gen `index' = _n
	qui reshape long _dummyvar, i(`index')
	tempname labelname
	label define `labelname' `labeldefine'
	label values _j `labelname'
	lgraph _dummyvar `xvar' _j [`WeightExp'], statistic(`statistic') errortype(`errortype') `legend' separate(`separate') ///
		loptions(`loptions') eoptions(`eoptions') leoptions(`leoptions') foptions(`foptions') `bw' fit(`fit') `plotorderoptions' ///
		colorgradient(`colorgradient') `marker' by(`by') alsocollapse(`alsocollapse') nopreserve addplot(`addplot') `scheme' `options'
	return local command `r(command)'
	return local options `r(options)'
	exit
}


// Parse
gettoken depvar xvar : varlist
gettoken xvar1 xvar2 : xvar
local colorg_by : word 1 of `colorgradient'
if "`by'" != "" {
	if `"`colorg_by'"' != "by" {
		local xvar3 `xvar2'
		local xvar2 `by'
	}
	else {
		local xvar3 `by'
	}
}
if "`swap'" == "swap" & "`by'" != "" {
	local oldxvar2 `xvar2'
	local xvar2 `xvar3'
	local xvar3 `oldxvar2'
}

qui replace `touse' = 0 if `xvar1' == . | `depvar' == .
local nvars : word count `xvar2'
if `nvars' > 1 error 103
local Options `options'

// Parse options and store them in loption`level' & eoption`level' OR loption & eoption

tempvar dummy

if "`xvar2'" == "" {
	qui gen `dummy' = 1 if `touse'
	local xvar2 `dummy'
}

qui levelsof `xvar2' if `touse'
local xvar2levels `r(levels)'
local firstxvar2level : word 1 of `xvar2levels'
foreach le in le l e f {
	tokenize `"``le'options'"', parse(";")
	foreach level in `xvar2levels' {
		local i = 1
		while "``i''" != "" {
			if "``i''" == ";" {
				local i = `i' + 1
				continue
			}

			// Take out the numlist part
			local `i'_test : subinstr local `i' "to" ""
			local numlist_n = indexnot(`"``i'_test'"', "1234567890()/:.-,[] ")
			local numlist = substr(`"``i''"', 1,`=`numlist_n'-1')
			local op = substr(`"``i''"', max(1, `numlist_n'), length(`"``i''"'))
			if "`numlist'" == "" | "`numlist'" == "." {
				if "`le'" == "le" {
					local loption`level' `loption`level'' ``i''
					local eoption`level' `eoption`level'' ``i''
					local foption`level' `foption`level'' ``i''
				}
				else {
					local `le'option`level' ``le'option`level'' ``i''
				}
				if "`plotorderoptions'" == "`plotorderoptions'" & "`level'" == "`firstxvar2level'" {
					if "`le'" == "le" {
						local lOption `lOption' ``i''
						local eOption `eOption' ``i''
						local fOption `fOption' ``i''
					}
					else {
						local `le'Option ``le'Option' ``i''
					}
				}
			}
			else {
				numlist "`numlist'"
				foreach levelo in `r(numlist)' {
					if "`level'" == "`levelo'" {
						if "`le'" == "le" {
							local loption`level' `loption`level'' `op'
							local eoption`level' `eoption`level'' `op'
							local foption`level' `foption`level'' `op'
						}
						else {
							local `le'option`level' ``le'option`level'' `op'
						}
					}
					if "`plotorderoptions'" == "`plotorderoptions'" & "`level'" == "`firstxvar2level'" {
						if "`le'" == "le" {
							local lOption`levelo' `lOption`levelo'' `op'
							local eOption`levelo' `eOption`levelo'' `op'
							local fOption`levelo' `fOption`levelo'' `op'
						}
						else {
							local `le'Option`levelo' ``le'Option`levelo'' `op'
						}
					}
				}
			}
			local i = `i' + 1
		}
	}
}

// Parse Other options
if "`statistic'" == "" local statistic mean
if "`bw'" == "bw" local scheme scheme(s2mono)
local nomarker = cond("`marker'" == "", "", "msymbol(none)")
if `"`addplot'"' != "" local argument4 `"|| `addplot' ||"'

// Parse errortype
if "`errortype'" != "" {
	local 0 , `errortype'
	syntax , [ci(numlist min=1 max=1) Collapse(varlist min=2 max=2) Quantile(numlist min=2 max=2) minmax sd SEmean SEBinomial SEPoisson iqr]

	if "`ci'" != "" local scaleparam semean
	if "`quantile'" != "" {
		tokenize `quantile'
		local extratocollapse (p`1') lbound = `depvar' (p`2') ubound = `depvar'
	}

	if "`minmax'" != "" {
		local extratocollapse (min) lbound = `depvar' (max) ubound = `depvar'
	}

	if "`collapse'" != "" {
		local var1 : word 1 of `collapse'
		local var2 : word 2 of `collapse'
		local extratocollapse (mean) lbound = `var1' (mean) ubound = `var2'
	}

	foreach type in sd semean sebinomial sepoisson {
		if "``type''" == "`type'" {
			local scaleparam `type'
		}
	}

	if `"`iqr'"' == "iqr" local extratocollapse (p25) lbound = `depvar' (p75) ubound = `depvar'

}

if `"`scaleparam'"' != "" local scale_collapse (`scaleparam') scaleparam=`depvar'

// Variable label
local ylabel : variable label `depvar'

// Collapse
qui collapse `alsocollapse' (`statistic') statistic=`depvar' `scale_collapse' (count) count=`depvar' ///
	`extratocollapse' if `touse' [`WeightExp'], by(`xvar1' `xvar2' `xvar3')

tempvar scaleparam2
if "`ci'" != "" & `"`scaleparam'"' == "semean" qui gen `scaleparam2' = invttail(count - 1,((100-`ci')/2)/100)*scaleparam
else if "`ci'" != "" & (`"`scaleparam'"' == "sepoisson" | `"`scaleparam'"' == "sebinomial") {
	qui gen `scaleparam2' = invnormal(1 - ((100-`ci')/2)/100)*scaleparam
}
else if `"`scaleparam'"' != "" qui gen `scaleparam2' = scaleparam

if `"`scaleparam'"' != "" {
	qui gen lbound = statistic - `scaleparam2'
	qui gen ubound = statistic + `scaleparam2'
}

// Setting colors
gr_setscheme, `scheme'
foreach colorpattern in color linepattern {
	foreach try in lineplot line "" {
		if "``colorpattern'order'" == "" {
			local `colorpattern'order `.__SCHEME.`colorpattern'.p1`try'' `.__SCHEME.`colorpattern'.p2`try'' `.__SCHEME.`colorpattern'.p3`try'' `.__SCHEME.`colorpattern'.p4`try'' `.__SCHEME.`colorpattern'.p5`try'' `.__SCHEME.`colorpattern'.p6`try'' ///
				`.__SCHEME.`colorpattern'.p7`try'' `.__SCHEME.`colorpattern'.p8`try'' `.__SCHEME.`colorpattern'.p9`try'' `.__SCHEME.`colorpattern'.p10`try'' `.__SCHEME.`colorpattern'.p11`try'' `.__SCHEME.`colorpattern'.p12`try'' ///
				`.__SCHEME.`colorpattern'.p13`try'' `.__SCHEME.`colorpattern'.p14`try'' `.__SCHEME.`colorpattern'.p15`try''
		}
		if "``colorpattern'order'" == "" local `colorpattern'order `.__SCHEME.`colorpattern'.p`try''
	}
	if "``colorpattern'order'" == "" {
		if "`colorpattern'" == "color" local `colorpattern'order black
		if "`colorpattern'" == "linepattern" local `colorpattern'order solid

		di "{txt}The current scheme does not have a default `colorpattern'"
		di "{txt}`colorpattern' set as {res}``colorpattern'order'"
	}
}

local colornumber : word count `colororder'
local patternnumber : word count `linepatternorder'

// Determining the look of the lines

qui tab `xvar2'
local max = r(r)

// Parse colorgradient
if "`colorgradient'" != "" {
	gettoken Color else : colorgradient
	if "`Color'" == "on" local Color : word 1 of `colororder'
	gettoken startcol endcol : else
	if lower(substr("`startcol'",1,3)) == "neg" {
		local startcol 1
		local endcol 0.1
	}
	else {
		if "`startcol'" == "" local startcol 0.1
		if "`endcol'" == "" local endcol 1
	}
	local interval = (`endcol' - `startcol')/(`max' - 1)
}

if "`xvar3'" != "" {
	qui levelsof `xvar3'
	local xvar3levels `r(levels)'
	local nlevels3 : word count `r(levels)'
	matrix yesno = J(`max', `nlevels3', 0)

}

local i = 1
local count = 0
local counter = 1
foreach level in `xvar2levels' {
	local ii = mod(`i'- 1,`colornumber' ) + 1
	local iii = mod(`i'- 1,`patternnumber' ) + 1
	if "`colorgradient'" != "" {
		local factor = `startcol' + (`i' - 1)*`interval'
		local precolor `Color'*`factor'
	}
	else local precolor : word `ii' of `colororder'
	local prepattern : word `iii' of `linepatternorder'

	if "`fit'" != "" {
		local line_option color(`precolor') lpat(blank)
	}
	else {
		local line_option color(`precolor') lpat(`prepattern')
	}

	if "`xvar3'" != "" {
		local line_option_copy `line_option'
		local j = 1
		foreach level3 in `xvar3levels' {
			qui count if `xvar3' == `level3' & `xvar2' == `level'
			if r(N) > 0 {
				if "`Color'" == "by" {
					local jj = mod(`j' - 1, `colornumber') + 1
					local C : word `jj' of `colororder'
					local line_option : subinstr local line_option_copy "color(by*" "color(`C'*"
				}
				if "`plotorderoptions'" == "plotorderoptions" local extraoptions `lOption' `lOption`counter''
				else local extraoptions `loption`level''
				local argument `argument' (connect statistic `xvar1' if `xvar2' == `level' & `xvar3' == `level3', `line_option' `nomarker' `extraoptions')
				local x3label`i'_`j' : label (`xvar3') `level3'
				local count = `count' + 1
				local counter = `counter' + 1
				matrix yesno[`i', `j'] = 1
			}
			local j = `j' + 1
		}
	}
	else 	{
		if "`plotorderoptions'" == "plotorderoptions" local extraoptions `lOption' `lOption`counter''
		else local extraoptions `loption`level''
		local argument `argument' (connect statistic `xvar1' if `xvar2' == `level', `line_option' `nomarker' `extraoptions')
		local counter = `counter' + 1
	}

	// generate labels for legend
	local x2label`i' : label (`xvar2') `level'
	local i = `i' + 1
}


// Determine look of errorbars

local argument2


if "`errortype'" != "" {

	local i = 1
	local counter = 1
	foreach level in `xvar2levels' {
		local ii = mod(`i'- 1,`colornumber' ) + 1
		local iii = mod(`i'- 1,`patternnumber' ) + 1
		if "`colorgradient'" != "" {
			local factor = `startcol' + (`i' - 1)*`interval'
			local precolor `Color'*`factor'
		}
		else local precolor : word `ii' of `colororder'
		local prepattern : word `iii' of `linepatternorder'

		local line_option color(`precolor') lpat(`prepattern')

		if "`xvar3'" != "" {
			local line_option_copy `line_option'
			local j = 1
			foreach level3 in `xvar3levels' {
				if yesno[`i',`j'] == 1 {
					if "`Color'" == "by" {
						local jj = mod(`j' - 1, `colornumber') + 1
						local C : word `jj' of `colororder'
						local line_option : subinstr local line_option_copy "color(by*" "color(`C'*"
					}
					if "`plotorderoptions'" == "plotorderoptions" local extraoptions `eOption' `eOption`counter''
					else local extraoptions `eoption`level''
					local argument2 `argument2' (rcap ubound lbound `xvar1' if `xvar2' == `level' & `xvar3' == `level3', `line_option' `extraoptions')
					local counter = `counter' + 1
				}
				local j = `j' + 1
			}
		}
		else {
			if "`plotorderoptions'" == "plotorderoptions" local extraoptions `eOption' `eOption`counter''
			else local extraoptions `eoption`level''
			local argument2 `argument2' (rcap ubound lbound `xvar1' if `xvar2' == `level' , `line_option' `extraoptions')
			local counter = `counter' + 1
		}
		local i = `i' + 1
	}
}

// Separating error bars

tempvar rank

qui egen `rank' = group(`xvar2' `xvar3')
qui sum `rank'
local nlevels = r(max)
qui sum `xvar1', meanonly
qui replace `xvar1' = `xvar1' + (`rank' - (`nlevels' + 1)/2) *  `separate' * (r(max) - r(min))

// Determine line of best fit

if "`fit'" != "" {

	local 0 `fit'
	syntax anything [, noWeight]
	if "`weight'" == "" & "`statistic'" == "mean" local aw aw=count

	qui tab `xvar2'
	local max = r(r)

	// generate argument for twoway command + labels for legend
	local i = 1
	local counter = 1
	foreach level in `xvar2levels' {

		local ii = mod(`i'- 1,`colornumber' ) + 1
		local iii = mod(`i'- 1,`patternnumber' ) + 1
		if "`colorgradient'" != "" {
			local factor = `startcol' + (`i' - 1)*`interval'
			local precolor `Color'*`factor'
		}
		else local precolor : word `ii' of `colororder'
		local prepattern : word `iii' of `linepatternorder'

		local line_option color(`precolor') lpat(`prepattern')

		if "`xvar3'" != "" {
			local j = 1
			local line_option_copy `line_option'
			foreach level3 in `xvar3levels' {
				if yesno[`i',`j'] == 1 {
					if "`Color'" == "by" {
						local jj = mod(`j' - 1, `colornumber') + 1
						local C : word `jj' of `colororder'
						local line_option : subinstr local line_option_copy "color(by*" "color(`C'*"
					}
					if "`plotorderoptions'" == "plotorderoptions" local extraoptions `fOption' `fOption`counter''
					else local extraoptions `foption`level''
					local argument3 `argument3' (`anything' statistic `xvar1' [`aw'] if `xvar2' == `level' & `xvar3' == `level3', `line_option' `extraoptions')
					local counter = `counter' + 1
				}
				local j = `j' + 1
			}
		}
		else {
			if "`plotorderoptions'" == "plotorderoptions" local extraoptions `fOption' `fOption`counter''
			else local extraoptions `foption`level''
			local argument3 `argument3' (`anything' statistic `xvar1' [`aw'] if `xvar2' == `level', `line_option' `extraoptions')
			local counter = `counter' + 1
		}

		local i = `i' + 1
	}
}


// Drawing the graph
sort `xvar1'
local properstat = proper("`statistic'")
local options_def `"ytitle(`"`properstat' `ylabel'"')"'

if `"`legend'"' == "nolegend" | `max' == 1 {
	local options_def `options_def' legend(off)
}
else {
	// Create default legend
	local options_def `options_def' legend(on order(

	if "`xvar3'" != "" {

		local k = 1
		forvalues i = 1/`max' {
			local labelled = 0
			forval j = 1/`nlevels3' {
				if `"`colorg_by'"' != "by" local extracond if `labelled' == 0
				else local include_x3label  , `x3label`i'_`j''

				if yesno[`i',`j'] == 1 {
					local labelled = 1
					if "`fit'" != "" {
						local l = `k' + `count'
						`extracond' local options_def `options_def' `l' `"Fitted line for `x2label`i''`include_x3label'"'
					}
					`extracond' local options_def `options_def' `k' `"`x2label`i''`include_x3label'"'
					local k = `k' + 1
				}
			}
		}
	}
	else {
		forvalues i = 1/`max' {
			if "`fit'" != "" {
				local j = `i' + `max'
				local options_def `options_def' `j' `"Fitted line for `x2label`i''"'
			}
			local options_def `options_def' `i' `"`x2label`i''"'
		}
	}

	local options_def `options_def' ))
}

foreach i in "" 2 3 {		// Get rid of the dummy variable
	local argument`i' : subinstr local argument`i' "if `dummy' == 1" "", all
}

twoway `argument' `argument3'  `argument2' `argument4', `options_def' `Options' `scheme'
return local command `argument' `argument3' `argument2' `argument4'
return local options `options_def' `Options' `scheme'

end