#include <origin.h>
#include <GetNBox.h>
#include <xfutils.h>
enum{
	format_yacross,
	format_xacross,
};
enum{
	rowpos_none,
	rowpos_selrow1,
	rowpos_wksrow1,
	rowpos_label,
	rowpos_custom,
};
enum{
	colpos_none,
	colpos_selcol1,
	colpos_wkscol1,
	colpos_xcol,
	colpos_custom,
};
//put additional include files here
#include	<..\Originlab\WksColLabels.h>
#include	<..\Originlab\theme_utils.h>
//put your own support static functions here
#define	STR_INPUT_TITLE_XYZ	_L("Input Data")
#define	STR_INPUT_TITLE_Z	_L("Z Data")
#define	STR_X_VALUES_IN		_L("X Values in")
#define	STR_Y_VALUES_IN		_L("Y Values in")
#define	STR_X_VALUES		_L("X Range")
#define	STR_Y_VALUES		_L("Y Range")

#define	RCRange	ORANGE
///Sophy 8/31/2010 ORG-923-P2 LABEL_INDEX_NOT_SET_WHEN_RUN_FROM_SCRIPT_MODE
//static	int	sg_nColLabel;
//static	bool	_init_column_label(TreeNode& trGetN, const Worksheet& wks, bool bInit, int* pnColLabel)
static	bool	_init_column_label(TreeNode& trGetN, const Worksheet& wks, bool bInit)
///end LABEL_INDEX_NOT_SET_WHEN_RUN_FROM_SCRIPT_MODE
{
	ASSERT( wks );
	if( !wks )
	{
		trGetN.label.Enable = false;
		return false;
	}

	vector<string> vstrLabelName;
	vector<int> vnLabelNum;	
	get_wks_shown_col_label_info_skip_specials( wks, vstrLabelName, vnLabelNum );
		
	string strTemp;
	strTemp.SetTokens( vstrLabelName, '|' );
	strTemp += "|";
	
	trGetN.label.SetAttribute( STR_COMBO_ATTRIB, strTemp );
		
	if( bInit )
	{
		int nSize = vstrLabelName.GetSize();
		if( nSize >= 0 )
		{
			trGetN.label.strVal = vstrLabelName[nSize - 1];
		}
	}
	///Sophy 8/31/2010 ORG-923-P2 LABEL_INDEX_NOT_SET_WHEN_RUN_FROM_SCRIPT_MODE
	//get_wks_shown_col_label_info( wks, vstrLabelName, vnLabelNum );
					//
	//int nPos  = vstrLabelName.Find(trGetN.label.strVal);
	//
	//if( -1 != nPos )
	//{
		//trGetN.label.Enable = true;
		//*pnColLabel =  nPos - vstrLabelName.GetSize();
	//}
	//else
	//{
		//ASSERT( FALSE );
		//trGetN.label.Enable = false;
		//return false;
	//}
	///end LABEL_INDEX_NOT_SET_WHEN_RUN_FROM_SCRIPT_MODE
	
	return true;
}

enum {
	SEL_INVALID		= 0,
	SEL_ONE_ROW		= 0x0001,
	SEL_ONE_COL		= 0x0002,	
	SEL_WHOLE_ROW	= 0x0004,
	SEL_WHOLE_COL	= 0x0008,
	SEL_SINGLE_ROW	= 0x0010,
	SEL_SINGLE_COL	= 0x0020,
};
#define	IS_WHOLE_SHEET(_SelMode)	((_SelMode & SEL_WHOLE_ROW) && (_SelMode & SEL_WHOLE_COL))
static	int	_get_range_mode(Range& irng)
{
	int nSelMode = SEL_INVALID;
	if ( irng.IsEmpty() )
		return SEL_INVALID;
	
	Datasheet ds;
	int r1, c1, r2, c2;
	if ( !irng.GetRange(0, r1, c1, r2, c2, ds) || !ds.IsValid() )
		return SEL_INVALID;

	if ( r1 == 0 && (r2 == -1 || r2 == ds.GetNumRows() - 1) )
		nSelMode |= SEL_WHOLE_COL;
	if ( c1 == 0 && (c2 == -1 || c2 == ds.GetNumCols() - 1) )
		nSelMode |= SEL_WHOLE_ROW;
	
	if ( r1 == r2 )
		nSelMode |= SEL_SINGLE_ROW;
	if ( c1 == c2 )
		nSelMode |= SEL_SINGLE_COL;

	return nSelMode;
}
/// return 1 if success, return -1 if has empty or text data
static	int	_is_subrng_valid_xy_array(const Datasheet &ds, int r1, int c1, int r2, int c2, bool bColumn)
{
	if ( bColumn && c2 - c1 != 0 )
		return 0;
	else if ( !bColumn && r2 - r1 != 0 )
		return 0;
	
	///Sophy 8/31/2010 ORG-923-P2 LABEL_INDEX_NOT_SET_WHEN_RUN_FROM_SCRIPT_MODE
	Grid grid; //need to attach to grid in case ds not in active folder(no window in VC level)
	if ( !ds )
		return 0;
	else
		grid.Attach(ds);
	///end LABEL_INDEX_NOT_SET_WHEN_RUN_FROM_SCRIPT_MODE
	
	int nIndex = bColumn ? c1 : r1;
	int i1 = bColumn ? r1 : c1;
	int i2 = bColumn ? r2 : c2;
	if ( i2 - i1 <= 0 )
		return 0;
	
	vector	vx;
	vx.SetSize(i2-i1+1);
	for ( int ii=i1; ii<=i2; ++ii )
	{
		if( NANUM == (vx[ii-i1] = bColumn ? ds.Cell(ii, nIndex) : ds.Cell(nIndex, ii)) )
			return -1;
	}
	
	int mo = ocmath_is_monotonic(vx, vx.GetSize());
	
	return (MONO_DECREASE == mo || MONO_INCREASE == mo) ? 1 : 0;
}


