/*------------------------------------------------------------------------------*
 * File Name:	Cluster.c	 													*
 * Creation: 	Sophy 3/9/2010													*
 * Purpose: OriginC Source C file												*
 * Copyright (c) OriginLab Corp.2004, 2005, 2006, 2007, 2008, 2009, 2010		*
 * All Rights Reserved															*
 * 																				*
 * Modification Log:															*
 *------------------------------------------------------------------------------*/
 
////////////////////////////////////////////////////////////////////////////////////
// Including the system header file Origin.h should be sufficient for most Origin
// applications and is recommended. Origin.h includes many of the most common system
// header files and is automatically pre-compiled when Origin runs the first time.
// Programs including Origin.h subsequently compile much more quickly as long as
// the size and number of other included header files is minimized. All NAG header
// files are now included in Origin.h and no longer need be separately included.
//
// Right-click on the line below and select 'Open "Origin.h"' to open the Origin.h
// system header file.
#include <Origin.h>
////////////////////////////////////////////////////////////////////////////////////

//#pragma labtalk(0) // to disable OC functions for LT calling.

////////////////////////////////////////////////////////////////////////////////////
// Include your own header files here.
#include <..\Originlab\grobj_utils.h>
#include <..\Originlab\GraphObjTools.h>
#include <..\Originlab\GraphObjToolDlg.h>
#include <..\Originlab\QuickStats.h>
////////////////////////////////////////////////////////////////////////////////////
// Start your functions here.

bool	init_shape_position(TreeNode& trGUI, bool bCheckInitAttrib/* = true*/)
{
	if ( !trGUI )
		return false;
	
	TreeNode trROI = trGUI.roi;
	if ( !trROI )
		return false;
	
	TreeNode trShapeProp = trROI.shapeProp;
	if ( !trShapeProp )
		return false;
	
#define	STR_SHAPE_INIT	"InitValue"
	int nInit = 0;
	if ( bCheckInitAttrib && trShapeProp.GetAttribute(STR_SHAPE_INIT, nInit) && nInit == 1 )
		return false; //already init
	
	GraphLayer gl = Project.ActiveLayer();
	if ( gl )
	{
		double dX1, dX2, dY1, dY2;
		dX1 = gl.X.From;
		dX2 = gl.X.To;
		dY1 = gl.Y.From;
		dY2 = gl.Y.To;
		trShapeProp.xCenter.dVal = (dX1 + dX2)/2;
		trShapeProp.yCenter.dVal = (dY1 + dY2)/2;
		trShapeProp.width.dVal = (dX2 - dX1) * 0.6; //60 percent
		trShapeProp.height.dVal = (dY2 - dY1) * 0.6;
		trShapeProp.SetAttribute(STR_SHAPE_INIT, 1);
	}
	return true;
}

int	AddtoolRegionStats(const TreeNode& trGUI)
{
	GraphLayer gl = Project.ActiveLayer();
	if ( !gl )
		return -1;
	
	string strMainObjName;
	if ( get_ROI_main_obj_name(gl, XFNAME, strMainObjName) ) //attach to existing tool
	{
		RegionStatsTool stool(strMainObjName);
		Tree tr;
		stool.GetTree(tr);

		tr.GUI.Replace(trGUI.Clone(), true, true, true); //need to keep version attribute for checking.
		stool.UpdateThemeFileInGUITree(tr.GUI);
		stool.SetTree(tr);
		stool.ApplySettings();
		graphobjtool_events(stool, strMainObjName, OE_MOVE);
		stats_gadget_message(MSG_START_GADGET);
	}
	else //create new one
	{
		RegionStatsTool stool;
		int nErr = stool.Create(XFNAME, GCTC_FREE_FORM_RECT, trGUI, trGUI.roi.shape.nVal);
		if(nErr < 0)
		{
			return nErr;
		}
		stats_gadget_message(MSG_CREATE);
	}
	return 0;
}

//commands for context menu
enum {
	CMD_CHANGE_SHAPE = GOT_CMD_CUSTOM + 0x0001,
};

//report worksheet state
enum {
	REPORT_DISABLE = 0,
	REPORT_ENABLE = 1,
	REPORT_CREATED = 2,
};
class RegionStatsTool : public GraphObjCurveTool
{
public:
	RegionStatsTool(LPCSTR lpcszMainObjName = NULL) : GraphObjCurveTool(lpcszMainObjName)
	{
	}
	
	bool	GetResults(TreeNode& trResults)
	{
		if ( !trResults )
			return false;

		return GetResultsTree(trResults);
	}
	
	string	GetReportWorksheet()
	{
		string strName = "";
		Tree trGUI;
		if ( !GetGUITree(trGUI) )
			return strName;
		strName = trGUI.output.wksName.strVal;
		return strName;
	}
	///Sophy 6/25/2010 ORG-25-P5 UPDATE_REPORT_SHEET_NAME_FROM_GADGETTOOL_DLG
	//virtual
	BOOL	SetReportWorksheet(LPCSTR lpcszWorksheetName)
	{
		string strName(lpcszWorksheetName);
		if ( !check_update_worksheet_name(strName) )
		{
			return FALSE;
		}
		Tree trGUI;
		if ( !GetGUITree(trGUI) )
			return FALSE;
		trGUI.output.wksName.strVal = strName;
		return SetGUITree(trGUI);
	}
	///end UPDATE_REPORT_SHEET_NAME_FROM_GADGETTOOL_DLG
	int		GetReportState()
	{
		Tree trGUI;
		if ( !GetGUITree(trGUI) || trGUI.output.appendwks.nVal != 1 )
			return REPORT_DISABLE;
		
		string strWorksheet = trGUI.output.wksName.strVal;
		Worksheet wksReport(strWorksheet);
		return (wksReport.IsValid() ? REPORT_CREATED : REPORT_ENABLE);
	}
	bool	ApplySettings()
	{
		return UpdateROIGUI();
	}
	
