#include <origin.h>
#include <GetNBox.h>
#include <xfutils.h>
enum{
	arrange_vertical,
	arrange_horizontal,
};
enum{
	type_line,
	type_scatter,
	type_linesymb,
	type_column,
};
enum{
	sort_none,
	sort_ascending,
	sort_descending,
};
//put additional include files here
#include <event_utils.h> /// Hong 07/06/07 v8/0655 ADD_ERR_CHECKING
//put your own support static functions here

/*------------------------------------------------------------------------------*
 * Modification Log:															*
 *	Folger 08/26/08 QA80-12074 PLOTGROUP_IMPROVEMENT							*
 *	Folger 10/06/08 QA80-12074 MORE_IMPROVEMENT_ON_PLOTGROUP					*
 *	Folger 10/15/08 IMPROVE_ERROR_CHECKING										*
 *	Kenny 08/25/2009 QA81-14203 RESET_PAGE_NAME_IF_CREATED_FROM_OC				*
 *	Jasmine 10/13/09 MAKE_SAME_COLOR_AS_COLUMN_TEMPLATE							*
 *	Jasmine 10/13/09 QA81-14321 SAVE_SPACING_SETTING_FOR_ARRANGEMENT			*
*------------------------------------------------------------------------------*/

/// change to MACRO to support all origin Internal data type
/*
static int _get_group_index(const vector<string>& vsGroup, vector<int>& vnIndex, int nC1 = 0, int nC2 = -1)
{
	int nSize = vsGroup.GetSize();
	
	if ( nSize < 1 || nSize < nC1 )
		return 0;
	
	if ( nC2 > nSize || -1 == nC2 )
		nC2 = nSize;
	
	string strVal = vsGroup[nC1];
	vnIndex.Add(nC1);
	
	for (int ii = nC1 + 1; ii < nC2; ii++)
	{
		if ( strVal != vsGroup[ii] )
		{
			strVal = vsGroup[ii];
			vnIndex.Add(ii);
		}
	}
	
	return vnIndex.GetSize();
}
*/

#define _GET_GROUP_DATA_INDEX(_vVals_, _vnIndex_, _nC1_, _nC2_) \
	int _nSize = _vVals_.GetSize(); \
	\
	if ( _nSize < 1 || _nSize < _nC1_ ) \
	{ \
		/*deal invalid range to one group with whole range*/ \
		_vnIndex_.Add(_nC1_); \
		_vnIndex_.Add(_nC2_); \
		return 1; \
	} \
	\
	if ( _nC2_ > _nSize || -1 == _nC2_ ) \
		_nC2_ = _nSize; \
	\
	_vnIndex_.Add(_nC1_); \
	\
	for (int ii = _nC1_ + 1; ii < _nC2_; ii++) \
	{ \
		if ( _vVals_[ii-1] != _vVals_[ii] ) \
			_vnIndex_.Add(ii); \
	} \
	\
	int _nIndexs = _vnIndex_.GetSize(); \
	/*add 1 overfloat item to indicate last range*/ \
	_vnIndex_.Add(_nC2_); \
	return _nIndexs; 
	
static int _get_column_group_data_index(const Column& col, vector<int>& vnIndex, int nC1 = 0, int nC2 = -1)
{
	if (!col) // deal invalid column to one group with whole range
	{
		vnIndex.Add(nC1);
		vnIndex.Add(nC2);
		return 1; 
	}
		
	int nDataType = col.GetInternalDataType();
	
	if ( FSI_TEXT == nDataType || FSI_MIXED == nDataType ) // need specail deal for non-numeric data cause by vectorbase
	{
		vector<string> vsVals;
		col.GetStringArray(vsVals);
		_GET_GROUP_DATA_INDEX(vsVals, vnIndex, nC1, nC2);
	}
	else
	{
		vectorbase& vb = col.GetDataObject();
		_GET_GROUP_DATA_INDEX(vb, vnIndex, nC1, nC2);
	}
	
	ASSERT(FALSE);
	return 0; //should not come here
}