// if r2 or c2 == -1, it modify c2 and r2 to the correct column and row
static void _get_correct_row_and_col(int &r1, int &c1, int &r2, int &c2, const Datasheet &ds)
{
	if(-1 == c2)
	c2 = ds.GetNumCols()-1;
	if(-1 == r2)
	r2 = ds.GetNumRows()-1;
				
	while(c1 < c2 && NANUM == ds.Cell(r1, c2))
		--c2;
	while(r1 < r2 && NANUM == ds.Cell(r2, c1))
		--r2;
}

static	bool	_init_dlg(TreeNode &trGetN, const Worksheet &wks)
{
	trGetN.type.Show 		= false;
	trGetN.ogl.Show 		= false;
	trGetN.AutoUpdate.Show 	= false; 
	
	ASSERT(wks);
	if( !wks )
	{
		trGetN.label.Enable = false;
		return false;
	}
	///Sophy 8/31/2010 ORG-923-P2 LABEL_INDEX_NOT_SET_WHEN_RUN_FROM_SCRIPT_MODE
	//return _init_column_label(trGetN, wks, true, &sg_nColLabel);
	return _init_column_label(trGetN, wks, true);
	///end LABEL_INDEX_NOT_SET_WHEN_RUN_FROM_SCRIPT_MODE
}

static	bool	_init_xyz_titles(TreeNode& trGetN)
{
	XYRange irng, xrange, yrange;
	xy_range_from_GetN_data_node(trGetN.irng, irng);
	xy_range_from_GetN_data_node(trGetN.xrange, xrange);
	xy_range_from_GetN_data_node(trGetN.yrange, yrange);
	if ( !irng || irng.IsEmpty() )
	{
		trGetN.xtitle.strVal 	= "X Title";
		trGetN.ytitle.strVal 	= "Y Title";
		trGetN.ztitle.strVal 	= "Z Title";
		return false;
	}
	Datasheet ds;
	int r1, r2, c1, c2;
	irng.GetRange(0, r1, c1, r2, c2, ds);
	if ( !ds )
		return false;
	
	string strTitle;
	DataRange drVM;
	if ( _construct_virtual_range(trGetN, drVM) )
	{
		MatrixObject mo;
		if ( mo.Attach(drVM, format_yacross == trGetN.format.nVal ? MATOBJATTACH_TRANSPOSE : 0) )
		{
			strTitle = mo.GetLongName();
			if ( strTitle.IsEmpty() )
			{
				int nSelMode = _get_range_mode(irng);
				if ( IS_WHOLE_SHEET(nSelMode) )
					strTitle = ds.GetName();
				else
					strTitle = "vm1";
			}
			mo.CheckUpdateVSName(strTitle);
			trGetN.ztitle.strVal = strTitle;
			string strX, strY;
			mo.GetAxesLongName(strX, strY);
			trGetN.xtitle.strVal = strX.IsEmpty() ? "X Title" : strX;
			trGetN.ytitle.strVal = strY.IsEmpty() ? "Y Title" : strY;
		}
	}
	else
	{
		strTitle = trGetN.ztitle.strVal;
		if ( strTitle.IsEmpty() )
		{
			int nSelMode = _get_range_mode(irng);
			if ( IS_WHOLE_SHEET(nSelMode) )
				strTitle = ds.GetName();
			else
				strTitle = "vm1";
		}
		trGetN.ztitle.strVal = strTitle;
	}
	return true;
}

static	bool	_get_longname_units_format_string(Column& col, string& strLNUnits)
{
	if ( !col )
		return false;
	string strName = col.GetLongName();
	if ( strName.IsEmpty() )
		strName = col.GetName();
	string strUnits = col.GetUnits();
	if ( strUnits.IsEmpty() )
		strLNUnits = strName;
	else
		strLNUnits.Format("%s (%s)", strName, strUnits);
	return true;
}
static	int	_update_xyz_titles(TreeNode& trGetN)
{
	XYRange irng;
	xy_range_from_GetN_data_node(trGetN.irng, irng);
	Worksheet wks;
	int r1, r2, c1, c2;
	irng.GetRange(0, r1, c1, r2, c2, wks);
	if ( !wks )
		return XFERR_RANGE_LESS_THAN_2X2;
	
	TreeNode trXtitle = trGetN.xtitle;
	TreeNode trYtitle = trGetN.ytitle;
	TreeNode trZtitle = trGetN.ztitle;
	
	//update X/Y titles
	int nFormat = trGetN.format.nVal;
	int nRowMode = trGetN.rowpos.nVal;
	int nColMode = trGetN.colpos.nVal;
	bool bXAuto = octree_get_auto_support(&trXtitle) == 1;
	bool bYAuto = octree_get_auto_support(&trYtitle) == 1;
	if ( rowpos_label == nRowMode )
	{
		///Sophy 8/31/2010 ORG-923-P2 LABEL_INDEX_NOT_SET_WHEN_RUN_FROM_SCRIPT_MODE
		//int nType = get_label_type_by_name(wks, trGetN.label.strVal);
		//string strLabel = get_label_name_by_type(nType, wks);
		string strLabel = trGetN.label.strVal;
		///end LABEL_INDEX_NOT_SET_WHEN_RUN_FROM_SCRIPT_MODE
		
		string strName;
		Column col(wks, c1);
		_get_longname_units_format_string(col, strName);
		if ( format_xacross == nFormat )
		{
			if ( bXAuto )
				trXtitle.strVal = strLabel;
			if ( bYAuto )
				trYtitle.strVal = strName;
		}
		else
		{
			if ( bXAuto )
				trXtitle.strVal = strName;
			if ( bYAuto )
				trYtitle.strVal = strLabel;
		}
	}
	else
	{
		if ( bXAuto )
			trXtitle.strVal = "X Title";
		if ( bYAuto )
			trYtitle.strVal = "Y Title";
	}
	
	if ( colpos_custom == nColMode )
	{
		XYRange colrng;
		xy_range_from_GetN_data_node(trGetN.colrng, colrng);
		
		Worksheet wks;
		int r1, r2, c1, c2;
		colrng.GetRange(0, r1, c1, r2, c2, wks);
		if ( !wks )
			return false;
		
		string strName;
		Column col(wks, c1);
		_get_longname_units_format_string(col, strName);
		if ( format_xacross == nFormat )
		{
			if ( bYAuto )
				trYtitle.strVal = strName;
		}
		else if ( format_yacross == nFormat )
		{
			if ( bXAuto )
				trXtitle.strVal = strName;
		}
	}
	///Sophy 8/19/2010 ORG-815-P5 AUTO_INIT_XY_TITLE_WHEN_SEL_X_TO_LEFT_SELECTION_MODE
	else if ( colpos_xcol == nColMode || colpos_wkscol1 == nColMode || colpos_selcol1 == nColMode)
	{
		int nXIndex = (colpos_xcol == nColMode) ? wks.FindColIndex(c1, OKDATAOBJ_DESIGNATION_X, FCI_NO_CHECK_EVEN_SPACED) : ((colpos_wkscol1 == nColMode) ? 0 : c1);
		if ( nXIndex >= 0 )
		{
			Column colX(wks, nXIndex);
			string strName;
			_get_longname_units_format_string(colX, strName);
			if ( format_xacross == nFormat )
			{
				if ( bYAuto )
					trYtitle.strVal = strName;
			}
			else if ( format_yacross == nFormat )
			{
				if ( bXAuto )
					trXtitle.strVal = strName;
			}
		}

	}
	///end AUTO_INIT_XY_TITLE_WHEN_SEL_X_TO_LEFT_SELECTION_MODE
	
	//update Z title
	DataRange drVM;
	if ( _construct_virtual_range(trGetN, drVM) )
	{
		string strZtitle = trGetN.ztitle.strVal;
		if ( strZtitle.IsEmpty() )
			return XFERR_Z_TITLE_IS_EMPTY;
		else
		{
			MatrixObject mo;
			if ( mo.Attach(drVM, format_yacross == nFormat ? MATOBJATTACH_TRANSPOSE : 0) )
			{
				string strUpdated = strZtitle;
				BOOL bValidName = mo.CheckUpdateVSName(strUpdated);
				if ( !bValidName )
					return XFERR_INVALID_SERIES_NAME;
			}
		}
	}
	return CER_NO_ERROR;
}