	bool	DoPreferences(HWND hWndParent = NULL)
	{
		return DoEdit(hWndParent);
	}
	
	bool	DoEditDataPoints(HWND hWndParent = NULL)
	{
		DoEditData(hWndParent);
		return OnMove(); //update result.
	}
	
	bool	DoOutputResult()
	{
		return DoOutput(false);
	}
	
	bool	DoGo()
	{
		return DoActiveReport();
	}
protected:
	//virtual
	string	GetSignature(){ return "xf_addtool_cluster"; }
	//virtual
	string	GetXFName(){ return XFNAME; }
	//virtual
	string	GetPreferenceTitle(){ return TOOL_PREFERENCES_TITLE; }	
	//virtual
	TreeNode GetToolNameNode(const TreeNode& trGUI)
	{
		TreeNode trToolName;
		if ( trGUI )
			trToolName = trGUI.roi.toolname;
		ASSERT(trToolName);
		return trToolName;
	}
	//virtual
	TreeNode GetFillColorNode(const TreeNode& trGUI)
	{
		TreeNode trFillColor;
		if ( trGUI )
			trFillColor = trGUI.roi.rectcolor;
		ASSERT(trFillColor);
		return trFillColor;
	}
	
	//virtual
	BOOL	GetReportWorksheet(Worksheet& wksReport)
	{
		Tree trGUI;
		if ( !GetGUITree(trGUI) || trGUI.output.appendwks.nVal != 1 )
			return FALSE;
		string strWorksheet = trGUI.output.wksName.strVal;
		wksReport.Attach(strWorksheet);
		return wksReport.IsValid();
	}	
	
	//virtual
	BOOL	OnMove()
	{
		if ( !OnPreMove() )
			return FALSE;
		
		if ( calculate() )
		{
			updateResultText();
		}
		stats_gadget_message(MSG_UPDATE_DATA);
		///Sophy 6/24/2010 ORG-25-S1 UPDATE_ROI_POSITIN_SETTINGS_ON_MOVE
		if ( QUERY_ROI_TOOL_OPTION(ROI_OPTION_ON_MOVE_EVENT) )
		{
			Tree trGUI;
			if ( GetGUITree(trGUI) )
			{
				double dLeft, dRight, dTop, dBottom;
				GetRectCoordinate(dLeft, dRight, dTop, dBottom);
				TreeNode trShapeProp = trGUI.roi.shapeProp;
				trShapeProp.xCenter.dVal = (dRight + dLeft) / 2;
				trShapeProp.yCenter.dVal = (dTop + dBottom) / 2;
				trShapeProp.width.dVal = fabs(dRight - dLeft);
				trShapeProp.height.dVal = fabs(dTop - dBottom);
				SetGUITree(trGUI);
			}
		}
		///end UPDATE_ROI_POSITIN_SETTINGS_ON_MOVE
		
		GraphObjCurveTool::OnMove();
		return m_gp.Refresh();
	}

	//virtual
	bool	DoOutput(bool bUpdateLastOutput)
	{
		Tree trGUI;
		if ( !GetGUITree(trGUI) )
			return false;
		bool bOutToScriptWindow = trGUI.output.script.nVal == 1;
		bool bOutToResultLog = trGUI.output.reslog.nVal == 1;
		bool bOutToWks = trGUI.output.appendwks.nVal == 1;
		string strWksName = trGUI.output.wksName.strVal;
		DWORD dwCtrl = TREE2STR_CHECK_USER_LABEL;
		OutputResultTree(bOutToScriptWindow, bOutToResultLog, false, "", dwCtrl);
		if ( bOutToWks ) //region stats is a special report tree and need special handling
		{
			Tree trResults;
			if ( GetResultsTree(trResults) )
			{
				return reportResultTree(trResults, strWksName);
			}
		}
		return true;
	}
	
	//virtual
	bool	UpdateROIGUI()
	{
		if ( checkUpdateShape() )
		{
			GraphObjCurveTool::UpdateROIGUI();
		}
		stats_gadget_message(MSG_START_GADGET); //need to change main object name or settings

		return true;
	}

	//virtual
	bool	CheckGetMainObjectPositionFromGUITree(const TreeNode& trGUI, double& x0, double& x1, bool& bFixWidth, double& y0, double& y1)
	{
		TreeNode trShapeProp = trGUI.roi.ShapeProp;
		if ( trShapeProp )
		{
			double dWidth = trShapeProp.width.dVal;
			double dHeight = trShapeProp.height.dVal;
			x0 = trShapeProp.xCenter.dVal - dWidth/2;
			x1 = x0 + dWidth;
			y0 = trShapeProp.yCenter.dVal - dHeight/2;
			y1 = y0 + dHeight;
		}
		return true;
	}

