#include <origin.h>
#include <GetNBox.h>
#include <xfutils.h>
enum{
	distr_normal,
	distr_log,
	distr_exp,
	distr_weibull,
};
enum{
	method_blom,
	method_renard,
	method_hazen,
	method_var,
	method_km,
};
//put additional include files here
#include <event_utils.h>
#include <ReportTree.h>
#include <..\Originlab\grobj_utils.h>
#include <o8dlg.h>		///------ Folger 10/29/09 XF_HELP_MAPPING_TO_ORIGIN_CHM

/*------------------------------------------------------------------------------*
 * Modification Log:															*
 *	Folger 08/26/09 FAILS_TO_SPECIFY_PARAMETER_VALUES_MANUALLY					*
 *  Iris 9/01/2009 GOT_ERR_MSG_WHEN_INPUT_DATA_HAS_MISSING_VAL_FOR_WEIBULL_TYPE	*
 *  Iris 9/02/2009 FIX_AXES_TITLE_ANGLE_BUG_WHEN_RECALCULATE_ON_EXCHANGEDXY_GRAPH
 *  Iris 9/04/2009 CHANGE_INC_TO_1_WHEN_XAXIS_TYPE_IS_LN						*
 *  Iris 9/04/2009 KEEP_X_AXIS_TYPE_CONSISTENT_WITH_SOURCE_COL					*
 *  Iris 9/08/2009 ADD_EXCHANGE_XY_CHECKBOX										*
 *  Iris 9/08/2009 TRIM_MISSING													*
 * 	Iris 9/08/2009 IMPROVE_REPORT_DATA_LONG_NAME								*
 *  Iris 9/08/2009 ADD_ADDITIONAL_TABLE											*
 *  Iris 9/08/2009 FIX_RESET_THEME_IN_QQ_PLOT_WILL_PLOT_ONE_PP_GRAPH			*
 *  Iris 9/09/2009 NEED_CHECK_INPUT_IN_EVENT									*
 *	Folger 10/29/09 XF_HELP_MAPPING_TO_ORIGIN_CHM								*
 *  Iris 11/06/2009 QA81-14607-P2 SET_WEIBULL_AND_EXP_PLOT_GOOD_DEFAULT_Y_INC	*
 *  Iris 11/11/2009 ADJUST_RESCALE_MARGIN_BY_AXIS_REAL_SPACE					*
 *  Iris 11/12/2009 REMOVE_SCALE_CODES_FROM_XF_REPLACE_TO_IMPROVE_AUTO_RESCALE_IN_LOW_LEVEL*
 *  Iris 2/23/2010 FIX_GRAPH_TITLE_NOT_LOCALIZED								*
 *	Folger 04/06/10 QA81-15273 BETTER_NAME_FOR_PROPABILITY_PLOT_OUTPUT_WORKSHEET*
 *	Folger 04/07/10 QA81-15277 PLOT_PROBABILITY_FAILED_TO_UPDATE_SCALE_TYPE_AFTER_CHANGE_PARAM
 *	Folger 04/07/10 QA81-14644-P1 WEIBULL_PLOT_MISSING_REFERENCE_LINE_FOR_LARGE_DATA
 *	Folger 04/14/10 QA81-15277 NEED_ALWAYS_UPDATE_PROBABILITY_PLOT_OUTPUT_COLUMN_LONGNAME_AND_AXIS_TITLE
 *	Kyle 08/27/2010 ORG-927-P1 CALLING_ATTRIBUTE_REMOVED
 *	Max 10/29/2010 ORG-1356 AVOID_MISSING_REFERENCE_X_FOR_KAPLAN_MEIER
 *------------------------------------------------------------------------------*/

//put your own support static functions here
#define 	PROB_DATA_TBALE_ID				10
#define 	REF_DATA_TBALE_ID				11
#define 	ADDITIONAL_DATA_TBALE_ID		12

enum
{
	PP_PLOT = 0,
	QQ_PLOT
};

enum
{
	X_DATA_COL_ID = 100,
	Y_DATA_COL_ID,
	REF_X_COL_ID,
	REF_Y_COL_ID,
	ADDI_COL1_ID,
	ADDI_COL2_ID,
};

#define STR_DISTR_NORMAL		"Normal"
#define STR_DISTR_LOG			"Lognormal"
#define STR_DISTR_EXP			"Exponential"
#define STR_DISTR_WEIBULL		"Weibull"

static	void _calculate_from_to(double& dFrom, double& dTo, double dMin, double dMax)
{
	dFrom = dMin / 2;
	dTo = dMax + (100 - dMax) / 2;
}

/// Iris 9/08/2009 ADD_EXCHANGE_XY_CHECKBOX
static bool _layer_is_exchanged(GraphLayer& gl)
{
	if( !gl )
		return false;	
	GraphPage gp = gl.GetPage();
	
	string strLT;
	strLT.Format( "vv = %s!Layer%d.Exchangexy", gp.GetName(), gl.GetIndex()+1 );		
	LT_execute(strLT);
	
	double dIsExchangedXY = 0;
	LT_get_var("vv", &dIsExchangedXY);
	return (bool)dIsExchangedXY;
}

static void _exchange_xy(GraphLayer& gl, bool bExchange)
{
	if( bExchange == _layer_is_exchanged(gl) )
		return;
	
	if( !gl )
		return;
	GraphPage gp = gl.GetPage();
	
	string strLT;
	strLT.Format("%s!Layer%d.Exchangexy=%d", gp.GetName(), gl.GetIndex()+1, bExchange);
	LT_execute(strLT);
}
///end ADD_EXCHANGE_XY_CHECKBOX