static	void	_update_input_range(int& r1, int& c1, int& r2, int& c2, const Datasheet& ds)
{
	///Sophy 8/24/2010 ORG-815-P10 PROPER_UPDATE_SELECTION_BOUNDS_ON_WHOLE_ROW_COL
	//if ( (r1 >= 0  && r2 == -1 || c2 == -1) && ds.IsValid() )
	//{
		//Worksheet wks(ds);
		//if ( c2 == -1 )
			//c2 = wks.GetNumCols() - 1;
		//wks.GetBounds(r1, c1, r2, c2, TRUE);
		//if ( r2 == -1 )
			//r2 = wks.GetNumRows() - 1;
	//}
	if ( ds )
	{
		if ( c2 == -1 )
			c2 = ds.GetNumCols() - 1;
		
		if ( r1 >= 0 && r2 == -1 ) //if it is label area selection, we should not update it 
		{
			Worksheet wks(ds);
			wks.GetBounds(r1, c1, r2, c2, TRUE);
		}
	}
	///end PROPER_UPDATE_SELECTION_BOUNDS_ON_WHOLE_ROW_COL
}

class	VirtualMatrixConstructor
{
public:
	VirtualMatrixConstructor(){ m_nFormat = -1; m_nRowMode = -1; m_nColMode = -1; }
	~VirtualMatrixConstructor(){}
	BOOL	MakeVirtualMatrixRange(const TreeNode& trGetN, DataRange& drVM);
	BOOL	MakeVirtualMatrixRange(const TreeNode& trGetN, Range& irng, Range& rowrng, Range& colrng, DataRange& drVM);	///Sophy 8/27/2010 ORG-923-P1 LT_EXECUTE_FAIL_TO_RESOLVE_INPUT_RANGE_DUE_TO_ACTIVE_RESULT_GRAPH
	int		UpdateGUI(TreeNode& trGetN, bool bAllowAuto);
	int		CheckInput(TreeNode& trGetN);
protected:
	enum {
		VM_NORMAL_RANGE		= -3,
		VM_NO_ROW_RANGE		= -2,
		VM_NO_COL_RANGE		= -1,// use minus value to distinguish from XF error code
	};
	int		Init(const TreeNode& trGetN);
	///Sophy 8/27/2010 ORG-923-P1 LT_EXECUTE_FAIL_TO_RESOLVE_INPUT_RANGE_DUE_TO_ACTIVE_RESULT_GRAPH
	int		Init(const TreeNode& trGetN, Range& irng, Range& rowrng, Range& colrng);	
	int		InitSettings(const TreeNode& trGetN);
	BOOL	AddRanges(DataRange& drVM);
	///end LT_EXECUTE_FAIL_TO_RESOLVE_INPUT_RANGE_DUE_TO_ACTIVE_RESULT_GRAPH
	int		InitRowRange();
	int		InitColRange();
	BOOL	AddX(DataRange& drVM);
	BOOL	AddY(DataRange& drVM);
	BOOL	AddZ(DataRange& drVM);
	BOOL	AddRow(DataRange& drVM, LPCSTR lpcszName);
	BOOL	AddCol(DataRange& drVM, LPCSTR lpcszName);
private:
	int	m_nFormat;
	int	m_nRowMode;
	int	m_nColMode;
	int m_nR1, m_nC1, m_nR2, m_nC2;
	int	m_nRr1, m_nRc1, m_nRr2, m_nRc2;	//row range
	int	m_nCr1, m_nCc1, m_nCr2, m_nCc2;	//column range
	int	m_nZr1, m_nZc1, m_nZr2, m_nZc2; //z range
	int	m_nColLabelIndex;	///Sophy 8/31/2010 ORG-923-P2 LABEL_INDEX_NOT_SET_WHEN_RUN_FROM_SCRIPT_MODE
	Range	m_irng, m_rowrng, m_colrng;
	Worksheet	m_wks;
};