	//virtual
	bool	IsSupportMultiPlots(){ return true; }
	//virtual
	bool	IsSupportEditData(){ return true; }
	//virutal
	bool	IsPlotForROI(const DataPlot& dp)
	{
		if ( !dp )
			return false;
		XYRange xySrc;
		Column colY;
		if ( dp.GetDataRange(xySrc) && xySrc.GetYColumn(colY) && is_dataobj_tagged(colY, STR_DATASETOBJ_FITCURVE) )
			return false;
		if( !okutil_is_good_plot_type_for_analysis(dp.GetPlotType()) )
			return false;
		return true;
	}

	//virtual
	BOOL	OnDestroy()
	{
		if ( GraphObjCurveTool::OnDestroy() )
		{
			stats_gadget_message(MSG_DESTROY);
		}
		return TRUE;
	}

	//virtual
	bool	UpdateContextMenu(Menu& pm, uint& nPosition)
	{
		Tree trGUI;
		GetGUITree(trGUI);
		int nShape = trGUI.roi.shape.nVal;
		DWORD dwFlags = MF_STRING | ((nShape == SHAPE_CIRCLE) ? MF_CHECKED : 0);
		pm.Add(_L("Use Circular ROI Box"), CMD_CHANGE_SHAPE, dwFlags), nPosition++;
		return true;
	}
	
	//virtual
	bool	OnCmdCustom(int nCmdID)
	{
		switch(nCmdID)
		{
		case CMD_CHANGE_SHAPE:
			OnSwitchShape();
			break;
			
		default:
			ASSERT(false);
			return false;
		}
		return true;
	}
	
	bool	OnSwitchShape()
	{
		Tree trGUI;
		GetGUITree(trGUI);
		int nShape = trGUI.roi.shape.nVal;
		trGUI.roi.shape.nVal = ((nShape == SHAPE_RECT) ? SHAPE_CIRCLE : SHAPE_RECT);
		SetGUITree(trGUI);
		return UpdateROIGUI();
	}
	
	///Sophy 4/8/2010 EXPAND_FULL_RANGE_SUPPORT_MULTIPLE_DATAPLOTS
	//virtual
	void	DoExpand(DataPlot& dp)
	{
		if ( !dp )
			GetSelectedPlot(dp);
		
		double dMinX, dMaxX, dMinY, dMaxY;
		BeforeExpandRect(dp, dMinX, dMaxX, dMinY, dMaxY);
		
		if ( GetMultiPlotsState() )
		{
			double dMinX2, dMaxX2, dMinY2, dMaxY2;
			foreach(DataPlot dpMore in m_gl.DataPlots)
			{
				if ( dp.GetIndex() != dpMore.GetIndex() && IsPlotForROI(dpMore) )
				{
					BeforeExpandRect(dpMore, dMinX2, dMaxX2, dMinY2, dMaxY2);
					if ( dMinX2 < dMinX )
						dMinX = dMinX2;
					if ( dMaxX2 > dMaxX )
						dMaxX = dMaxX2;
					if ( dMinY2 < dMinY )
						dMinY = dMinY2;
					if ( dMaxY2 > dMaxY )
						dMaxY = dMaxY2;
				}
			}
		}
		ExpandROI(dMinX, dMaxX, dMinY, dMaxY);
	}
	///end EXPAND_FULL_RANGE_SUPPORT_MULTIPLE_DATAPLOTS

private:
	