/// Hong 11/05/07 QA80-8438 SHOW_INFO_LONGNAME_INDICATE_GROUP_PLOT_PAGE
static string _get_column_group_label(const Worksheet& wks, LPCSTR lpcszSName, int nCol, int nRow)
{
	string strLabel = "";
	if ( !wks || !lpcszSName || lpcszSName[0] == '\0' )
		return strLabel;
	
	string strCellVal;
	wks.GetCell(nRow, nCol, strCellVal);
	strLabel.Format("%s = %s", lpcszSName, strCellVal);
	
	return strLabel;	
}
/// end SHOW_INFO_LONGNAME_INDICATE_GROUP_PLOT_PAGE

//------ Folger 08/26/08 QA80-12074 PLOTGROUP_IMPROVEMENT
static	void	_update_legend(GraphLayer& gl, Column& col, const vector<int>& vnRowIndecies)
{
	legend_update(gl, ALM_COMMENT);
	
	if ( !col )
		return;
	
	string strDataName = col.GetLongName();
	if ( strDataName.IsEmpty() )
		col.GetName(strDataName);
	vector<string>		vsValues;
	col.GetStringArray(vsValues);
	
	vector<string>	vsLegends;
	GraphObject go = gl.GraphObjects(GO_LEGEND_NAME);
	ASSERT(go);
	go.Text.GetTokens(vsLegends, '\r');
	
	ASSERT(vsLegends.GetSize() == vnRowIndecies.GetSize() - 1);
	for ( int ii=0; ii<vsLegends.GetSize(); ++ii )
	{
		string	str;
		str.Format(" (%s=%s)", strDataName, vsValues[vnRowIndecies[ii]]);
		vsLegends[ii] += str;
	}
	
	go.Text.SetTokens(vsLegends, '\r');
}

static	bool	_is_input_range_valid(TreeNode& trInput, string *pstrErrMsg = NULL)
{
	XYRange iy;
	if ( !okxf_resolve_tree_construct_range(&trInput, &iy) )
	{
		if ( pstrErrMsg )
			*pstrErrMsg = CER_INVALID_XYRANGE;
		return false;
	}
	
	return true;
}
//------ End QA80-12074 PLOTGROUP_IMPROVEMENT

//------ Folger 10/06/08 QA80-12074 MORE_IMPROVEMENT_ON_PLOTGROUP
static		bool	_is_xyrange_even_sampling(const XYRange& xy)
{
	DataRange	dr;
	xy.GetSubRange(dr, DRR_NO_FACTORS | DRR_GET_DEPENDENT);

	Worksheet	wks;
	int			r1, c1, r2, c2;
	dr.GetRange(0, r1, c1, r2, c2, wks);		/// the first sub range must be Y column
	
	if ( wks )
	{
		Column col(wks, c1);
		if ( col && col.IsEvenSampling() )
			return true;
	}
	
	return false;
}

static		int		_convert_gui_plot_type(int nPlot)
{
	vector<int> vnPlots = {IDM_PLOT_LINE, IDM_PLOT_SCATTER, IDM_PLOT_LINESYMB, IDM_PLOT_COLUMN};
	if(0 <= nPlot && nPlot < vnPlots.GetSize())
		return vnPlots[nPlot];
	
	return IDM_PLOT_SCATTER;
}

static		void	_update_layer_y_axis_title(GraphLayer& gl, Column& col, int	nRowIndex)
{
	if ( !col )
		return;
	
	GraphObject		go = gl.GraphObjects("YL");
	if ( !go )
		return;
	
	string strName = col.GetLongName();
	if ( strName.IsEmpty() )
		col.GetName(strName);
	vector<string>		vsValues;
	col.GetStringArray(vsValues);
	ASSERT(nRowIndex < vsValues.GetSize());
	
	string			strText;
	strText.Format("%s\r\n(%s=%s)", go.Text, strName, vsValues[nRowIndex]);
	go.Text = strText;
}

//------ End MORE_IMPROVEMENT_ON_PLOTGROUP

///Jasmine 10/13/09 MAKE_SAME_COLOR_AS_COLUMN_TEMPLATE
static void _set_column_plot_color(DataPlot& dp)
{
	if(!dp)
	{
		ASSERT(0);
		return;
	}
	
	Tree trFmt;
	trFmt = dp.GetFormat(FPB_ALL, FOB_ALL, TRUE, TRUE);
	trFmt.Root.Pattern.Border.Width.dVal = SYSCOLOR_BLACK;
	trFmt.Root.Pattern.Fill.FillColor.nVal = SYSCOLOR_RED;
	int iRet = dp.UpdateThemeIDs(trFmt.Root, "Error", "Unknown tag");
	ASSERT(iRet == 0);
	BOOL bRet = dp.ApplyFormat(trFmt, TRUE, TRUE, TRUE);
	ASSERT(bRet);
}
///End MAKE_SAME_COLOR_AS_COLUMN_TEMPLATE