int		VirtualMatrixConstructor::Init(const TreeNode& trGetN)
{
	TreeNode trIrng = trGetN.irng;
	TreeNode trRowrng = trGetN.rowrng;
	TreeNode trColrng = trGetN.colrng;
	okxf_resolve_tree_construct_range(&trIrng, &m_irng);
	okxf_resolve_tree_construct_range(&trRowrng, &m_rowrng);
	okxf_resolve_tree_construct_range(&trColrng, &m_colrng);
	///Sophy 8/27/2010 ORG-923-P1 LT_EXECUTE_FAIL_TO_RESOLVE_INPUT_RANGE_DUE_TO_ACTIVE_RESULT_GRAPH
	//if ( !m_irng || m_irng.IsEmpty() )
		//return XFERR_RANGE_LESS_THAN_2X2;
	//
	//m_irng.GetRange(0, m_nR1, m_nC1, m_nR2, m_nC2, m_wks);
	//if ( !m_wks )
		//return XFERR_RANGE_LESS_THAN_2X2;
	//_update_input_range(m_nR1, m_nC1, m_nR2, m_nC2, m_wks);
	//m_nZr1 = m_nR1, m_nZc1 = m_nC1, m_nZr2 = m_nR2, m_nZc2 = m_nC2; //these values might be updated later.
	//m_nFormat = trGetN.format.nVal;
	//m_nRowMode = trGetN.rowpos.nVal;
	//m_nColMode = trGetN.colpos.nVal;
	//if ( m_nFormat != format_xacross && m_nFormat != format_yacross )
		//return XFERR_VM_INVALID_DATAFORMAT; //invalid format.
	//
	//return CER_NO_ERROR;
	return InitSettings(trGetN);
	///end LT_EXECUTE_FAIL_TO_RESOLVE_INPUT_RANGE_DUE_TO_ACTIVE_RESULT_GRAPH
}

///Sophy 8/27/2010 ORG-923-P1 LT_EXECUTE_FAIL_TO_RESOLVE_INPUT_RANGE_DUE_TO_ACTIVE_RESULT_GRAPH
int		VirtualMatrixConstructor::Init(const TreeNode& trGetN, Range& irng, Range& rowrng, Range& colrng)
{
	m_irng = irng;
	m_rowrng = rowrng;
	m_colrng = colrng;
	return InitSettings(trGetN);
}

int		VirtualMatrixConstructor::InitSettings(const TreeNode& trGetN)
{
	if ( !m_irng || m_irng.IsEmpty() )
		return XFERR_RANGE_LESS_THAN_2X2;
	
	m_irng.GetRange(0, m_nR1, m_nC1, m_nR2, m_nC2, m_wks);
	if ( !m_wks )
		return XFERR_RANGE_LESS_THAN_2X2;
	_update_input_range(m_nR1, m_nC1, m_nR2, m_nC2, m_wks);
	m_nZr1 = m_nR1, m_nZc1 = m_nC1, m_nZr2 = m_nR2, m_nZc2 = m_nC2; //these values might be updated later.
	m_nFormat = trGetN.format.nVal;
	m_nRowMode = trGetN.rowpos.nVal;
	m_nColMode = trGetN.colpos.nVal;
	if ( m_nFormat != format_xacross && m_nFormat != format_yacross )
		return XFERR_VM_INVALID_DATAFORMAT; //invalid format.
	///Sophy 8/31/2010 ORG-923-P2 LABEL_INDEX_NOT_SET_WHEN_RUN_FROM_SCRIPT_MODE
	m_nColLabelIndex = 0;
	if ( m_nRowMode == rowpos_label )
	{
		vector<string> vstrLabelName;
		vector<int> vnLabelNum;	
		get_wks_shown_col_label_info(m_wks, vstrLabelName, vnLabelNum);
		int nPos  = vstrLabelName.Find(trGetN.label.strVal);
		if ( nPos >= 0 )		//this should be always successful
			m_nColLabelIndex = nPos - vstrLabelName.GetSize();
		else
		{
			ASSERT(FALSE);
			return XFERR_VM_INVALID_DATAFORMAT;
		}
	}
	///end LABEL_INDEX_NOT_SET_WHEN_RUN_FROM_SCRIPT_MODE
	if ( m_nC2 - m_nC1 < 1 || m_nR2 - m_nR1 < 1 )
		return XFERR_RANGE_LESS_THAN_2X2;
	return CER_NO_ERROR;
}
///end LT_EXECUTE_FAIL_TO_RESOLVE_INPUT_RANGE_DUE_TO_ACTIVE_RESULT_GRAPH