/// Iris 11/10/2009 QA81-14607-S1 SET_GRAPH_SEETINGS_BY_TEMPLATE_REPLACE_CODE
static void _reload_template(GraphLayer& gl, TreeNode& trGetN)
{
	string strTemplate;
	if( PP_PLOT == trGetN.option.nVal )
	{
		switch(trGetN.distr.nVal)
		{
			case distr_normal:
				strTemplate = "PPNormal";
				break;
			case distr_log:
				strTemplate = "PPLog";
				break;
			case distr_exp:
				strTemplate = "PPExp";
				break;
			case distr_weibull:
				strTemplate = "PPWeibull";
				break;
			default:
				ASSERT(false);
				strTemplate = "PPNormal";
				break;				
		}
	}
	else
	{
		strTemplate = "QQ";
	}
	
	GraphPage gp = gl.GetPage();
	gp.LoadTemplate(strTemplate);
	if( !gl ) // gl will be invalid after load template
		gl = gp.Layers();
}
///end SET_GRAPH_SEETINGS_BY_TEMPLATE_REPLACE_CODE

///////////////////////////////////////////////////////////////////////
/////////////////////////Plotting Part Begin///////////////////////////
///////////////////////////////////////////////////////////////////////
BOOL	plot_prob_CreatePlots(GraphLayer& gl, DataRange& dr, TreeNode& trGetN, BOOL bFromAutoUpdate)
{
	if( !gl )
	{
		ASSERT(0);
		return false;
	}
	
	Tree trFormat;
	if( bFromAutoUpdate )
	{
		GraphPage gp = gl.GetPage();
		
		/// Iris 11/10/2009 QA81-14607-S1 SET_GRAPH_SEETINGS_BY_TEMPLATE_REPLACE_CODE
		// need keep original scale when do recalculate and change param.
		//DWORD dwProFilter = FPB_ALL & ~(FPB_SCALE | FPB_TEXT);
		DWORD dwProFilter = FPB_ALL & ~(FPB_TEXT);
		///end SET_GRAPH_SEETINGS_BY_TEMPLATE_REPLACE_CODE
		trFormat = gp.GetFormat(dwProFilter, FOB_ALL, true, true);
		
		/// Iris 9/02/2009 FIX_AXES_TITLE_ANGLE_BUG_WHEN_RECALCULATE_ON_EXCHANGEDXY_GRAPH
		// for exchanged xy axis graph, after load template, axis will be reset	
		/// Iris 9/08/2009 ADD_EXCHANGE_XY_CHECKBOX
		/*
		string strLT;
		strLT.Format( "vv = %s!Layer%d.Exchangexy", gp.GetName(), gl.GetIndex()+1 );		
		LT_execute(strLT);
		
		double dIsExchangedXY = 0;
		LT_get_var("vv", &dIsExchangedXY);
		if( 1 == dIsExchangedXY )
		{
			trFormat.Root.Layers.Layer1.Axes.X.Titles.BottomTitle.Angle.nVal = 0;
			trFormat.Root.Layers.Layer1.Axes.Y.Titles.LeftTitle.Angle.nVal = 90;
		}
		*/
		///------ Folger 04/07/10 QA81-15277 PLOT_PROBABILITY_FAILED_TO_UPDATE_SCALE_TYPE_AFTER_CHANGE_PARAM
		//trFormat.Root.Layers.Layer1.Axes.X.Titles.BottomTitle.Angle.Remove();
		//trFormat.Root.Layers.Layer1.Axes.Y.Titles.LeftTitle.Angle.Remove();
		TreeNode	trAxes = trFormat.Root.Layers.Layer1.Axes;
		if ( trAxes )
		{
			TreeNode	trX = trAxes.X;
			TreeNode	trY = trAxes.Y;
			if ( trX )
			{
				trX.Titles.BottomTitle.Angle.Remove();
				trX.Scale.Remove();
			}
			if ( trY )
			{
				trY.Titles.LeftTitle.Angle.Remove();
				trY.Scale.Remove();
			}
		}
		///------ End PLOT_PROBABILITY_FAILED_TO_UPDATE_SCALE_TYPE_AFTER_CHANGE_PARAM
		bool bExchanged = _layer_is_exchanged(gl);
		///end ADD_EXCHANGE_XY_CHECKBOX
		///end FIX_AXES_TITLE_ANGLE_BUG_WHEN_RECALCULATE_ON_EXCHANGEDXY_GRAPH
		
		/// Iris 11/10/2009 QA81-14607-S1 SET_GRAPH_SEETINGS_BY_TEMPLATE_REPLACE_CODE
		/*
		gp.LoadTemplate(PP_PLOT == trGetN.option.nVal ? "PP" : "QQ");
		if( !gl ) // gl will be invalid after load template
			gl = gp.Layers();
		*/
		_reload_template(gl, trGetN);
		///end SET_GRAPH_SEETINGS_BY_TEMPLATE_REPLACE_CODE
			
		/// Iris 9/08/2009 ADD_EXCHANGE_XY_CHECKBOX
		if( bExchanged )
			_exchange_xy(gl, true);
		///end ADD_EXCHANGE_XY_CHECKBOX
	}
	/// Iris 11/10/2009 QA81-14607-S1 SET_GRAPH_SEETINGS_BY_TEMPLATE_REPLACE_CODE
	else
	{
		_reload_template(gl, trGetN);
	}
	///end SET_GRAPH_SEETINGS_BY_TEMPLATE_REPLACE_CODE
	
	///////// start plotting
	Worksheet	wks;
	int			c1, c2;
	int			nRangeIndex = 1; //the first range is <empty>
	dr.GetRange(wks, c1, c2, nRangeIndex); 
	if ( !wks )
	{
		ASSERT(false);
		return FALSE;
	}
	
	int x1 = c1;
	int y1 = x1 + 1;
	if( y1 > c2 )
		return false;
	
	Curve crv1(wks, x1, y1);
	int nPlot1 = gl.AddPlot(crv1, IDM_PLOT_SCATTER);
	
	// plot reference line
	nRangeIndex = 2; //the first range is <empty>
	dr.GetRange(wks, c1, c2, nRangeIndex); 
	if ( !wks )
	{
		ASSERT(false);
		return FALSE;
	}
	
	int x2 = c1;
	int y2 = x2 + 1;	
	if( y1 > c2 )
		return false;
	
	Curve crv2(wks, x2, y2);
	int nPlot2 = gl.AddPlot(crv2, IDM_PLOT_LINE);	
	
	Dataset		dsY(wks, y1);
	double		dX1Min, dX1Max;
	double		dX2Min, dX2Max;
	double		dYMin, dYMax;
	dsY.GetMinMax(dYMin, dYMax);	
	
	_set_layer_format(trGetN, gl, dX2Min, dX2Max, dYMin, dYMax);	
	///////// end plotting
	
	if( bFromAutoUpdate )
	{
		GraphPage gp = gl.GetPage();
		bool bRet = gp.ApplyFormat(trFormat, true, true);
		ASSERT( bRet );
	}
	
	return true;
}