	//virtual
	void	getBoundingBoxInfo(string& str)
	{
		str.Empty(); //empty the string to force Data Display window NOT visible, the ROI range can be seen from Status bar
	}

	
	bool	reportResultTree(TreeNode& trStatsResult, LPCSTR lpcszWksName)
	{
		foreach(TreeNode trOneResultSet in trStatsResult.Children)
		{
			foreach(TreeNode trInnerOuter in trOneResultSet.Children)
			{
				string strRootLabel, strName, strDataName;
				if ( !trInnerOuter.GetAttribute(STR_LABEL_ATTRIB, strRootLabel) )
					strRootLabel = trInnerOuter.tagName;
				
				TreeNode trName = trInnerOuter.name;
				///Sophy 4/14/2010 BETTER_OUTPUT_ARRANGEMENT_IN_REPORT
				TreeNode trInOut = trInnerOuter.InsertNode(trName, "region");
				if ( trInOut )
				{
					trInOut.SetAttribute(STR_LABEL_ATTRIB, "Region");
					trInOut.strVal = strRootLabel;
					trInOut.TypeID = TNVAL_TYPE_CSTRING;
				}
				///end BETTER_OUTPUT_ARRANGEMENT_IN_REPORT
				if ( trName )
				{
					strDataName = trName.strVal;
					trName.SetAttribute(STR_LABEL_ATTRIB, _L("Data"));
					trName.strVal = strDataName;
					trName.TypeID = TNVAL_TYPE_CSTRING;
				}
				TreeNode trIndices = trInnerOuter.dpIndices;
				if ( trIndices )
				{
					trIndices.Remove();
					if ( !trIndices.GetAttribute(STR_LABEL_ATTRIB, strName) )
						strName = trIndices.tagName;
					strName.Format("%s %s of \"%s\"", strRootLabel, strName, strDataName);
					trIndices.SetAttribute(STR_LABEL_ATTRIB, strName);
				}
				TreeNode trXValues = trInnerOuter.dpXValues;
				TreeNode trYValues = trInnerOuter.dpYValues;
				if ( trXValues )
				{
					ASSERT(trYValues);
					trXValues.Remove();
					trYValues.Remove();
					if ( !trXValues.GetAttribute(STR_LABEL_ATTRIB, strName) )
						strName = trXValues.tagName;
					strName.Format("%s %s of \"%s\"", strRootLabel, strName, strDataName);
					trXValues.SetAttribute(STR_LABEL_ATTRIB, strName);
					
					if ( !trYValues.GetAttribute(STR_LABEL_ATTRIB, strName) )
						strName = trYValues.tagName;
					strName.Format("%s %s of \"%s\"", strRootLabel, strName, strDataName);
					trYValues.SetAttribute(STR_LABEL_ATTRIB, strName);
				}

				out_tree_to_wks(trInnerOuter, lpcszWksName, CREATE_HIDDEN | CREATE_LOAD_1ST_LAYER_ONLY);
				
				Worksheet wksReport;
				if ( (trIndices || trXValues && trYValues ) && wksReport.Attach(lpcszWksName) )
				{
#define		STR_WKS_NAME_DETAILS	"Details"
					WorksheetPage wp = wksReport.GetPage();
					Layer layer = wp.Layers(STR_WKS_NAME_DETAILS);
					Worksheet wksDetails(layer);
					int nIndex = 0;
					if ( !wksDetails )
					{
						nIndex = wp.AddLayer(STR_WKS_NAME_DETAILS);
						if ( nIndex < 0 )
							return false;
						wksDetails = wp.Layers(nIndex);
					}
					
					
					
#define		CHECK_GET_COL(_colReport, _Node) { \
	nIndex = wks_find_empty_column(wksDetails, 0);	\
	if ( nIndex < 0 )	\
		nIndex = wksDetails.AddCol();	\
	_colReport.Attach(wksDetails, nIndex);	\
	_colReport.SetName(_Node.tagName, OCD_ENUM_NEXT);	\
	string strLabel;	\
	_Node.GetAttribute(STR_LABEL_ATTRIB, strLabel);	\
	_colReport.SetLongName(strLabel);	\
	_colReport.SetFormat(OKCOLTYPE_NUMERIC);	\
	int nType = OKDATAOBJ_DESIGNATION_Y;	\
	_Node.GetAttribute(STR_COL_DESIGNATION_ATTRIB, nType);	\
	_colReport.SetType(nType);	\
}
					Column colReport;


					if ( trIndices )
					{
						CHECK_GET_COL(colReport, trIndices);
						vectorbase& vbI = colReport.GetDataObject();
						vbI = trIndices.nVals;
					}

					if ( trXValues )
					{
						CHECK_GET_COL(colReport, trXValues);
						vectorbase& vbX = colReport.GetDataObject();
						vbX = trXValues.dVals;
					}

					if ( trYValues )
					{
						CHECK_GET_COL(colReport, trYValues);
						vectorbase& vbY = colReport.GetDataObject();
						vbY = trYValues.dVals;
					}
				}
					
			}
		}
		return true;
	}
	
	bool	calculate()
	{
		Tree trGUI;
		if ( !GetGUITree(trGUI) )
			return false;

		vector vAllXInner, vAllYInner, vAllXOuter, vAllYOuter;
		vector vXInner, vYInner, vXOuter, vYOuter;
		vector<int> vnPointIndicesInner, vnPointIndicesOuter, vnAllPointsInner, vnAllPointsOuter;
		vector<int> vnPlots;
		if ( !GetMultiPlotsState() )
		{
			DataPlot dp;
			if ( GetSelectedPlot(dp) )
				vnPlots.Add(dp.GetIndex());
		}
		else
		{
			foreach(DataPlot dp in m_gl.DataPlots)
			{
				if ( IsPlotForROI(dp) )
					vnPlots.Add(dp.GetIndex());
			}
		}
		
		Tree trAllResults;
		for ( int iPlot = 0; iPlot < vnPlots.GetSize(); iPlot++ )
		{
			DataPlot dp = m_gl.DataPlots(vnPlots[iPlot]);
			//inner points
			dp.GetDataPoints(m_go, vXInner, vYInner, &vnPointIndicesInner, GDPS_AFTER_MASK);
			//outer points
			dp.GetDataPoints(0, -1, vXOuter, vYOuter);
			vnPointIndicesOuter.Data(0, vYOuter.GetSize() - 1, 1);
			if ( vnPointIndicesInner.GetSize() > 0 )
			{
				vXOuter.RemoveAt(vnPointIndicesInner);
				vYOuter.RemoveAt(vnPointIndicesInner);
				vnPointIndicesOuter.RemoveAt(vnPointIndicesInner);
			}
			
			vAllXInner.Append(vXInner);
			vAllYInner.Append(vYInner);
			vAllXOuter.Append(vXOuter);
			vAllYOuter.Append(vYOuter);
			vnAllPointsInner.Append(vnPointIndicesInner);
			vnAllPointsOuter.Append(vnPointIndicesOuter);
			
			string strDataName;
			dp.GetLegend(strDataName);
			//stats inner/outer points
			TreeNode trOneResult = trAllResults.AddNode("Result");
			trOneResult.SetAttribute(STR_LABEL_ATTRIB, "Result_" + strDataName);
			TreeNode trInnerResults = trOneResult.AddNode("Inner");
			TreeNode trOuterResults = trOneResult.AddNode("Outer");

			TreeNode trResultInner = getOneResult(vXInner, vYInner, vnPointIndicesInner, dp);
			trInnerResults.Replace(trResultInner, true, true, true);
			trInnerResults.SetAttribute(STR_LABEL_ATTRIB, STR_INNER_POINTS);


			TreeNode trResultOutter = getOneResult(vXOuter, vYOuter, vnPointIndicesOuter, dp);
			trOuterResults.Replace(trResultOutter, true, true, true);
			trOuterResults.SetAttribute(STR_LABEL_ATTRIB, STR_OUTER_POINTS);


		}
		if ( GetMultiPlotsState() ) //multiple plots combined stats
		{
			TreeNode trAllResult = trAllResults.AddNode("AllResult");
			trAllResult.SetAttribute(STR_LABEL_ATTRIB, _L("Result_") + _L("All Points"));
			
			TreeNode trTemp;
			
			TreeNode trAllInner = getOneResult(vAllXInner, vAllYInner, vnAllPointsInner);
			trTemp = trAllResult.AddNode("Inner");
			trTemp.Replace(trAllInner, true, true, true);
			trTemp.SetAttribute(STR_LABEL_ATTRIB, STR_INNER_POINTS);

			TreeNode trAllOuter = getOneResult(vAllXOuter, vAllYOuter, vnAllPointsOuter);
			trTemp = trAllResult.AddNode("Outer");
			trTemp.Replace(trAllOuter, true, true, true);
			trTemp.SetAttribute(STR_LABEL_ATTRIB, STR_OUTER_POINTS);
		}
		SetResultsTree(trAllResults);
		return true;	
	}
	
	TreeNode	getOneResult(vector vX, vector vY, const vector<int>&vnPointIndices, const DataPlot& dp = NULL)
	{
		Tree tr; //curve stats result tree
		Tree trGUI;
		Tree trResults;
		if ( !GetGUITree(trGUI) )
			return trResults;
		
		curve_stats(vX, vY, tr);
		
		trResults = trGUI.quantities.Clone();
		string str;
		if ( dp )
			dp.GetLegend(str);
		else
			str = _L("All Plots");
		trResults.name.strVal = str;
		trResults.name.TypeID = TNVAL_TYPE_CSTRING;
		
		if ( trResults.shape.nVal == 1 )
		{
			trResults.shape.strVal = trGUI.roi.shape.nVal == SHAPE_RECT ? _L("Rectangle") : _L("Circle");
			trResults.shape.TypeID = TNVAL_TYPE_CSTRING;
		}
		else
			trResults.shape.Remove();
#define	COPY(_VAL)				trResults._VAL = tr._VAL
#define	TYPE(_SubNode, _Type)	trResults._SubNode.SetAttribute(STR_COL_DESIGNATION_ATTRIB, _Type)
		COPY(N.nVal);		TYPE(N, OKDATAOBJ_DESIGNATION_Y);
		COPY(sum.dVal);		TYPE(sum, OKDATAOBJ_DESIGNATION_Y);
		COPY(mean.dVal);	TYPE(mean, OKDATAOBJ_DESIGNATION_Y);
		COPY(median.dVal);	TYPE(median, OKDATAOBJ_DESIGNATION_Y);
		COPY(sd.dVal);		TYPE(sd, OKDATAOBJ_DESIGNATION_ERROR);
		COPY(min.dVal);		TYPE(min, OKDATAOBJ_DESIGNATION_Y);
		COPY(max.dVal);		TYPE(max, OKDATAOBJ_DESIGNATION_Y);
		
		//move node to last
		TreeNode trTemp;
		int nChecked;
		
		trTemp = trResults.dpIndices;
		nChecked = trTemp.nVal;
		trTemp.Remove();
		if ( nChecked )
		{
			trTemp = trResults.AddNode("dpIndices");
			trTemp.nVals = vnPointIndices;
			trTemp.SetAttribute(STR_LABEL_ATTRIB, _L("Indices"));
			TYPE(dpIndices, OKDATAOBJ_DESIGNATION_X);
		}
		
		trTemp = trResults.dpValues;
		nChecked = trTemp.nVal;
		trTemp.Remove();
		if ( nChecked )
		{
			trTemp = trResults.AddNode("dpXValues");
			trTemp.dVals = vX;
			trTemp.SetAttribute(STR_LABEL_ATTRIB, _L("X Values"));
			TYPE(dpXValues, OKDATAOBJ_DESIGNATION_X);
			
			trTemp = trResults.AddNode("dpYValues");
			trTemp.dVals = vY;
			trTemp.SetAttribute(STR_LABEL_ATTRIB, _L("Y Values"));
			TYPE(dpYValues, OKDATAOBJ_DESIGNATION_Y);
		}
		tree_copy_values_to_attributes(trGUI.quantities, trResults, STR_LABEL_ATTRIB);
		return trResults.Clone();
	}

	bool	checkUpdateShape()
	{
		Tree	tr, trGUI;
		bool bUpdateROIGUI = true;
		if ( GetTree(tr) )
		{
			TreeNode trShape = tr.GUI.roi.shape;
			if ( trShape )
			{
				int nShape = trShape.nVal;
				int nTypeID = 0;
				m_go.GetObjectType(&nTypeID);
				if ( (SHAPE_RECT == nShape && GROT_ELLIPSE == nTypeID) || (SHAPE_CIRCLE == nShape && GROT_RECT == nTypeID) )
				{
					DataPlot dp;
					GetSelectedPlot(dp);
					bool bMultiPlots = GetMultiPlotsState();
		
					{
						ROIToolOptionAccessHelper helper(ROI_OPTION_IGNORE_EVENT);
						string strCmd;
						strCmd.Format("label -rc %s", m_go.GetName());
						LT_execute(strCmd);
					}
					init_shape_position(tr.GUI);
					Create(GetXFName(), GCTC_FREE_FORM_RECT, tr.GUI, nShape);
					m_gp.Refresh();
					if ( dp.IsValid() )
						SetPlot(dp.GetIndex());
					
					bUpdateROIGUI = false; //if newly create, not need to run base class UpdateROIGUI.
					SetMultiPlotsState(bMultiPlots);
				}
				else
				{
					//adjust main object position, in case position changed.
					TreeNode trShapeProp = tr.GUI.roi.shapeProp;
					if ( !trShapeProp )
						return false;
					
					double dxCenter = trShapeProp.xCenter.dVal;
					double dyCenter = trShapeProp.yCenter.dVal;
					double dWidth = trShapeProp.Width.dVal;
					double dHeight = trShapeProp.Height.dVal;
					
					double dLeft = dxCenter - dWidth/2;
					double dRight = dLeft + dWidth;
					double dBottom = dyCenter - dHeight/2;
					double dTop = dBottom + dHeight;
					update_roi_position(m_go, dLeft, dTop, dRight, dBottom);
					m_gp.Refresh();
				}
			}
		}
		return bUpdateROIGUI;
	}
	
	bool	updateResultText()
	{
		//Tree trGUI, trResults;
		//if ( !GetGUITree(trGUI) || !GetResultsTree(trResults) )
			//return false;
		//
		//string strText = "";
//#define	QUERY_OPTION(_Node)	(trGUI.showtop._Node.nVal == 1)
//#define	FORMAT_VALUE(_Node)	(ftoa(trResults._Node.dVal, "*5*"))
		//if ( QUERY_OPTION(xCenter) )
			//strText += "XC = " + FORMAT_VALUE(xCenter);
		//if ( QUERY_OPTION(yCenter) )
			//strText += " YC = " + FORMAT_VALUE(yCenter) + "\n";
		//
		//if ( QUERY_OPTION(n) )
			//strText += "N = " + trResults.n.nVal;
		//if ( QUERY_OPTION(mean) )
			//strText += " Mean = " + FORMAT_VALUE(mean);
		//if ( QUERY_OPTION(sd) )
			//strText += " SD = " + FORMAT_VALUE(sd);
		//if ( QUERY_OPTION(sum) )
			//strText += " Sum = " + FORMAT_VALUE(sum);
		//strText.TrimRight('\n');
		//UpdateTopLabel(strText);
		UpdateTopLabel(NULL, false);
		return true;
	}
};