int		VirtualMatrixConstructor::InitRowRange()
{
	int nRowRange = VM_NORMAL_RANGE;
	int nColStartOffset = (colpos_selcol1 == m_nColMode || colpos_wkscol1 == m_nColMode && m_nC1 == 0) ? 1 : 0;
	//update Z range
	m_nZc1 = m_nC1 + nColStartOffset; ///Sophy 8/24/2010 ORG-815-P11 CENTRALIZE_CODE_UPDATE_Z_RANGE_INDICES
	switch(m_nRowMode)
	{
	case rowpos_none:
		nRowRange = VM_NO_ROW_RANGE;
		break;
	case rowpos_selrow1:
		m_nRr1 = m_nR1;
		m_nRc1 = m_nC1 + nColStartOffset;
		m_nRr2 = m_nR1;
		m_nRc2 = m_nC2;
		break;
	case rowpos_wksrow1:
		m_nRr1 = 0;
		m_nRc1 = m_nC1 + nColStartOffset;
		m_nRr2 = 0;
		m_nRc2 = m_nC2;
		break;
	case rowpos_label:
		///Sophy 8/31/2010 ORG-923-P2 LABEL_INDEX_NOT_SET_WHEN_RUN_FROM_SCRIPT_MODE
		//m_nRr1 = sg_nColLabel;
		m_nRr1 = m_nColLabelIndex;
		///end LABEL_INDEX_NOT_SET_WHEN_RUN_FROM_SCRIPT_MODE
		m_nRc1 = m_nC1 + nColStartOffset;
		///Sophy 8/31/2010 ORG-923-P2 LABEL_INDEX_NOT_SET_WHEN_RUN_FROM_SCRIPT_MODE
		//m_nRr2 = sg_nColLabel;
		m_nRr2 = m_nColLabelIndex;
		///end LABEL_INDEX_NOT_SET_WHEN_RUN_FROM_SCRIPT_MODE
		m_nRc2 = m_nC2;
		break;
	case rowpos_custom:
		{
			int rr1, rc1, rr2, rc2;
			Datasheet dsRow;
			if ( !m_rowrng || m_rowrng.IsEmpty() )
				return (format_xacross == m_nFormat) ? XFERR_INVALID_X_COORD : XFERR_INVALID_Y_COORD;
			m_rowrng.GetRange(0, m_nRr1, m_nRc1, m_nRr2, m_nRc2, dsRow);
			if ( !dsRow || !is_same_layer(dsRow, m_wks) )
				return XFERR_SELECT_DIFF_WORKSHEET;
			
			int nRowSelMode = _get_range_mode(m_rowrng);
			if ( (nRowSelMode & SEL_WHOLE_ROW) )
			{
				if ( m_nRr1 >= 0 )
					_update_input_range(m_nRr1, m_nRc1, m_nRr2, m_nRc2, dsRow);
				if ( m_nRc1 < m_nZc1 )
					m_nRc1 = m_nZc1;
				if ( m_nRc2 > m_nZc2 || m_nRc2 < 0 )	///Sophy 8/20/2010 ORG-815-P8 PROPER_INIT_CUSTOM_RANGE_WHEN_SELECT_WHOLE_LABEL_ROW
					m_nRc2 = m_nZc2;
			}
		}
		break;
	default:
		ASSERT(false);
		nRowRange = VM_NO_ROW_RANGE;
		break;
	}
	return nRowRange;
}

int		VirtualMatrixConstructor::InitColRange()
{
	int nColRange = VM_NORMAL_RANGE;
	int nRowStartOffset = (rowpos_selrow1 == m_nRowMode || rowpos_wksrow1 == m_nRowMode && m_nR1 == 0) ? 1 : 0;
	
	//update Z range
	m_nZr1 = m_nR1 + nRowStartOffset;	///Sophy 8/24/2010 ORG-815-P11 CENTRALIZE_CODE_UPDATE_Z_RANGE_INDICES
	
	switch(m_nColMode)
	{
	case colpos_none:
		nColRange = VM_NO_COL_RANGE;
		break;
	case colpos_selcol1:
		m_nCr1 = m_nR1 + nRowStartOffset;
		m_nCc1 = m_nC1;
		m_nCr2 = m_nR2;
		m_nCc2 = m_nC1;
		break;
	case colpos_wkscol1:
		m_nCr1 = m_nR1 + nRowStartOffset;
		m_nCc1 = 0;
		m_nCr2 = m_nR2;
		m_nCc2 = 0;
		break;
	case colpos_xcol:
		{
			int nXIndex = m_wks.FindColIndex(m_nC1, OKDATAOBJ_DESIGNATION_X, FCI_NO_CHECK_EVEN_SPACED);
			if ( nXIndex >= 0 )
			{
				m_nCr1 = m_nR1 + nRowStartOffset;
				m_nCc1 = nXIndex;
				m_nCr2 = m_nR2;
				m_nCc2 = nXIndex;
			}
			else
			{
				nColRange = VM_NO_COL_RANGE;
			}
		}
		break;
	case colpos_custom:
		{
			Datasheet dsCol;
			if ( !m_colrng || m_colrng.IsEmpty() )
				return (format_xacross == m_nFormat) ? XFERR_INVALID_Y_COORD : XFERR_INVALID_X_COORD;
			m_colrng.GetRange(0, m_nCr1, m_nCc1, m_nCr2, m_nCc2, dsCol);
			if ( !dsCol || !is_same_layer(dsCol, m_wks) )
				return XFERR_SELECT_DIFF_WORKSHEET;
			
			int nColSelMode = _get_range_mode(m_colrng);
			if ( (nColSelMode & SEL_WHOLE_COL) )
			{
				if ( m_nCr1 >= 0 )
					_update_input_range(m_nCr1, m_nCc1, m_nCr2, m_nCc2, dsCol);
				if ( m_nCr1 < m_nZr1 )
					m_nCr1 = m_nZr1;
				if ( m_nCr2 > m_nZr2 || m_nCr2 < 0 ) ///Sophy 8/20/2010 ORG-815-P8 PROPER_INIT_CUSTOM_RANGE_WHEN_SELECT_WHOLE_LABEL_ROW
					m_nCr2 = m_nZr2;
			}
		}
		break;
	default:
		ASSERT(false);
		nColRange = VM_NO_COL_RANGE;
		break;
	}
	return nColRange;
}

BOOL	VirtualMatrixConstructor::MakeVirtualMatrixRange(const TreeNode& trGetN, DataRange& drVM)
{
	if ( CER_NO_ERROR != Init(trGetN) )
		return FALSE;
	
	return AddRanges(drVM);
}
///Sophy 8/27/2010 ORG-923-P1 LT_EXECUTE_FAIL_TO_RESOLVE_INPUT_RANGE_DUE_TO_ACTIVE_RESULT_GRAPH
BOOL	VirtualMatrixConstructor::MakeVirtualMatrixRange(const TreeNode& trGetN, Range& irng, Range& rowrng, Range& colrng, DataRange& drVM)
{
	if ( CER_NO_ERROR != Init(trGetN, irng, rowrng, colrng) )
		return FALSE;
	
	return AddRanges(drVM);
}