static string _get_distr_str_from_type(int distr)
{
	switch( distr )
	{
	case distr_normal:
		return STR_DISTR_NORMAL;
	case distr_log:
		return STR_DISTR_LOG;
	case distr_exp:
		return STR_DISTR_EXP;
	case distr_weibull:
		return STR_DISTR_WEIBULL;
	default:
		ASSERT(0);
	}
	return "";
}

static void _set_layer_format(TreeNode& trGetN, GraphLayer& gl, double dXMin, double dXMax, double dYMin, double dYMax)
{
	///Iris 11/11/2009 ADJUST_RESCALE_MARGIN_BY_AXIS_REAL_SPACE
	/*
	// change x & y axes type on distribute method
	double yfrom, yto;
	_calculate_from_to(yfrom, yto, dYMin, dYMax);
	*/
	///End ADJUST_RESCALE_MARGIN_BY_AXIS_REAL_SPACE
	//int xtype = SCALE_TYPE_LINEAR, ytype = SCALE_TYPE_LINEAR; /// Iris 11/10/2009 QA81-14607-S1 SET_GRAPH_SEETINGS_BY_TEMPLATE_REPLACE_CODE
	string strTitlePrefix = _get_distr_str_from_type(trGetN.distr.nVal);
	
	/// Iris 11/10/2009 QA81-14607-S1 SET_GRAPH_SEETINGS_BY_TEMPLATE_REPLACE_CODE
	/*
	switch(trGetN.distr.nVal)
	{
	case distr_normal:
		xtype = SCALE_TYPE_LINEAR;
		ytype = SCALE_TYPE_PROBABILITY;		
		break;
		
	case distr_log:
		xtype = SCALE_TYPE_LN;
		ytype = SCALE_TYPE_PROBABILITY;
		break;
		
	case distr_exp:
		xtype = SCALE_TYPE_LN;
		ytype = SCALE_TYPE_DOUBLE_LOG_RECIPROCAL;
		break;
		
	case distr_weibull:
		xtype = SCALE_TYPE_LN;
		ytype = SCALE_TYPE_DOUBLE_LOG_RECIPROCAL;
		break;
		
	default:
		ASSERT(0);
		break;
	}
	*/
	///end SET_GRAPH_SEETINGS_BY_TEMPLATE_REPLACE_CODE
	
	// get source column
	/// Iris 9/09/2009 FIX_RUNTIME_ERR_WHEN_WKS_BOOK_HAS_LONGNAME
	/*
	Column colSrc;
	okxf_resolve_string_get_origin_object(trGetN.irng.strVal, &colSrc);	
	*/
	DataRange irng;
	TreeNode trRange = trGetN.irng;
	okxf_resolve_tree_construct_range(&trRange, &irng);
	Column colSrc;
	bool bRet = _get_source_col(irng, colSrc);
	ASSERT(bRet);
	///end FIX_RUNTIME_ERR_WHEN_WKS_BOOK_HAS_LONGNAME
	
	int nSrcColFormat = colSrc.GetFormat();
	int nSrcColSubFormat = colSrc.GetSubFormat();		
	
	Tree tr;
	/// Iris 9/04/2009 KEEP_X_AXIS_TYPE_CONSISTENT_WITH_SOURCE_COL		
	if( distr_normal == trGetN.distr.nVal ) // only support for Normal since other distribute method is log type, log type not meaning for date/time format, so just keep numeric for other methods.
	{
		if( nSrcColFormat != OKCOLTYPE_TEXT && nSrcColFormat >= OKCOLTYPE_NUMERIC && nSrcColFormat <= OKCOLTYPE_WEEKDAY ) // col format from Numeric to WeekDay is one by one corresponding to the format of axis label format
		{
			tr.Root.Axes.X.Labels.BottomLabels.Type.nVal = nSrcColFormat;	
			tr.Root.Axes.X.Labels.BottomLabels.NumericFormat.nVal = nSrcColSubFormat;
			
			if( QQ_PLOT == trGetN.option.nVal ) // Y axis in QQ plot also need set same format with source column
			{
				tr.Root.Axes.Y.Labels.LeftLabels.Type.nVal = nSrcColFormat;	
				tr.Root.Axes.Y.Labels.LeftLabels.NumericFormat.nVal = nSrcColSubFormat;
			}
		}
	}
	///end KEEP_X_AXIS_TYPE_CONSISTENT_WITH_SOURCE_COL
	
	/// Iris 11/10/2009 QA81-14607-S1 SET_GRAPH_SEETINGS_BY_TEMPLATE_REPLACE_CODE
	/*
	if( PP_PLOT == trGetN.option.nVal ) // no need update axes type for qq plot
	{
		tr.Root.Axes.X.Scale.Type.nVal = xtype;
		tr.Root.Axes.Y.Scale.Type.nVal = ytype;
	}
	*/
	///end SET_GRAPH_SEETINGS_BY_TEMPLATE_REPLACE_CODE
	
	///Iris 11/12/2009 REMOVE_SCALE_CODES_FROM_XF_REPLACE_TO_IMPROVE_AUTO_RESCALE_IN_LOW_LEVEL
	/*
	///Iris 11/11/2009 ADJUST_RESCALE_MARGIN_BY_AXIS_REAL_SPACE
	if( PP_PLOT == trGetN.option.nVal )
	{
		double dRescaleMargin = 8; //default rescale margin for linear space
		bool bb = gl.ConvertByAxis(&dRescaleMargin, 1, OKAXISTYPE_Y, false);
		ASSERT(bb);
		tr.Root.Axes.Y.Scale.RescaleMargin.dVal = dRescaleMargin;
	}
	///End ADJUST_RESCALE_MARGIN_BY_AXIS_REAL_SPACE
	*/
	///End REMOVE_SCALE_CODES_FROM_XF_REPLACE_TO_IMPROVE_AUTO_RESCALE_IN_LOW_LEVEL
	
	if( 0 == gl.UpdateThemeIDs(tr.Root) )
		gl.ApplyFormat(tr, true, true);
	
	// rescale
	gl.Rescale(); 
	
	if( PP_PLOT == trGetN.option.nVal ) // no need change y from/to for qq plot
	{
		///Iris 11/11/2009 ADJUST_RESCALE_MARGIN_BY_AXIS_REAL_SPACE
		//gl.Y.From = yfrom;
		//gl.Y.To = yto;	
		///end ADJUST_RESCALE_MARGIN_BY_AXIS_REAL_SPACE
		
		/// Iris 11/10/2009 QA81-14607-S1 SET_GRAPH_SEETINGS_BY_TEMPLATE_REPLACE_CODE
		/*
		//---- Iris 9/04/2009 CHANGE_INC_TO_1_WHEN_XAXIS_TYPE_IS_LN
		//if ( distr_weibull == trGetN.distr.nVal )		
		if ( distr_weibull == trGetN.distr.nVal || distr_log == trGetN.distr.nVal || distr_exp == trGetN.distr.nVal )
		//----
			gl.X.Inc = 1;
		/// Iris 11/06/2009 QA81-14607-P2 SET_WEIBULL_AND_EXP_PLOT_GOOD_DEFAULT_Y_INC
		if ( distr_weibull == trGetN.distr.nVal || distr_exp == trGetN.distr.nVal )
			gl.Y.Inc = 1;
		///end SET_WEIBULL_AND_EXP_PLOT_GOOD_DEFAULT_Y_INC
		*/
		///end SET_GRAPH_SEETINGS_BY_TEMPLATE_REPLACE_CODE		
	}	
	
	// add text to Text label
	string strText = _get_label_text(trGetN.location);
	string strTemp = _get_label_text(trGetN.scale);
	if( !strTemp.IsEmpty() )
		strText += "  " + strTemp;
	strText.TrimLeft();
	strText.TrimRight();
	
	//GraphObject 	go = gl.GraphObjects("Text");
	//ASSERT( go );
	//if( !strText.IsEmpty() && go )
	//{
		//go.Text = strText;
	//}
		
	// format the Title of Graph and X/Y axes labels
	string strColName = colSrc.GetLongName().IsEmpty() ? colSrc.GetName() : colSrc.GetLongName();			
	
	// Title
	GraphObject go = gl.GraphObjects("Title");
	if( go )
	{
		string str;
		///Iris 2/23/2010 FIX_GRAPH_TITLE_NOT_LOCALIZED
		//str.Format("%s %s Plot of %s", strTitlePrefix, PP_PLOT == trGetN.option.nVal? "Probability" : "Q-Q", "%(?X)");
		str.Format("%s %s Plot of", strTitlePrefix, PP_PLOT == trGetN.option.nVal? "Probability" : "Q-Q");
		str += " %s";
		str = _L(str);
		str.Format(str, "%(?X)");
		///End FIX_GRAPH_TITLE_NOT_LOCALIZED
		str += "\r\n\p81(" + strText + ")";		/// 81% of normal font size
		go.Text = str;
	}
	
	//------ Iris 9/08/2009 IMPROVE_REPORT_DATA_LONG_NAME changed xy axes title to %(?X) and %(?Y), will be auto update on plotting data column.
	/*
	// X/Y axes label
	go = gl.GraphObjects("XB");
	if( go )
		go.Text = strColName;
	
	go = gl.GraphObjects("YL");
	if( go )
	{
		string str;
		if( PP_PLOT == trGetN.option.nVal )
		{
			str.Format("%s %s", strTitlePrefix, "Percentiles");
		}
		else
		{
			str.Format("Expected %s Value", strTitlePrefix);
		}
		go.Text = str;
	}
	*/
	//------
	
	_exchange_xy(gl, trGetN.exchange.nVal); /// Iris 9/08/2009 ADD_EXCHANGE_XY_CHECKBOX
}