void addtool_region_stats_events(string strGrName, int nEvent, int nMsg = 0)
{
	RegionStatsTool sTool;
	graphobjtool_events(sTool, strGrName, nEvent, nMsg);
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////StatsGadgetDlg////////////////////////////////////////////////////////
class StatsGadgetDlg : public GadgetDlg
{
public:
	StatsGadgetDlg(){}
	~StatsGadgetDlg(){}
	//virtual
	BOOL	StartGadget();
	BOOL	UpdateDlgData();
	BOOL	UpdateSettings();
	
protected:
	
	
EVENTS_BEGIN	
	ON_GRID_BEFORE_MOUSE_DOWN(IDC_VSFLEXGRID_1, OnBeforeMouseDownTable)
	ON_GRID_BEFORE_MOUSE_DOWN(IDC_VSFLEXGRID_2, OnBeforeMouseDownTable)

EVENTS_END

	BOOL	OnActivateReport(Control ctrl);
	BOOL	OnDestroy();
	BOOL	InitTables(const TreeNode& trResults);
	//virtual
	BOOL	OnPreferences(Control ctrl);
	BOOL	OnEditData(Control ctrl);
	BOOL	OnOutputResult(Control ctrl);
	string	GetToolType(){ return XFNAME; }
	BOOL	InitButtons();
	BOOL	ArrangeControls();

protected:
	//virtual
	BOOL	UpdateReportSheetName(LPCSTR lpcszSheetName);	///Sophy 6/25/2010 ORG-25-P5 UPDATE_REPORT_SHEET_NAME_FROM_GADGETTOOL_DLG

	
private:
	BOOL	updateTable(GridTableControl& table, const TreeNode& trStatsResult, int nRow);
	
private:
	RegionStatsTool	m_Tool;
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////Implementation////////////////////////////////////////////////////
BOOL	StatsGadgetDlg::StartGadget()
{
	if ( !CheckInitTool() )
		return FALSE;
	
	if (  !m_Tool.Init(m_strMainObjName) )
		return FALSE;
	
	int nReportState = m_Tool.GetReportState();
	//init control value
	Control ctrl = GetItem(IDC_EDIT_REPORT_SHEET);
	if ( ctrl )
	{
		ctrl.Text = m_Tool.GetReportWorksheet();
		ctrl.Enable = (nReportState != REPORT_DISABLE)
	}
	ctrl = GetItem(IDC_OUTPUT_RESULT);
	if ( ctrl )
		ctrl.Enable = (nReportState != REPORT_DISABLE);
	
	ctrl = GetItem(IDC_BTN_ACTIVATE_REPORT);
	if ( ctrl )
		ctrl.Enable = (nReportState == REPORT_CREATED);

	//get results in object storage.
	Tree trResults;
	if ( !m_Tool.GetResults(trResults) )
		return FALSE;
	
	return InitTables(trResults);
}

BOOL	StatsGadgetDlg::UpdateDlgData()
{
	///Sophy 7/19/2010 ORG-25-P7 FAIL_TO_UPDATE_WHEN_CHANGE_SETTINGS_IN_PLT_DETAILS_DLG
	//if ( !m_Tool.IsValidLayer() || !m_Tool.IsValidGrObj() ) //tool not init
	//	return FALSE;
	if ( !m_Tool.IsValidGrObj() ) //after close Plot Details dialog, all GraphObjects all reconstruct, need to reconnect.
	{
		if ( !CheckInitTool() || !m_Tool.Init(m_strMainObjName) )
			return FALSE;
	}
	///end FAIL_TO_UPDATE_WHEN_CHANGE_SETTINGS_IN_PLT_DETAILS_DLG
	
	Tree trResults;
	if ( !m_Tool.GetResults(trResults) )
		return FALSE;
	
	m_TableOne.ClearAll();
	m_TableTwo.ClearAll();
	int nRow = 0;
	foreach(TreeNode trOneResult in trResults.Children)
	{
		updateTable(m_TableOne, trOneResult.Inner, nRow);
		updateTable(m_TableTwo, trOneResult.Outer, nRow);
		nRow++;
	}
	m_TableOne.ResizeCols();
	m_TableTwo.ResizeCols();
	return TRUE;
}

BOOL	StatsGadgetDlg::updateTable(GridTableControl& table, const TreeNode& trStatsResult, int nRow)
{
	if ( !trStatsResult )
		return FALSE;
	
	vector<string>	vsRowValues;
	string strVal;
	int nTypeID;
	foreach(TreeNode trNode in trStatsResult.Children)
	{
		nTypeID = trNode.TypeID;
		string strVal;
		if ( TNVAL_TYPE_CSTRING == nTypeID )
			strVal = trNode.strVal;
		else if ( (TNVAL_TYPE_BIT_VECTOR | TNVAL_TYPE_INT) == nTypeID )
		{
			vector<int> vnVals;
			vector<string> vs;
			vnVals = trNode.nVals;
			convert_int_vector_to_string_vector(vnVals, vs);
			strVal = get_display_string(vs, 5);
		}
		else if ( (TNVAL_TYPE_BIT_VECTOR | TNVAL_TYPE_DOUBLE) == nTypeID )
		{
			vector<double> vdVals;
			vector<string> vs;
			vdVals = trNode.dVals;
			convert_double_vector_to_string_vector(vdVals, vs, vdVals.GetSize());
			strVal = get_display_string(vs, 5);
		}
		else if ( TNVAL_TYPE_INT == nTypeID )
		{
			strVal = trNode.nVal;
		}
		else	//if ( TNVAL_TYPE_DOUBLE == nTypeID )
		{
			strVal = ftoa(trNode.dVal, "*");
		}
		vsRowValues.Add(strVal);
	}
	return table.SetCells(vsRowValues, nRow, false);
}

BOOL	StatsGadgetDlg::OnActivateReport(Control ctrl)
{
	if ( !m_Tool.DoGo() )
	{
		MessageBox(GetSafeHwnd(), _L("Fail to activate result worksheet!"), _L("Warning"), MB_OK);
	}
	return TRUE;
}

BOOL	StatsGadgetDlg::InitTables(const TreeNode& trResults)
{
	if ( !trResults )
		return FALSE;
	TreeNode trInner = trResults.FirstNode.Inner;
	if ( !trInner )
		return FALSE;
	
	string	strLabel;
	vector<string>	vsColNames;
	vector<int>		vnColTypes;
	foreach(TreeNode trCol in trInner.Children)
	{
		if ( !trCol.GetAttribute(STR_LABEL_ATTRIB, strLabel) )
			strLabel = trCol.tagName;
		vsColNames.Add(strLabel);
		
		int nTypeID = trCol.TypeID;
		if ( TNVAL_TYPE_CSTRING & nTypeID || TNVAL_TYPE_BIT_VECTOR & nTypeID )
			vnColTypes.Add(flexDTString);
		else if ( TNVAL_TYPE_INT & nTypeID )
			vnColTypes.Add(TNVAL_TYPE_INT);
		else
			vnColTypes.Add(flexDTDouble)
	}
	strLabel.SetTokens(vsColNames, '|');
	m_TableOne.SetCols(vsColNames.GetSize());
	m_TableTwo.SetCols(vsColNames.GetSize());
	m_TableOne.SetColHeaderString(strLabel);
	m_TableTwo.SetColHeaderString(strLabel);

	for ( int iCol = 0; iCol < vsColNames.GetSize(); iCol++ )
	{
		m_TableOne.SetColDataType(iCol, vnColTypes[iCol]);
		m_TableTwo.SetColDataType(iCol, vnColTypes[iCol]);
	}
	m_TableOne.SetExplorerBar(flexExSort);
	m_TableTwo.SetExplorerBar(flexExSort);
	return UpdateDlgData();
}

BOOL	StatsGadgetDlg::OnPreferences(Control ctrl)
{
	m_Tool.DoPreferences(GetSafeHwnd());
	return TRUE;
}

BOOL	StatsGadgetDlg::OnEditData(Control ctrl)
{
	m_Tool.DoEditDataPoints();
	return TRUE;
}

BOOL	StatsGadgetDlg::OnOutputResult(Control ctrl)
{
	m_Tool.DoOutputResult();
	return TRUE;
}

BOOL	StatsGadgetDlg::InitButtons()
{
	vector<uint> vnBtnsToHide = {
		IDC_BTN_ACTIVATE_REPORT,
		IDC_BUTTON1,
		IDC_BUTTON2,
		IDC_BUTTON3, 
		IDC_BUTTON4, 
		IDC_BUTTON5, 
		IDC_BUTTON6, 
		IDC_BUTTON7, 
		IDC_BUTTON8
	}
	for ( int iBtn = 0; iBtn < vnBtnsToHide.GetSize(); iBtn++ )
	{
		Button btn = GetItem(vnBtnsToHide[iBtn]);
		if ( btn )
			btn.Visible = false;
	}
	
	vector<uint> vnBtns = {
		IDC_BTN_PREPERENCES,
		IDC_BTN_ACTIVATE_REPORT,
		IDC_OUTPUT_RESULT,
		IDC_BTN_EDIT
	};
	
	vector<string> vsBtnTips(vnBtns.GetSize());
	
	vsBtnTips[0] = _L("Preferences");
	vsBtnTips[1] = _L("Go");
	vsBtnTips[2] = _L("Output Report");
	vsBtnTips[3] = _L("Edit Data Points");
	
	vector<int> vnBitmaps = {
		IDB_PF_ADV,
		IDB_ACTIVATE_WKS,
		IDB_DUMP_DATA_INTO_WKS,
		IDB_EDIT
	};
	for ( iBtn = 0; iBtn < vnBtns.GetSize(); iBtn++ )
	{
		Button btnCtrl = GetItem(vnBtns[iBtn]);
		if ( btnCtrl )
			btnCtrl.Visible = true;
		BitmapRadioButton btn = GetItem(vnBtns[iBtn]);
		vector<string> vsTips(1);
		vsTips[0] = vsBtnTips[iBtn];
		if ( btn )
		{
			btn.Init(1, vnBitmaps[iBtn], 16, vsTips);
		}
	}
	return TRUE;
}

#define	DEFAULT_GAP	3
BOOL	StatsGadgetDlg::ArrangeControls()
{
	SetControlGap(DEFAULT_GAP);
	vector<uint> vnGroupBtnIDs = {
		IDC_REPORT_LABEL,
		IDC_EDIT_REPORT_SHEET,
		IDC_OUTPUT_RESULT,
		IDC_BTN_ACTIVATE_REPORT,
		IDC_BTN_EDIT,
		IDC_BTN_PREPERENCES
	};
	const int nControlGap = GetControlGap();
	int nBottom = ArrangeControlsLeftRight(vnGroupBtnIDs, nControlGap, nControlGap, nControlGap);
	Button btnPref;
	RECT rPrefBtn;
	GetControlClientRect(vnGroupBtnIDs[0], rPrefBtn, &btnPref);
	
	Control cFrame = GetItem(IDC_FRAME);
	MoveControl(cFrame, rPrefBtn);
	cFrame.Visible = false;
	
	Control tab;
	RECT rTab;
	GetControlClientRect(IDC_BOTTOM_TAB, rTab, &tab);
	rTab.top = nBottom + nControlGap;
	rTab.left = nControlGap;
	MoveControl(tab, rTab);
	return TRUE;
}

///Sophy 6/25/2010 ORG-25-P5 UPDATE_REPORT_SHEET_NAME_FROM_GADGETTOOL_DLG
//virtual
BOOL	StatsGadgetDlg::UpdateReportSheetName(LPCSTR lpcszSheetName)
{
	return m_Tool.SetReportWorksheet(lpcszSheetName); 
}
///end UPDATE_REPORT_SHEET_NAME_FROM_GADGETTOOL_DLG

int		stats_gadget_message(int nMsg, DWORD dwParam/* = 0*/)
{
	static	StatsGadgetDlg*	_pStatsDlg = NULL;
	static	int				_nReferCount = 0;
	switch(nMsg)
	{
	case MSG_CREATE:
		if ( NULL == _pStatsDlg )
		{
			ASSERT(0 == _nReferCount);
			_pStatsDlg = new StatsGadgetDlg;
			_pStatsDlg->Create(GetWindow());
		}
		_nReferCount++;
		stats_gadget_message(MSG_START_GADGET); //update data
		break;
		
	case MSG_DESTROY:
		_nReferCount--;
		if ( 0 == _nReferCount && NULL != _pStatsDlg )
		{
			_pStatsDlg->SendMessage(WM_CLOSE); //destroy resources
			delete _pStatsDlg;
			_pStatsDlg = NULL;
		}
		else if ( NULL != _pStatsDlg )
		{
			_pStatsDlg->Visible = false;
		}
			
		break;
		
	case MSG_START_GADGET:
		if ( NULL != _pStatsDlg )
		{
			_pStatsDlg->Visible = _pStatsDlg->StartGadget();
		}
		break;
		
	case MSG_UPDATE_DATA:
		if ( NULL != _pStatsDlg )
		{
			_pStatsDlg->UpdateDlgData();
		}
		break;
		
	default:
		ASSERT(FALSE);
		return -1;
	}
	return 0;
}