BOOL	VirtualMatrixConstructor::AddRanges(DataRange& drVM)
{
	BOOL bRet = TRUE;
	bRet &= AddX(drVM);
	bRet &= AddY(drVM);
	bRet &= AddZ(drVM);
	return bRet;
}
///end LT_EXECUTE_FAIL_TO_RESOLVE_INPUT_RANGE_DUE_TO_ACTIVE_RESULT_GRAPH

BOOL	VirtualMatrixConstructor::AddX(DataRange& drVM)
{
	if ( format_xacross == m_nFormat )
		return AddRow(drVM, "X");
	else
		return AddCol(drVM, "X");
	return TRUE;
}

BOOL	VirtualMatrixConstructor::AddY(DataRange& drVM)
{
	if ( format_xacross == m_nFormat )
		return AddCol(drVM, "Y");
	else
		return AddRow(drVM, "Y");
	return TRUE;
}

BOOL	VirtualMatrixConstructor::AddZ(DataRange& drVM)
{
	drVM.Add("Z", m_wks, m_nZr1, m_nZc1, m_nZr2, m_nZc2);
	return TRUE;
}

BOOL	VirtualMatrixConstructor::AddRow(DataRange& drVM, LPCSTR lpcszName)
{
	int nInitRet = InitRowRange();
	if ( VM_NO_ROW_RANGE == nInitRet )
		return TRUE; //no need to add
	else if ( VM_NORMAL_RANGE == nInitRet )
	{
		drVM.Add(lpcszName, m_wks, m_nRr1, m_nRc1, m_nRr2, m_nRc2);
	}
	else //error when init range
	{
		return FALSE;
	}
	return TRUE;
}

BOOL	VirtualMatrixConstructor::AddCol(DataRange& drVM, LPCSTR lpcszName)
{
	int nInitRet = InitColRange();
	if ( VM_NO_COL_RANGE == nInitRet )
		return TRUE; //no need to add
	else if ( VM_NORMAL_RANGE == nInitRet )
	{
		drVM.Add(lpcszName, m_wks, m_nCr1, m_nCc1, m_nCr2, m_nCc2);
	}
	else //error when init range
	{
		return FALSE;
	}
	return TRUE;
}

int		VirtualMatrixConstructor::UpdateGUI(TreeNode& trGetN, bool bAllowAuto)
{
	int nInit = Init(trGetN);
	int nRowInit = InitRowRange();
	int nColInit = InitColRange();
	
	//update labels
	TreeNode trRow = trGetN.rowpos;
	TreeNode trCol = trGetN.colpos;
	BOOL bTransPosed = format_yacross == m_nFormat;
	trRow.SetAttribute(STR_LABEL_ATTRIB, bTransPosed ? STR_Y_VALUES_IN : STR_X_VALUES_IN);
	trCol.SetAttribute(STR_LABEL_ATTRIB, bTransPosed ? STR_X_VALUES_IN : STR_Y_VALUES_IN);
	trGetN.rowrng.SetAttribute(STR_LABEL_ATTRIB, bTransPosed ? STR_Y_VALUES : STR_X_VALUES);
	trGetN.colrng.SetAttribute(STR_LABEL_ATTRIB, bTransPosed ? STR_X_VALUES : STR_Y_VALUES);

	//update combolists
	int nSelMode = _get_range_mode(m_irng);
	string strCombo, strMap;
	vector<string>	vsLabels, vsIndices;
	//row combolist, default value
	bool bAutoRow = bAllowAuto && (octree_get_auto_support(&trRow) == 1); //whether need to auto init mode selection
	vsLabels.Add(_L("None"));	vsIndices.Add(rowpos_none);
	vsLabels.Add(_L("1st row in selection"));	vsIndices.Add(rowpos_selrow1);
	if ( !(IS_WHOLE_SHEET(nSelMode)) )
	{
		vsLabels.Add(_L("1st row in worksheet"));	vsIndices.Add(rowpos_wksrow1);
		if ( bAutoRow )
			m_nRowMode = rowpos_none;
	}
	else
	{
		if ( bAutoRow )
			m_nRowMode = rowpos_selrow1;
	}
	vsLabels.Add(_L("Column Label"));	vsIndices.Add(rowpos_label);
	vsLabels.Add(_L("Custom"));		vsIndices.Add(rowpos_custom);
	strCombo.SetTokens(vsLabels, '|');
	strMap.SetTokens(vsIndices, '|');
	trRow.SetAttribute(STR_COMBO_ATTRIB, strCombo);	trRow.SetAttribute(STR_INTMAP_ATTRIB, strMap);
	//invalid, should be updated
	if ( vsIndices.Find((string)m_nRowMode) < 0 )
		m_nRowMode = rowpos_selrow1;
	
	trRow.nVal = m_nRowMode;
	octree_set_auto_support(&trRow, 0);
	//column combolist, default value
	bool bAutoCol = bAllowAuto && (octree_get_auto_support(&trCol) == 1); //whether need to auto init mode selection
	vsLabels.SetSize(0);	vsIndices.SetSize(0);
	vsLabels.Add(_L("None"));	vsIndices.Add(colpos_none);
	vsLabels.Add(_L("1st column in selection"));	vsIndices.Add(colpos_selcol1);
	if ( !(IS_WHOLE_SHEET(nSelMode)) )
	{
		vsLabels.Add(_L("1st column in worksheet"));	vsIndices.Add(colpos_wkscol1);
		int nXIndex = m_wks.IsValid() ? m_wks.FindColIndex(m_nC1, OKDATAOBJ_DESIGNATION_X, FCI_NO_CHECK_EVEN_SPACED) : -1;
		if ( nXIndex >= 0 )
		{
			vsLabels.Add(_L("X column to the left of selection"));	vsIndices.Add(colpos_xcol);
			if ( bAutoCol )
				m_nColMode = colpos_xcol;
		}
		else if ( bAutoCol )
			m_nColMode = colpos_none;
	}
	else
	{
		if ( bAutoCol )
			m_nColMode = colpos_selcol1;
	}
	vsLabels.Add(_L("Custom"));		vsIndices.Add(colpos_custom);
	strCombo.SetTokens(vsLabels, '|');
	strMap.SetTokens(vsIndices, '|');
	trCol.SetAttribute(STR_COMBO_ATTRIB, strCombo);	trCol.SetAttribute(STR_INTMAP_ATTRIB, strMap);
	//invalid, should be updated.
	if ( vsIndices.Find((string)m_nColMode) < 0 )
	{
		if ( IS_WHOLE_SHEET(nSelMode) )
			m_nColMode = colpos_selcol1;
		else if ( vsIndices.Find((string)colpos_wkscol1) >= 0 )
			m_nColMode = colpos_wkscol1;
		else
			m_nColMode = colpos_none;
	}
	trCol.nVal = m_nColMode;
	octree_set_auto_support(&trCol, 0);
	
	//show hide controls
	trGetN.label.Show = (rowpos_label == m_nRowMode);
	trGetN.rowrng.Show = (rowpos_custom == m_nRowMode);
	trGetN.colrng.Show = (colpos_custom == m_nColMode);
	return CER_NO_ERROR;
}