static string _get_label_text(TreeNode& trN)
{
	string strText;
	if( trN.Show )
	{
		trN.GetAttribute(STR_LABEL_ATTRIB, strText);
		strText += " = " + ftoa(trN.dVal, "*");
	}
	return strText;
}
///////////////////////////////////////////////////////////////////////
/////////////////////////Plotting Part End////////////////////////////
///////////////////////////////////////////////////////////////////////

static void _get_normalize_data(int option, int distr, int method, double location, double scale, int n, vector& vNormal)
{
	vNormal.SetSize(n);
	
	switch(method)
	{
	case method_blom:
		for(int ii = 0; ii < n; ii++)
			vNormal[ii] = (ii+1-3.0/8.0)/(n+1.0/4.0);
		break;
		
	case method_renard:
		for(int ii = 0; ii < n; ii++)
			vNormal[ii] = (double)(ii+1-0.3)/(double)(n+0.4);
		break;
		
	case method_hazen:
		for(int ii = 0; ii < n; ii++)		
			vNormal[ii] = (ii+1-1.0/2.0)/(double)n;
		break;
		
	case method_var:
		for(int ii = 0; ii < n; ii++)		
			 vNormal[ii] = (double)(ii+1)/(double)(n+1);
		break;
		
	case method_km:
		for(int ii = 0; ii < n; ii++)		
			 vNormal[ii] = (double)(ii+1)/(double)n;
		break;
		
	default:
		ASSERT(0);
		break;
	}
	
	if( PP_PLOT == option )
	{
		//if( distr_normal == distr || distr_log == distr )
			vNormal *= 100;
	}
	
	if( QQ_PLOT == option )
	{
		switch( distr )
		{
		case distr_normal:
			for(int ii = 0; ii < n; ii++)
				vNormal[ii] = norminv(vNormal[ii]) * scale + location;
			break;
			
		case distr_log:
			for(int ii = 0; ii < n; ii++)
				vNormal[ii] = exp(norminv(vNormal[ii]) * scale + location);
			break;
			
		case distr_exp:
			for(int ii = 0; ii < n; ii++)
				vNormal[ii] = scale * -1 * ln( 1-vNormal[ii] );
			break;
			
		case distr_weibull:
			for(int ii = 0; ii < n; ii++)
				vNormal[ii] = wblinv(vNormal[ii], scale, location);
			break;
			
		default:
			ASSERT(0);
			break;
		}
	}
}