void plotgroup(const XYRange& iy, const Column& pgrp, const Column& lgrp, const Column& dgrp, const int& arrange, const string& template, const int& type, const int& sort)
{
	XYRange dr = iy;
	
	int nXC1, nXC2, nYC1, nYC2;
	Worksheet wksX, wksY;
	
	//------ Folger 10/06/08 QA80-12074 MORE_IMPROVEMENT_ON_PLOTGROUP
	bool	bIsEvenSampling = _is_xyrange_even_sampling(dr);
	int		nPlot = _convert_gui_plot_type(type);
	
	vector		vx, vy;
	double		rXMin = NANUM, rXMax = NANUM, rYMin = NANUM, rYMax = NANUM;
	double		rXInc = NANUM, rYInc = NANUM;
	if ( iy.GetData(vy, vx) )
	{
		vy.GetMinMax(rYMin, rYMax);
		vx.GetMinMax(rXMin, rXMax);
		
		RoundLimits(&rYMin, &rYMax, &rYInc);
		RoundLimits(&rXMin, &rXMax, &rXInc);
	}
	//------ End MORE_IMPROVEMENT_ON_PLOTGROUP
	
	dr.GetRange(wksX, nXC1, nXC2);	
	if ( !bIsEvenSampling )		//------ Folger 10/06/08 QA80-12074 MORE_IMPROVEMENT_ON_PLOTGROUP
		dr.GetRange(wksY, nYC1, nYC2, 1);
	
	//------ Folger 10/06/08 QA80-12074 MORE_IMPROVEMENT_ON_PLOTGROUP
	Worksheet wks;
	if ( sort != sort_none )
	{
		vector<int>		vnCols;
		
		if ( pgrp )
		{
			vnCols.Add(pgrp.GetIndex());
			if ( !wks )
				pgrp.GetParent(wks);
		}
		
		if ( lgrp )
		{
			vnCols.Add(lgrp.GetIndex());
			if ( !wks )
				lgrp.GetParent(wks);
		}
		
		if ( dgrp )
		{
			vnCols.Add(dgrp.GetIndex());
			if ( !wks )
				dgrp.GetParent(wks);
		}
	
		if ( vnCols.GetSize() > 0 )
		{
			vector<int>		vnSort(vnCols.GetSize());
			vnSort = sort == sort_ascending ? SORT_ASCENDING : SORT_DESCENDING;
			wks.Sort(vnCols, vnSort);
		}
	}
	//------ End MORE_IMPROVEMENT_ON_PLOTGROUP
	
	vector<int> vnPgGrpIndex;
	int nPageNum = _get_column_group_data_index(pgrp, vnPgGrpIndex);
	
	// page group
	/// Hong 11/05/07 QA80-8438 SHOW_INFO_LONGNAME_INDICATE_GROUP_PLOT_PAGE
	int nColIndex = -1;
	string strName;
	if ( pgrp )
	{
		pgrp.GetParent(wks);
		nColIndex = pgrp.GetIndex();
		//------ Folger 08/26/08 QA80-12074 PLOTGROUP_IMPROVEMENT
		//pgrp.GetName(strSName);
		strName = pgrp.GetLongName();
		if ( strName.IsEmpty() )
			pgrp.GetName(strName);
		//------ End QA80-12074 PLOTGROUP_IMPROVEMENT
	}
	/// end SHOW_INFO_LONGNAME_INDICATE_GROUP_PLOT_PAGE	
	for (int ii=0; ii < nPageNum; ii++)
	{
		GraphPage gp;
		/// Kenny 08/25/2009 QA81-14203 RESET_PAGE_NAME_IF_CREATED_FROM_OC
		//gp.Create(template);
		gp.Create(template, CREATE_DEFAULT_OPTIONS | CREATE_ENUM_EXIST_PAGE);
		/// End QA81-14203 RESET_PAGE_NAME_IF_CREATED_FROM_OC
		/// Hong 11/05/07 QA80-8438 SHOW_INFO_LONGNAME_INDICATE_GROUP_PLOT_PAGE
		string strGroupLabel;
		strGroupLabel = _get_column_group_label(wks, strName, nColIndex, vnPgGrpIndex[ii]);		
		gp.SetLongName(strGroupLabel);
		gp.TitleShow = WIN_TITLE_SHOW_BOTH;
		/// end SHOW_INFO_LONGNAME_INDICATE_GROUP_PLOT_PAGE
		
		vector<int> vnLyGrpIndex;
		//int nLayerNum = _get_group_index(vsLyGroup, vnLyGrpIndex, vnPgGrpIndex[ii], vnPgGrpIndex[ii+1]);
		int nLayerNum = _get_column_group_data_index(lgrp, vnLyGrpIndex, vnPgGrpIndex[ii], vnPgGrpIndex[ii+1]);
	
		// layer group
		int nLyCount = gp.Layers.Count();
		for (int jj=0; jj < nLayerNum; jj++)
		{
			if (nLyCount < jj + 1)
				//------ Folger 10/06/08 QA80-12074 MORE_IMPROVEMENT_ON_PLOTGROUP
				//gp.AddLayer();
				gp.AddLayer(NULL, 0, template);
				//------
			
			GraphLayer ly( gp.Layers(jj) );
			
			vector<int> vnPloyGrpIndex;
			//int nPlotNum = _get_group_index(vsPlotGroup, vnPloyGrpIndex, vnLyGrpIndex[jj], vnLyGrpIndex[jj+1]);
			int nPlotNum = _get_column_group_data_index(dgrp, vnPloyGrpIndex, vnLyGrpIndex[jj], vnLyGrpIndex[jj+1]);
			
			for (int mm=0; mm < nPlotNum; mm++)
			{
				dr.SetRange(0, wksX, vnPloyGrpIndex[mm], nXC1, vnPloyGrpIndex[mm+1] - 1, nXC2);
				if ( !bIsEvenSampling )		//------ Folger 10/06/08 QA80-12074 MORE_IMPROVEMENT_ON_PLOTGROUP
					dr.SetRange(1, wksY, vnPloyGrpIndex[mm], nYC1, vnPloyGrpIndex[mm+1] - 1, nYC2);
				//------ Folger 10/06/08 QA80-12074 MORE_IMPROVEMENT_ON_PLOTGROUP
				//ly.AddPlot(dr);
				ly.AddPlot(dr, nPlot);
				//------
			}
			
			///Jasmine 10/13/09 MAKE_SAME_COLOR_AS_COLUMN_TEMPLATE
			if(nPlot == IDM_PLOT_COLUMN && ly.DataPlots.Count() == 1)				
			{
				DataPlot dp = ly.DataPlots(0);
				_set_column_plot_color(dp);
			}
			///End MAKE_SAME_COLOR_AS_COLUMN_TEMPLATE
			
			//------ Folger 10/06/08 QA80-12074 MORE_IMPROVEMENT_ON_PLOTGROUP
			_update_layer_y_axis_title(ly, lgrp, vnLyGrpIndex[jj]);
			//------
			
			ly.GroupPlots(0);
			_update_legend(ly, dgrp, vnPloyGrpIndex);		//------ Folger 08/26/08 QA80-12074 PLOTGROUP_IMPROVEMENT
			ly.Rescale();
			//------ Folger 10/06/08 QA80-12074 MORE_IMPROVEMENT_ON_PLOTGROUP
			if ( !is_missing_value(rXInc) )
			{
				Scale scX(ly.X);
		        scX.From = rXMin;
		        scX.To = rXMax;
		        scX.Inc = rXInc;
		        
		        Scale scY(ly.Y);
		        scY.From = rYMin;
		        scY.To = rYMax;
		        scY.Inc = rYInc;
			}
			//------ End MORE_IMPROVEMENT_ON_PLOTGROUP
		}
		
		///Jasmine 10/13/09 QA81-14321 SAVE_SPACING_SETTING_FOR_ARRANGEMENT
		stLayersGridFormat stFormat;
		stFormat.nXGap 		= 5;		
		stFormat.nYGap 		= 5;			
		stFormat.nLeftMg 	= 15;			
		stFormat.nRightMg 	= 10;			
		stFormat.nTopMg 	= 10;			
		stFormat.nBottomMg 	= 15;
		
		int nCol, nRow;
		if (arrange_horizontal == arrange)
		{
			nCol = nLayerNum;
			nRow = 1;
		}
		else//arrange_vertical
		{
			nCol = 1;
			nRow = nLayerNum;
		}
		
		bool bRet = page_arrange_layers(gp, nRow, nCol, &stFormat);
		
		bool bOK = save_arrange_spacing_settings(	gp, nCol, nRow, 
											stFormat.nXGap, stFormat.nYGap, 
											stFormat.nLeftMg, stFormat.nRightMg, stFormat.nTopMg, stFormat.nBottomMg);
		
		ASSERT(bRet && bOK);
		///End SAVE_SPACING_SETTING_FOR_ARRANGEMENT
	}
	
}