int		VirtualMatrixConstructor::CheckInput(TreeNode& trGetN)
{
	int nInit = Init(trGetN);
	if ( CER_NO_ERROR != nInit )
		return nInit;
	
	int nRowInit = InitRowRange();
	if ( CER_NO_ERROR != nRowInit && VM_NORMAL_RANGE != nRowInit && VM_NO_ROW_RANGE != nRowInit )
		return nRowInit;
	
	int nColInit = InitColRange();
	if ( CER_NO_ERROR != nColInit && VM_NORMAL_RANGE != nColInit && VM_NO_COL_RANGE != nColInit )
		return nColInit;
	
	//check Z range
	if ( m_nZc2 - m_nZc1 < 1 || m_nZr2 - m_nZr1 < 1 )
		return XFERR_RANGE_LESS_THAN_2X2;
	
	//check row range
	if ( rowpos_none != m_nRowMode ) //if not none, need to check monotonic and range
	{
		if ( m_nRr2 != m_nRr1 )
			return XFERR_REQUIRE_SINGLE_ROW;
		if ( m_nRc1 != m_nZc1 || m_nRc2 != m_nZc2 )
			return (format_xacross == m_nFormat) ? XFERR_X_COORD_IS_NOT_MATCH_WITH_INPUT_DATA_WIDTH : XFERR_Y_COORD_IS_NOT_MATCH_WITH_INPUT_DATA_HEIGHT;
		if ( rowpos_custom == m_nRowMode && m_nRr1 >= m_nZr1 && m_nRr1 <= m_nZr2 )
			return (format_xacross == m_nFormat) ? XFERR_IRNG_ACROSS_X_VALUES : XFERR_IRNG_ACROSS_Y_VALUES;
		int nFlag = _is_subrng_valid_xy_array(m_wks, m_nRr1, m_nRc1, m_nRr2, m_nRc2, false);
		if ( 0 == nFlag )
			return (format_xacross == m_nFormat) ? XFERR_RANGE_X_NOT_MONOTONIC : XFERR_RANGE_Y_NOT_MONOTONIC;
		else if ( -1 == nFlag )
			return (format_xacross == m_nFormat) ? XFERR_ROW_PARAMETER_EMPTY_OR_HAS_TEXT_DATA : XFERR_COL_PARAMETER_EMPTY_OR_HAS_TEXT_DATA;
	}
	
	//check column range
	if ( colpos_none != m_nColMode ) //not none, need to check
	{
		if ( m_nCc1 != m_nCc2 )
			return XFERR_REQUIRE_SINGLE_COL;
		if ( m_nCr1 != m_nZr1 || m_nCr2 != m_nZr2 )
			return (format_xacross == m_nFormat) ? XFERR_Y_COORD_IS_NOT_MATCH_WITH_INPUT_DATA_HEIGHT : XFERR_X_COORD_IS_NOT_MATCH_WITH_INPUT_DATA_WIDTH;
		if ( colpos_custom == m_nColMode && m_nCc1 >= m_nZc1 && m_nCc1 <= m_nZc2 )
			return (format_xacross == m_nFormat) ? XFERR_IRNG_ACROSS_Y_VALUES : XFERR_IRNG_ACROSS_X_VALUES;
		int nFlag = _is_subrng_valid_xy_array(m_wks, m_nCr1, m_nCc1,  m_nCr2, m_nCc2, true);
		if ( 0 == nFlag )
			return (format_xacross == m_nFormat) ? XFERR_RANGE_Y_NOT_MONOTONIC : XFERR_RANGE_X_NOT_MONOTONIC;
		else if ( -1 == nFlag )
			return (format_xacross == m_nFormat) ? XFERR_COL_PARAMETER_EMPTY_OR_HAS_TEXT_DATA : XFERR_ROW_PARAMETER_EMPTY_OR_HAS_TEXT_DATA;
	}
	
	return CER_NO_ERROR;
}

static	bool	_construct_virtual_range(TreeNode& trGetN, DataRange& drVM)
{
	VirtualMatrixConstructor vmc;
	return vmc.MakeVirtualMatrixRange(trGetN, drVM);
}

///Sophy 8/27/2010 ORG-923-P1 LT_EXECUTE_FAIL_TO_RESOLVE_INPUT_RANGE_DUE_TO_ACTIVE_RESULT_GRAPH
//when from LT script, use "1!" for range can not be resolved since result graph is active page at that moment.
static	bool	_construct_virtual_range(TreeNode&trGetN, Range& irng, Range& rowrng, Range& colrng, DataRange& drVM)
{
	VirtualMatrixConstructor vmc;
	return vmc.MakeVirtualMatrixRange(trGetN, irng, rowrng, colrng, drVM);
}
///end LT_EXECUTE_FAIL_TO_RESOLVE_INPUT_RANGE_DUE_TO_ACTIVE_RESULT_GRAPH

static	int		_check_input(const TreeNode& trGetN)
{
	VirtualMatrixConstructor vmc;
	return vmc.CheckInput(trGetN);
}