static void _get_reference_line_data(vector& vRefX, vector& vRefY, const vector& vx, const vector& vNormal, int option, int distr, double location, double scale)
{
	vRefX.SetSize(2);
	vRefY.SetSize(2);
	
	if( option == QQ_PLOT )
	{
		//double xmin, xmax, ymin, ymax, inc;
		//vx.GetMinMax(xmin, xmax);
		//vNormal.GetMinMax(ymin, ymax);	
		//double min = min(xmin, ymin);
		//double max = max(xmax, ymax);
		double min, max, inc;
		vNormal.GetMinMax(min, max);
		RoundLimits(&min, &max, &inc, 10);
		vRefX[0] = min;
		vRefX[1] = max;
		vRefY = vRefX;
		return;
	}
	
	if( option == PP_PLOT )
	{
		//vRefY[0] = 0.00001;
		//vRefY[1] = 0.99999;
		double		dMin, dMax;
		
		///Max 10/29/2010 ORG-1356 AVOID_MISSING_REFERENCE_X_FOR_KAPLAN_MEIER
		//vNormal.GetMinMax(dMin, dMax);
		int		nMin, nMax;
		vNormal.GetMinMax(dMin, dMax, &nMin, &nMax);
		///End AVOID_MISSING_REFERENCE_X_FOR_KAPLAN_MEIER
		
		///Max 10/29/2010 ORG-1356 AVOID_MISSING_REFERENCE_X_FOR_KAPLAN_MEIER
		if(dMax >= 100)	//best to pass the variable "method" to check if Kaplan_Meier, I now use the trick check
		{
			vector vTemp;
			vTemp = vNormal;
			vTemp[nMax] = -1;
			double	dMinTemp;
			vTemp.GetMinMax(dMinTemp, dMax);
		}
		ASSERT(dMax < 100);
		/// END AVOID_MISSING_REFERENCE_X_FOR_KAPLAN_MEIER
		
		_calculate_from_to(vRefY[0], vRefY[1], dMin, dMax);
		vRefY /= 100;
		
		switch(distr)
		{
		case distr_normal:		
			vRefX[0] = norminv( vRefY[0] ) * scale + location;
			vRefX[1] = norminv( vRefY[1] ) * scale + location;
			break;
		case distr_log:
			vRefX[0] = exp(norminv(vRefY[0]) * scale + location);
			vRefX[1] = exp(norminv(vRefY[1]) * scale + location);
			break;
		case distr_exp:
			vRefX[0] = -1 * scale * ln(1 - vRefY[0]);
			vRefX[1] = -1 * scale * ln(1 - vRefY[1]);
			break;
		case distr_weibull:
			vRefX[0] = wblinv( vRefY[0], scale, location );
			vRefX[1] = wblinv( vRefY[1], scale, location );
			break;
		default:
			ASSERT(0);
			break;
		}
		
		//bool bShowPercentY = (distr_normal == distr || distr_log == distr );
		//if( bShowPercentY )
			vRefY *= 100;
	}
	
	///------ Folger 04/07/10 QA81-14644-P1 WEIBULL_PLOT_MISSING_REFERENCE_LINE_FOR_LARGE_DATA
	if ( distr_exp == distr || distr_weibull == distr )
	{
		int		nSize = vNormal.GetSize() / 1000;
		if ( nSize > 2 )
		{			
			vector	vTempX, vTempY;
			vTempX = vRefX, vTempY = vRefY;
			
			double	xmin = min(vRefX);
			double	xmax = max(vRefX);
			vRefX.Data(xmin, xmax, (xmax - xmin) / (nSize - 1));
			vRefY.SetSize(nSize);
			
			int		nXScale = LOG10_SPACE;
			int		nYScale = DBL_LOG_SPACE;
			
			vRefX = real_space(vRefX, nXScale);
			vTempX = real_space(vTempX, nXScale);
			vTempY = real_space(vTempY, nYScale);
			
			if ( OE_NOERROR == ocmath_interpolate(vRefX, vRefY, nSize, vTempX, vTempY, vTempX.GetSize(), INTERP_TYPE_LINEAR) )
			{
				vRefX = real_inv_space(vRefX, nXScale);
				vRefY = real_inv_space(vRefY, nYScale);
			}
			else
			{
				ASSERT(FALSE);
			}
		}
	}
	///------ End WEIBULL_PLOT_MISSING_REFERENCE_LINE_FOR_LARGE_DATA
}

static void _get_label_on_distribution_type(int distr, string& strLocationLabel, string& strScaleLabel)
{
	switch(distr)
	{
	case distr_normal:
		strLocationLabel = _L("mu");
		strScaleLabel = _L("sigma");
		break;
	case distr_log:
	case distr_weibull:
		strLocationLabel = _L("shape");
		strScaleLabel = _L("scale");
		break;
	case distr_exp:
		strScaleLabel = _L("scale");
		break;
	default:
		ASSERT(0);
		break;
	}
}

static void _gui_update_show_and_labels(TreeNode& trGetN)
{
	bool bEnable = 0 == trGetN.estimate.nVal;
	trGetN.location.Enable = bEnable;
	trGetN.scale.Enable = bEnable;
	
	trGetN.location.Show = (distr_exp != trGetN.distr.nVal);
	
	// labels
	string strLocationLabel, strScaleLabel;
	_get_label_on_distribution_type(trGetN.distr.nVal, strLocationLabel, strScaleLabel);
	if( !strLocationLabel.IsEmpty() )
		trGetN.location.SetAttribute(STR_LABEL_ATTRIB, strLocationLabel);
	if( !strScaleLabel.IsEmpty() )
		trGetN.scale.SetAttribute(STR_LABEL_ATTRIB, strScaleLabel);
	
}