//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 plotgroup_event1(TreeNode& trGetN, int nRow, int nEventID, DWORD& dwEnables, LPCSTR lpcszNodeName, WndContainer& DynaCntrlContainer, string& strAux, string& strErrMsg)
{
	if( xf_is_extended_event(nEventID) )
		return plotgroup_event1_ex(trGetN, nRow, nEventID, dwEnables, lpcszNodeName, DynaCntrlContainer, strAux, strErrMsg);
	DECLARE_BUTTON_ENABLES
	//------ Folger 10/15/08 IMPROVE_ERROR_CHECKING
	/*
	/// Hong 07/06/07 v8/0655 ADD_ERR_CHECKING
	if ( GETNE_ON_INIT == nEventID || GETNE_ON_THEME == nEventID )
	{
		//------ Folger 08/26/08 QA80-12074 PLOTGROUP_IMPROVEMENT
		//int nSize;
		//XYRange xyRange;
		//bOKEnable = get_error_for_input_xyrange(trGetN.iy, strErrMsg, nSize, xyRange);
		bOKEnable = _is_input_range_valid(trGetN.iy, &strErrMsg);
		//------ End QA80-12074 PLOTGROUP_IMPROVEMENT
		
		return true;
	}
	
	if( 0 == lstrcmp(lpcszNodeName, "iy") )
	{
		//------ Folger 08/26/08 QA80-12074 PLOTGROUP_IMPROVEMENT
		int nSize;
		//XYRange xyRange;
		//bOKEnable = get_error_for_input_xyrange(trGetN.iy, strErrMsg, nSize, xyRange);
		bOKEnable = _is_input_range_valid(trGetN.iy, &strErrMsg);
		//------ End QA80-12074 PLOTGROUP_IMPROVEMENT
	
		return true;
	}
	*/
	if ( !(bOKEnable = _is_input_range_valid(trGetN.iy, &strErrMsg)) )
		return true;
	//------ End IMPROVE_ERROR_CHECKING
	
	if( 0 == lstrcmp(lpcszNodeName, "pgrp") )
	{
		bOKEnable = !trGetN.pgrp.strVal.IsEmpty();
		if ( !bOKEnable )
			strErrMsg = XFERR_INVALID_COL_RANGE;
	
		return true;
	}
	
	if( 0 == lstrcmp(lpcszNodeName, "lgrp") )
	{
		bOKEnable = !trGetN.lgrp.strVal.IsEmpty();
		if ( !bOKEnable )
			strErrMsg = XFERR_INVALID_COL_RANGE;
		
		return true;
	}
	
	if( 0 == lstrcmp(lpcszNodeName, "dgrp") )
	{
		bOKEnable = !trGetN.dgrp.strVal.IsEmpty();
		if ( !bOKEnable )
			strErrMsg = XFERR_INVALID_COL_RANGE;
		
		return true;
	}
	/// end ADD_ERR_CHECKING
	return false;

}

int plotgroup_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 plotgroup_before_execute(TreeNode& trGetN, int nGetNDialog, int& nRet, int dwCntrl)
{

}
//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 plotgroup_event1_ex(TreeNode& trGetN, int nRow, int nEventID, DWORD& dwEnables, LPCSTR lpcszNodeName, WndContainer& DynaCntrlContainer, string& strAux, string& strErrMsg)
{
	return false;
}