static	void	_update_gui_on_format_change(TreeNode& trGetN, bool bAllowAuto)
{
	VirtualMatrixConstructor vmc;
	vmc.UpdateGUI(trGetN, bAllowAuto);
}

static	void	_load_err_msg(int nErr, const TreeNode& trGetN, string& strErrMsg)
{
	BOOL bTranspose = (format_yacross == trGetN.format.nVal);
	strErrMsg = nErr;
	if ( XFERR_INVALID_SERIES_NAME == nErr )
	{
		string strZtitle = trGetN.ztitle.strVal;
		strErrMsg += ":" + strZtitle;
	}
	else if ( XFERR_REQUIRE_SINGLE_COL == nErr )
	{
		strErrMsg += ":" + (bTranspose ? STR_X_VALUES : STR_Y_VALUES);
	}
	else if ( XFERR_REQUIRE_SINGLE_ROW == nErr )
	{
		strErrMsg += ":" + (bTranspose ? STR_Y_VALUES : STR_X_VALUES);
	}
}


void plotvm(const Range& irng, const int& format, const int& rowpos, const Range& rowrng, const string& label, const int& colpos, const Range& colrng, const string& xtitle, const string& ytitle, const string& ztitle, const int& type, GraphLayer& ogl)
{
	DataRange drVM;
	Tree trRoot, trGetN;
	XFExeContext* pCtxt = Project.GetCurrentXFExeCtxt();
	pCtxt->GetGUI(trRoot, trGetN);
	///Sophy 8/27/2010 ORG-923-P1 LT_EXECUTE_FAIL_TO_RESOLVE_INPUT_RANGE_DUE_TO_ACTIVE_RESULT_GRAPH
	//if ( !_construct_virtual_range(trGetN, drVM) )
	if ( !_construct_virtual_range(trGetN, irng, rowrng, colrng, drVM) )
	///end LT_EXECUTE_FAIL_TO_RESOLVE_INPUT_RANGE_DUE_TO_ACTIVE_RESULT_GRAPH
		XF_THROW(-1);
	
	MatrixObject mo;
	mo.Attach(drVM, format_yacross == trGetN.format.nVal ? MATOBJATTACH_TRANSPOSE : 0);
	
	#ifdef 		__VIRTUAL_MATRIX__
	mo.SetAxesLongName(xtitle, ytitle);
	mo.SetLongName(ztitle);
	#endif		//__VIRTUAL_MATRIX__	
	int nPlot = ogl.AddPlot(mo, type);
	ogl.Rescale();
	mo.Detach();
}

//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 plotvm_event1(TreeNode& trGetN, int nRow, int nEventID, DWORD& dwEnables, LPCSTR lpcszNodeName, WndContainer& DynaCntrlContainer, string& strAux, string& strErrMsg)
{
	if( xf_is_extended_event(nEventID) )
		return plotvm_event1_ex(trGetN, nRow, nEventID, dwEnables, lpcszNodeName, DynaCntrlContainer, strAux, strErrMsg);
	DECLARE_BUTTON_ENABLES
	int nErr = CER_NO_ERROR;
	Worksheet wks;
	okxf_resolve_string_get_origin_object(trGetN.irng.strVal, &wks);
	ASSERT(wks);
	if(!wks)
		nErr = CER_NO_NUMERIC_WORKSHEET;
	
	bool bInitDlg = (GETNE_ON_INIT == nEventID) || (GETNE_ON_THEME == nEventID);
	if ( bInitDlg )
		_init_dlg(trGetN, wks);
	
	int nThemeSettings = xf_is_theme_applied(trGetN);
	vector<string> vsNodes = {"rowpos", "colpos", "format", "irng"};
	string strNodeName(lpcszNodeName);
	if ( bInitDlg || vsNodes.Find(strNodeName) >= 0 )
		_update_gui_on_format_change(trGetN, (THEME_NONE == nThemeSettings));
	
	///Sophy 8/20/2010 ORG-815-P9 PROPER_INIT_Z_TITLE_WHEN_RELOAD_THEME
	if ( bInitDlg )
		_init_xyz_titles(trGetN); //this event should be trigger after _update_gui_on_format_change, whith might affect the Z range
	///end PROPER_INIT_Z_TITLE_WHEN_RELOAD_THEME
	
	///Sophy 8/31/2010 ORG-923-P2 LABEL_INDEX_NOT_SET_WHEN_RUN_FROM_SCRIPT_MODE
	//vector<string> vsColLabels = {"rowpos", "label"};
	//if ( vsColLabels.Find(strNodeName) >= 0 )
		//_init_column_label(trGetN, wks, false, &sg_nColLabel);
	///end LABEL_INDEX_NOT_SET_WHEN_RUN_FROM_SCRIPT_MODE
	
	if ( CER_NO_ERROR == nErr )
		nErr = _check_input(trGetN);
	
	if ( CER_NO_ERROR == nErr )
	{
		nErr = _update_xyz_titles(trGetN);
	}
	if ( CER_NO_ERROR != nErr )
	{
		_load_err_msg(nErr, trGetN, strErrMsg);
		bOKEnable = false;
	}
	return true;

}

int plotvm_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 plotvm_before_execute(TreeNode& trGetN, int nGetNDialog, int& nRet, int dwCntrl)
{
	
	if ( 0 == nGetNDialog ) //script mode
	{
		int nThemeSettings = xf_is_theme_applied(trGetN);
		_update_gui_on_format_change(trGetN, (THEME_NONE == nThemeSettings));
		int nErr = _check_input(trGetN);
		if ( CER_NO_ERROR != nErr )
		{
			string strErrMsg;
			_load_err_msg(nErr, trGetN, strErrMsg);
			xf_warning_msg_box(strErrMsg, false, 'E');
			nRet = XFEVT_ABORT;
		}
	}
}
//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 plotvm_event1_ex(TreeNode& trGetN, int nRow, int nEventID, DWORD& dwEnables, LPCSTR lpcszNodeName, WndContainer& DynaCntrlContainer, string& strAux, string& strErrMsg)
{
	return false;
}