static void _auto_update_scale_vals(TreeNode& trGetN, bool& bOKEnable, string& strErrMsg)
{
	DataRange dr;
	TreeNode trIn = trGetN.irng;
	okxf_resolve_tree_construct_range(&trIn, &dr);
	
	vector vIn;
	DWORD dwRules = DRR_NO_WEIGHTS|DRR_GET_MISSING;
	DWORD dwPlotID;
	if( dr.GetData(dwRules, 0, &dwPlotID, NULL, &vIn) < 0 )
		XF_THROW(XFERR_INVALID_RANGE);
	
	vector<uint> vn;
	//---- Iris 10/09/2009 fix negative checking only once time when input data include negative, choose exp as distr(--> err msg show) and then change to log distr(==> not err).
	//if( strErrMsg.IsEmpty() && distr_normal != trGetN.distr.nVal && vIn.Find(MATREPL_TEST_LESSTHAN | MATREPL_TEST_EQUAL, 0, vn) > 0 )
	if( bOKEnable && distr_normal != trGetN.distr.nVal && vIn.Find(MATREPL_TEST_LESSTHAN | MATREPL_TEST_EQUAL, 0, vn) > 0 )
	//----
	{
		bOKEnable = false;
		strErrMsg = XFERR_NEGATIVE_DATA_FAILED_FOR_DISTR;
		strErrMsg += ":" + _get_distr_str_from_type( trGetN.distr.nVal );
		return;
	}
	
	///------ Folger 08/26/09 FAILS_TO_SPECIFY_PARAMETER_VALUES_MANUALLY
	if ( !trGetN.estimate.nVal )
		return;
	///------ End FAILS_TO_SPECIFY_PARAMETER_VALUES_MANUALLY
	
	double dLocation = NANUM, dScale = NANUM;
	switch(trGetN.distr.nVal)
	{
	case distr_normal:
		int 	n;
		double 	mean, std;
		ocmath_basic_summary_stats(vIn.GetSize(), vIn, &n, &mean, &std);
		dLocation = mean;
		dScale = std;
		break;
		
	case distr_log:
		vector vlnData;
		vlnData = ln(vIn);
		
		int 	n;
		double 	mean, std;
		ocmath_basic_summary_stats(vlnData.GetSize(), vlnData, &n, &mean, &std);
		dLocation = mean;
		dScale = std;
		break;
		
	case distr_exp:		
		int 	n;
		double 	mean;
		ocmath_basic_summary_stats(vIn.GetSize(), vIn, &n, &mean, NULL);
		dScale = mean;
		break;
		
	case distr_weibull:	
		vIn.Trim(); /// Iris 9/01/2009 GOT_ERR_MSG_WHEN_INPUT_DATA_HAS_MISSING_VAL_FOR_WEIBULL_TYPE
		vector vCensor(vIn.GetSize());
		vCensor = 1;
		double dCensored = 0;		
	    double dConfLevel=0.05; 
	    
	    double dCriterion;
		SASummary stSASummary;    
	    SAParaEstim psSAParaEstim[4];
	    int nSizeParaEstim = 4;	    
		int nRet = ocmath_surviv_weibull_simple(vIn, vIn.GetSize(), vCensor, vCensor.GetSize(), &dCensored, 1, dConfLevel, &dCriterion, &stSASummary, psSAParaEstim, nSizeParaEstim);
		ASSERT( nRet == STATS_NO_ERROR );
		if( STATS_NO_ERROR == nRet )
		{
			dScale = psSAParaEstim[2].ParaEst;
			dLocation = psSAParaEstim[3].ParaEst;
		} 
		else if( 61 == nRet ) // nag err
		{
			bOKEnable = false;
			strErrMsg = XFERR_NEGATIVE_DATA_FAILED_FOR_DISTR;
			strErrMsg += ":" + _get_distr_str_from_type( trGetN.distr.nVal );
		}
		else
		{
			bOKEnable = false;
			strErrMsg = XFERR_CANNOT_ESTIMATE_WEIBULL_PARAMS;
		}
		break;
		
	default:
		ASSERT(0);
		break;
	}
	
	
	if( distr_exp != trGetN.distr.nVal )
		trGetN.location.dVal = dLocation;
	trGetN.scale.dVal = dScale;
	
}

static bool _get_source_col(Range& irng, Column& colSrc)
{
	Worksheet wks;
	int c1, c2;
	irng.GetRange(wks, c1, c2);
	ASSERT( wks && c1 == c2);
	
	colSrc.Attach(wks, c1);		
	return colSrc.IsValid();
}


void plot_prob(const Range& irng, const int& option, const int& distr, const int& estimate, const double& location, const double& scale, const int& method, const int& exchange, ReportData& rd)
{
	
	// get input data
	int nErrCode = check_1_data(irng);
	if (CER_NO_ERROR != nErrCode)
	{
		XF_THROW(nErrCode);
	}	
	
	vector vIn;
	DWORD dwRules = DRR_NO_WEIGHTS|DRR_GET_MISSING;
	DWORD dwPlotID;
	if( irng.GetData(dwRules, 0, &dwPlotID, NULL, &vIn) < 0 )
		XF_THROW(XFERR_INVALID_RANGE);
	vIn.Trim(); /// Iris 9/08/2009 TRIM_MISSING
	
	// get x, y
	vector vx, vy;
	vx = vIn;
	vx.Sort();		
	_get_normalize_data(option, distr, method, location, scale, vx.GetSize(), vy);
	
	// get reference line data
	 vector vRefX, vRefY;
	_get_reference_line_data(vRefX, vRefY, vx, vy, option, distr, location, scale);
	
	/// Iris 9/09/2009 FIX_RUNTIME_ERR_WHEN_WKS_BOOK_HAS_LONGNAME
	/*
	Column colSrc;
	okxf_resolve_string_get_origin_object(irng.GetDescription(), &colSrc);
	*/	
	Column colSrc;	
	bool bRet = _get_source_col(irng, colSrc);
	ASSERT(bRet);
	///end FIX_RUNTIME_ERR_WHEN_WKS_BOOK_HAS_LONGNAME
	
	///------ Folger 04/14/10 QA81-15277 NEED_ALWAYS_UPDATE_PROBABILITY_PLOT_OUTPUT_COLUMN_LONGNAME_AND_AXIS_TITLE
	rd.SetAttribute(STR_REPORT_TREE_ALWAYS_UPDATE_COLUMN_LABELS_ATTRIB, 1);
	///------ End NEED_ALWAYS_UPDATE_PROBABILITY_PLOT_OUTPUT_COLUMN_LONGNAME_AND_AXIS_TITLE
	
	// output 
	ReportTable rt = rd.CreateTable("normal", PP_PLOT == option ? _L("Percentiles") : _L("Expected Value"), PROB_DATA_TBALE_ID);
	TreeNode trX = rt.AddColumn(vx, "X", X_DATA_COL_ID, _L("X"), OKDATAOBJ_DESIGNATION_X);
	/// Iris 9/08/2009 IMPROVE_REPORT_DATA_LONG_NAME
	string strColName = colSrc.GetLongName().IsEmpty()? colSrc.GetName() : colSrc.GetLongName();
	trX.SetAttribute(STR_LABEL_ATTRIB, strColName);
	///END IMPROVE_REPORT_DATA_LONG_NAME
	
	TreeNode trY = rt.AddColumn(vy, "Y", Y_DATA_COL_ID, _L("Y"), OKDATAOBJ_DESIGNATION_Y);	
	/// Iris 9/08/2009 IMPROVE_REPORT_DATA_LONG_NAME
	string 			strYLabel, strDistribution;
	TreeNode 		trGUI;
	XFExeContext	*pContext = Project.GetCurrentXFExeCtxt();
	if( pContext )
	{
		Tree trRoot;
		pContext->GetGUI(trRoot, trGUI);
		string strCombo;
		trGUI.distr.GetAttribute(STR_COMBO_ATTRIB, strCombo);
		strDistribution = strCombo.GetToken(distr, '|');		
		strYLabel.Format(PP_PLOT == option ? _L("%s Percentiles") : _L("Expected %s Value"), strDistribution);
	}
	trY.SetAttribute(STR_LABEL_ATTRIB, strYLabel);
	///END IMPROVE_REPORT_DATA_LONG_NAME
	
	/// Iris 9/04/2009 KEEP_X_AXIS_TYPE_CONSISTENT_WITH_SOURCE_COL
	if( distr_normal == distr ) // only support for Normal since other distribute method is log type, log type not meaning for date/time format, so just keep numeric for other methods.
	{
		///Kyle 08/27/2010 ORG-927-P1 CALLING_ATTRIBUTE_REMOVED
		//trX.SetAttribute(STR_COL_FORMAT_SRC_COL_UID_ATTRIB, colSrc.GetUID(true));
		//if( QQ_PLOT == option )
		//	trY.SetAttribute(STR_COL_FORMAT_SRC_COL_UID_ATTRIB, colSrc.GetUID(true));
		int nSrcUID = colSrc.GetUID(true);
		DWORD dwCntrl = SRC_COL_PROPERTY_FORMAT;
	
		okutil_source_column_property(&trX, &nSrcUID, &dwCntrl, false);
		if( QQ_PLOT == option )
			okutil_source_column_property(&trY, &nSrcUID, &dwCntrl, false);
		///End CALLING_ATTRIBUTE_REMOVED
	}
	///end KEEP_X_AXIS_TYPE_CONSISTENT_WITH_SOURCE_COL
	
	ReportTable rtRef = rd.CreateTable("ref", _L("Reference Line"), REF_DATA_TBALE_ID);
	rtRef.AddColumn(vRefX, "X", REF_X_COL_ID, _L("X"), OKDATAOBJ_DESIGNATION_X);
	rtRef.AddColumn(vRefY, "Y", REF_Y_COL_ID, _L("Y"), OKDATAOBJ_DESIGNATION_Y);
	
	/// Iris 9/08/2009 ADD_ADDITIONAL_TABLE
	// Additional Info columns
	vector<string> vsAddiLabel, vsAddiText;
	// col 1
	vsAddiLabel.Add( _L("Input Data") );
	vsAddiText.Add( irng.GetDescription() );
	
	// col 2
	vsAddiLabel.Add( _L("Distribution") );
	vsAddiText.Add( strDistribution );
	
	// col 3
	string strLocationLabel, strScaleLabel;
	_get_label_on_distribution_type(distr, strLocationLabel, strScaleLabel);
	if( distr_exp != distr )
	{
		vsAddiLabel.Add(strLocationLabel);
		vsAddiText.Add( ftoa(trGUI.location.dVal) );
	}
	
	// col 4
	vsAddiLabel.Add(strScaleLabel);
	vsAddiText.Add( ftoa(trGUI.scale.dVal) ); 
	
	ReportTable rtAdditional = rd.CreateTable("Additional", _L("Additional Table"), ADDITIONAL_DATA_TBALE_ID);
	rtAdditional.AddColumn(vsAddiLabel, "col1", ADDI_COL1_ID, _L("Additional Info"), OKDATAOBJ_DESIGNATION_X);
	rtAdditional.AddColumn(vsAddiText, "col2", ADDI_COL2_ID, _L("Description"), OKDATAOBJ_DESIGNATION_Y);
	///end ADD_ADDITIONAL_TABLE
	
	// rename report sheet
	Worksheet wksOut;
	if( rd.GetWorksheet(wksOut) )
	{
		///------ Folger 04/06/10 QA81-15273 BETTER_NAME_FOR_PROPABILITY_PLOT_OUTPUT_WORKSHEET
		//string strWksName = PP_PLOT == option? "Prob Plot Data 1" : "Q-Q Plot Data 1";
		string		strWksName;
		strWksName.Format("%s1", _get_distr_str_from_type(distr));
		///------ End BETTER_NAME_FOR_PROPABILITY_PLOT_OUTPUT_WORKSHEET
		wksOut.SetName(strWksName, OCD_ENUM_NEXT);
	}	
	
}

//nEventID < 0 on event, like GETNE_ON_INIT, GETNE_ON_OK etc, >0 on change from control
//return 0 to do nothing, return PEVENT_GETN_RET_TO_CLOSE to close dialog
//dwEnables = bOKEnable, bBTN1Enable, bBTN2Enable, bBTN3Enable, , can set to false to disable OK/Apply buttons
//lpcszNodeName = the trGetN TreeNode tag name that generates the event, or is  if GETNE_ON_OK etc
//strAux = optional string for XF Wizard Bar, like DLG_NEED_CHANGE_GRID etc, it will be invalid if from internal node value change event
//strErrMsg = allowing messages to be shown
int plot_prob_event1(TreeNode& trGetN, int nRow, int nEventID, DWORD& dwEnables, LPCSTR lpcszNodeName, WndContainer& DynaCntrlContainer, string& strAux, string& strErrMsg)
{
	if( xf_is_extended_event(nEventID) )
		return plot_prob_event1_ex(trGetN, nRow, nEventID, dwEnables, lpcszNodeName, DynaCntrlContainer, strAux, strErrMsg);
	DECLARE_BUTTON_ENABLES
	
	bOKEnable = dwEnables & GETNGEVT_OK_ENABLE;
	
	/// Iris 9/09/2009 NEED_CHECK_INPUT_IN_EVENT
	int nErrCode = check_1_data(trGetN.irng);
	if (CER_NO_ERROR != nErrCode)
	{
		strErrMsg = nErrCode;
		bOKEnable = false;
	}	
	///end NEED_CHECK_INPUT_IN_EVENT
	
	_auto_update_scale_vals(trGetN, bOKEnable, strErrMsg);
	
	if( bOKEnable )
		dwEnables |= GETNGEVT_OK_ENABLE;
	else
		dwEnables &= ~GETNGEVT_OK_ENABLE;
	
	_gui_update_show_and_labels(trGetN);	
	return true;

}

int plot_prob_make_tree(TreeNode& tr, LPCSTR lpcszVarName)
{
	return false;
}
//nGetNDialog = 0,1,2, corresponding to the XF GetN dropdown, 0 = dialog is not used
//you can change nRet to be one of XFEVT_PROCEED (defualt), XFEVT_ABORT, XFEVT_PROCEED_NO_DLG or XFEVT_PROCEED_WITH_DLG(when nGetNDialog = 0)
//nGetNDialog = -1 when Ok on GetN dialog to allow handler to preocess <auto> etc variables
//dwCntrl is combination of bits
//LTXF_FROM_GUI_MENU, LTXF_FROM_GUI_PROMPT, LTXF_FROM_AUTO_UPDATE, LTXF_FROM_FUNCTION, LTXF_THEME_USED, LTXF_CHANGE_PARAM
void plot_prob_before_execute(TreeNode& trGetN, int nGetNDialog, int& nRet, int dwCntrl)
{
	
	///------ Folger 10/29/09 XF_HELP_MAPPING_TO_ORIGIN_CHM
	if ( is_xf_startup(nGetNDialog, dwCntrl) )
	{
		if ( !O_QUERY_BOOL(dwCntrl, LTXF_SHOW_DIALOG) )
		{
			bool	bRet = TRUE;
			string	strErrMsg;
			_auto_update_scale_vals(trGetN, bRet, strErrMsg);
			if ( !bRet )
			{
				nRet = XFEVT_PROCEED_WITH_DLG;
				return;
			}
		}
		trGetN.SetAttribute(STR_HELPID_ATTRIB, IDD_XF_plot_prob);
	}
	///------ End XF_HELP_MAPPING_TO_ORIGIN_CHM
	
	/// Iris 9/08/2009 ADD_EXCHANGE_XY_CHECKBOX
	// set exchanged state from graph to GUI before dialog open
	if( -1 != nGetNDialog ) //nGetNDialog = -1 when Ok on GetN dialog
	{
		int nGraphUID;
		if( trGetN.AutoUpdate && trGetN.AutoUpdate.GetAttribute(STR_XF_BASED_PLOTTING_GRAPH_UID_ATTRIB, nGraphUID) )
		{
			GraphLayer gl;
			gl = (GraphLayer)Project.GetObject(nGraphUID);
			if( gl )
			{
				trGetN.exchange.nVal = _layer_is_exchanged(gl);
			}
		}
	}
	///end ADD_EXCHANGE_XY_CHECKBOX
	
	/// Iris 9/08/2009 FIX_RESET_THEME_IN_QQ_PLOT_WILL_PLOT_ONE_PP_GRAPH
	if( is_numeric(trGetN.option.strVal) )
	{
		trGetN.option.SetAttribute("Value", trGetN.option.strVal);
	}
	int nVal;
	if( PDS_UNASSIGNED == okutil_cvt_str_to_predefined_type(trGetN.option.strVal) && trGetN.option.GetAttribute("Value", nVal) )
	{
		trGetN.option.nVal = nVal;	
	}
	///end FIX_RESET_THEME_IN_QQ_PLOT_WILL_PLOT_ONE_PP_GRAPH
	
}
//nEventID < 0 on event, like GETNE_ON_INIT, GETNE_ON_OK etc, >0 on change from control
//return 0 to do nothing, return PEVENT_GETN_RET_TO_CLOSE to close dialog
//dwEnables = bOKEnable, bBTN1Enable, bBTN2Enable, bBTN3Enable, , can set to false to disable OK/Apply buttons
//lpcszNodeName = the trGetN TreeNode tag name that generates the event, or is  if GETNE_ON_OK etc
//strAux = optional string for XF Wizard Bar, like DLG_NEED_CHANGE_GRID etc, it will be invalid if from internal node value change event
//strErrMsg = allowing messages to be shown
int plot_prob_event1_ex(TreeNode& trGetN, int nRow, int nEventID, DWORD& dwEnables, LPCSTR lpcszNodeName, WndContainer& DynaCntrlContainer, string& strAux, string& strErrMsg)
{
	return false;
}
