#include <origin.h>
#include <GetNBox.h>
#include <xfutils.h>
enum{
	operation_first,
	operation_last,
	operation_right,
	operation_left,
	operation_pos,
};
#include <control.h>
//put additional include files here

/*------------------------------------------------------------------------------*
 * Modification Log:															*
 *	Folger 10/28/2010 ORG-1311-S2 COLMOVE_SUPPORT_MOVE_COLUMNS_TO_SPECIFIED_INDEX
 *------------------------------------------------------------------------------*/

//put your own support static functions here
//Get the first and last column from range
static bool _get_columns_from_range(const DataRange& drRef, Datasheet& wks, int& c1, int& c2)
{
	int nNum = drRef.GetNumRanges();
	if(nNum == 0)
		return false;
	
	vector vc1, vc2;
	vc1.SetSize(nNum);
	vc2.SetSize(nNum);
	 _get_sorted_columns_from_range(drRef, wks, vc1, vc2);
	 c1 = vc1[0];
	 c2 = vc2[nNum-1];
	return true;
}
//Check the operation selected by user is legal or not 
static int _check_operation_legal(Datasheet& wks, int c1, int c2, int nOperation)
{
	int nCols = wks.GetNumCols();
	switch(nOperation)
	{
		case MOVE_COL_TO_FIRST:
			if(c1 == 0)
				return XFERR_COLS_AT_FIRST;
			break;
		case MOVE_COL_TO_LAST:
			if(c2 == nCols-1)
				return XFERR_COLS_AT_LAST;
			break;
		case MOVE_COL_TO_RIGHT:
			if(c2 == nCols-1)
				return XFERR_COLS_BE_RIGHTMOST;
			break;
		case MOVE_COL_TO_LEFT:
			if(c1 == 0)
				return XFERR_COLS_BE_LEFTMOST;
			break;
	}
	return OE_NOERROR;	
}
//Init operation by user selected columns
static bool _check_reset_to_operation(TreeNode& trGetN, const DataRange& drRef)
{
	int c1, c2;
	Datasheet wks;
	if(!_get_columns_from_range(drRef, wks, c1, c2))
		return false;

	int nCols = wks.GetNumCols();
	int nOperation = trGetN.Operation.nVal+1;
	switch(nOperation)
	{
		case MOVE_COL_TO_FIRST:
			if(c1 == 0)
				trGetN.Operation.nVal = MOVE_COL_TO_LAST-1;	
			break;
		case MOVE_COL_TO_LAST:
			if(c2 == nCols-1)
				trGetN.Operation.nVal = MOVE_COL_TO_FIRST-1;
			break;
		case MOVE_COL_TO_RIGHT:
			if(c2 == nCols-1)
				trGetN.Operation.nVal = MOVE_COL_TO_LEFT-1;		
			break;
		case MOVE_COL_TO_LEFT:
			if(c1 == 0)
				trGetN.Operation.nVal = MOVE_COL_TO_RIGHT-1;		
			break;
	}
	return true;
}
//Check the operation user selected is legal or not
static int _check_reset_to_ok(TreeNode& trGetN, const DataRange& drRef)
{
	int c1, c2;
	Datasheet wks;
	_get_columns_from_range(drRef, wks, c1, c2);

	//int nCols = wks.GetNumCols();
	int nOperation = trGetN.Operation.nVal+1;
	int nRet =  _check_operation_legal(wks, c1, c2, nOperation);
	if(OE_NOERROR != nRet)
		return nRet;
	
	return OE_NOERROR;
}
//Get sorted columns' number from range 
static void _get_sorted_columns_from_range(const Range& rng, Datasheet& wks, vector& vc1, vector& vc2)
{
	int r1, r2, c1, c2;
	int nNum = rng.GetNumRanges();
	
	for(int ii = 0; ii < nNum; ii++)
	{
		rng.GetRange(ii, r1, c1, r2, c2, wks);
		vc1[ii] = c1;
		vc2[ii] = c2;
	}
	vector<uint> vnIndeces;
	vc2.Sort(SORT_ASCENDING, TRUE, vnIndeces);
	vc1.Reorder(vnIndeces);	
}
//Move columns user selected to first
static void _move_cols_to_first(const Range& rng, int nOperation)
{
	int r1, r2, c1, c2, nColBefore = 0;
	int nNum = rng.GetNumRanges();
	Datasheet wks;
	vector vc1, vc2;
	vc1.SetSize(nNum);
	vc2.SetSize(nNum);
	_get_sorted_columns_from_range(rng, wks, vc1, vc2);
	for(int ii = nNum-1; ii >= 0; ii--)
	{
		/// SY 2007-02-19 QA70-9387 v8.0566 MOVE_COLUMN_UNDO
		///	if(!wks.MoveColumns(vc1[ii]+nColBefore, vc2[ii]-vc1[ii]+1, nOperation))
		if(!wks.MoveColumns(vc1[ii]+nColBefore, vc2[ii]-vc1[ii]+1, nOperation, TRUE))
		/// end MOVE_COLUMN_UNDO
		{
			XF_THROW(XFERR_MOVE_COL_FAILED);
		}
		nColBefore = nColBefore+vc2[ii]-vc1[ii]+1;
	}	
}
//Move columns user selected to last
static void _move_cols_to_last(const Range& rng, int nOperation)
{
	int r1, r2, c1, c2, nColBefore = 0;
	int nNum = rng.GetNumRanges();
	Datasheet wks;
	vector vc1, vc2;
	vc1.SetSize(nNum);
	vc2.SetSize(nNum);
	_get_sorted_columns_from_range(rng, wks, vc1, vc2);
	for(int ii = 0; ii < nNum; ii++)
	{
		/// SY 2007-02-19 QA70-9387 v8.0566 MOVE_COLUMN_UNDO
		///	if(!wks.MoveColumns(vc1[ii]-nColBefore, vc2[ii]-vc1[ii]+1, nOperation))
		if(!wks.MoveColumns(vc1[ii]-nColBefore, vc2[ii]-vc1[ii]+1, nOperation, TRUE))
		/// end MOVE_COLUMN_UNDO
		{
			XF_THROW(XFERR_MOVE_COL_FAILED);
		}
		nColBefore = nColBefore+vc2[ii]-vc1[ii]+1;
	}
}
//Move columns user selected to left
///Arvin 01/07/08 WRONG_MOVE_RIGHT_WHEN_USE_CTRL_SELECTED_MULTI_COLUMNS
//static void _move_cols_to_left_or_right(const Range& rng, int nOperation)
static void _move_cols_to_left(const Range& rng, int nOperation)
///end WRONG_MOVE_RIGHT_WHEN_USE_CTRL_SELECTED_MULTI_COLUMNS
{
	int r1, r2, c1, c2, nColBefore = 0;
	int nNum = rng.GetNumRanges();
	Datasheet wks;
	vector vc1, vc2;
	vc1.SetSize(nNum);
	vc2.SetSize(nNum);
	_get_sorted_columns_from_range(rng, wks, vc1, vc2);
	for(int ii = 0; ii < nNum; ii++)
	{
		/// SY 2007-02-19 QA70-9387 v8.0566 MOVE_COLUMN_UNDO
		///	if(!wks.MoveColumns(vc1[ii], vc2[ii]-vc1[ii]+1, nOperation))
		if(!wks.MoveColumns(vc1[ii], vc2[ii]-vc1[ii]+1, nOperation, TRUE))
		/// end MOVE_COLUMN_UNDO
		{
			XF_THROW(XFERR_MOVE_COL_FAILED);
		}
	}
}

//Move columns user selected to right
///Arvin 01/07/08 WRONG_MOVE_RIGHT_WHEN_USE_CTRL_SELECTED_MULTI_COLUMNS
static void _move_cols_to_right(const Range& rng, int nOperation)
{
	int r1, r2, c1, c2, nColBefore = 0;
	int nNum = rng.GetNumRanges();
	Datasheet wks;
	vector vc1, vc2;
	vc1.SetSize(nNum);
	vc2.SetSize(nNum);
	_get_sorted_columns_from_range(rng, wks, vc1, vc2);
	for(int ii = nNum-1; ii >= 0; ii--)
	{
		/// SY 2007-02-19 QA70-9387 v8.0566 MOVE_COLUMN_UNDO
		///	if(!wks.MoveColumns(vc1[ii], vc2[ii]-vc1[ii]+1, nOperation))
		if(!wks.MoveColumns(vc1[ii], vc2[ii]-vc1[ii]+1, nOperation, TRUE))
		/// end MOVE_COLUMN_UNDO
		{
			XF_THROW(XFERR_MOVE_COL_FAILED);
		}
	}
}
///end WRONG_MOVE_RIGHT_WHEN_USE_CTRL_SELECTED_MULTI_COLUMNS

///------ Folger 10/28/2010 ORG-1311-S2 COLMOVE_SUPPORT_MOVE_COLUMNS_TO_SPECIFIED_INDEX
static	void	_check_show_position(TreeNode& trGetN)
{
	trGetN.col.Show = operation_pos == trGetN.operation.nVal;
}

class MoveColumnsToPositionHelper
{
public:
	int		IsValid(TreeNode& trGetN)
	{
		if ( operation_pos != trGetN.operation.nVal )
			return 0;
	
		Range		rng;;
		TreeNode	trRng = trGetN.rng;
		if ( !okxf_resolve_tree_construct_range(&trRng, &rng) )
			return XFERR_NO_ACTIVE_COLUMN;
		
		Column		col;
		if ( !okxf_resolve_string_get_origin_object(trGetN.col.strVal, &col) || !col )
			return XFERR_INVALID_COLUMN_OBJ;
		
		SheetRangesList rngList(rng);
		int nNumSheet = rngList.GetNumSheets();
		
		for ( int ii=0; ii<nNumSheet; ++ii )
		{
			DataRange drSub;
			if ( rngList.GetWksSubRange(ii, drSub) )
			{
				int		nErr = IsValid(drSub, col);
				if ( nErr )
					return nErr;
			}
		}
		
		return 0;
	}
	
	void	MoveColumns(DataRange& rng, Column& col)
	{
		int			nErr = IsValid(rng, col);
		if ( 0 != nErr )
		{
			XF_THROW(nErr);
		}
		
		PrepareWksAndColumns(rng);
		int		nIndex = col.GetIndex();
		for ( int ii=0; ii<m_vc1.GetSize(); ++ii )
		{
			int		nCols = m_vc2[ii] - m_vc1[ii] + 1;
			if ( !m_ds.MoveColumns(m_vc1[ii], nCols, MOVE_COL_TO_INDEX, TRUE, nIndex) )
				XF_THROW(XFERR_MOVE_COL_FAILED);
			nIndex = UpdateIndices(nIndex, nCols, ii);
		}
	}
	
private:
	int		IsValid(DataRange& rng, Column& col)
	{
		if ( !rng || rng.GetNumRanges() == 0 )
			return XFERR_NO_ACTIVE_COLUMN;
		
		PrepareWksAndColumns(rng);
		int		nIndex = col.GetIndex();
		for ( int ii=0; ii<m_vc1.GetSize(); ++ii )
		{
			if ( nIndex >=m_vc1[ii] && nIndex <= m_vc2[ii]  )
				return XFERR_MOVE_COLUMNS_INTERSECT_WITH_POSITION;
		}
		
		return 0;
	}
	
	void	PrepareWksAndColumns(DataRange& rng)
	{
		int		nNumRanges = rng.GetNumRanges();
		m_vc1.SetSize(nNumRanges);
		m_vc2.SetSize(nNumRanges);
		_get_sorted_columns_from_range(rng, m_ds, m_vc1, m_vc2);
		O_A(m_vc1.GetSize() == m_vc2.GetSize());
	}
	
	int		UpdateIndices(int nCol, int nCols, int nIndex)
	{
		if ( m_vc1[nIndex] > nCol )
		{
			nCol += nCols;
		}
		else if ( nCol - m_vc2[nIndex] > 1 )
		{
			for ( int ii=nIndex+1; ii<m_vc1.GetSize(); ++ii )
			{
				if ( m_vc1[ii] > nCol )
					break;
				
				m_vc1[ii] -= nCols;
				m_vc2[ii] -= nCols;
			}
		}
		
		return nCol;
	}
private:
	Datasheet		m_ds;
	vector			m_vc1, m_vc2;
};
///------ End COLMOVE_SUPPORT_MOVE_COLUMNS_TO_SPECIFIED_INDEX


void colmove(Range& rng, const int& operation, const Column& col)
{
	
	int c1, c2;
	Datasheet wks;
	if(!_get_columns_from_range(rng, wks, c1, c2))
	{
		warning_msg_box(XFERR_NO_ACTIVE_COLUMN, true); ///Echo 11/2/06
		XF_THROW(XFERR_NO_ACTIVE_COLUMN);
	}
	
	int nCase = operation + 1;
	int nRet = _check_operation_legal(wks, c1, c2, nCase);
	if(OE_NOERROR != nRet)
		XF_THROW(nRet);
	//------ Folger 01/18/08 QA70-10946 SUPPORT_MULTIPLE_SHEETS_OPERATION_WITH_SHEET_RANGE_LIST_CLASS
	/*
	switch(nCase)
	{
	case MOVE_COL_TO_FIRST:
		_move_cols_to_first(rng, MOVE_COL_TO_FIRST);
		break;
	case MOVE_COL_TO_LAST:
		 _move_cols_to_last(rng, MOVE_COL_TO_LAST);
		break;
	case MOVE_COL_TO_RIGHT:
		///Arvin 01/07/08 WRONG_MOVE_RIGHT_WHEN_USE_CTRL_SELECTED_MULTI_COLUMNS
		//_move_cols_to_left_or_right(rng, MOVE_COL_TO_RIGHT);
		_move_cols_to_right(rng, MOVE_COL_TO_RIGHT);
		///end WRONG_MOVE_RIGHT_WHEN_USE_CTRL_SELECTED_MULTI_COLUMNS
		break;
	case MOVE_COL_TO_LEFT:
		///Arvin 01/07/08 WRONG_MOVE_RIGHT_WHEN_USE_CTRL_SELECTED_MULTI_COLUMNS
		//_move_cols_to_left_or_right(rng, MOVE_COL_TO_LEFT);
		_move_cols_to_left(rng, MOVE_COL_TO_LEFT);
		///end WRONG_MOVE_RIGHT_WHEN_USE_CTRL_SELECTED_MULTI_COLUMNS
		break;
	default :
		break;
	}
	*/
	SheetRangesList rngList(rng);
	int nNumSheet = rngList.GetNumSheets();
	
	for ( int ii=0; ii<nNumSheet; ++ii )
	{
		DataRange drSub;
		if ( rngList.GetWksSubRange(ii, drSub) )
		{
			switch(nCase)
			{
			case MOVE_COL_TO_FIRST:
				_move_cols_to_first(drSub, MOVE_COL_TO_FIRST);
				break;
			case MOVE_COL_TO_LAST:
				 _move_cols_to_last(drSub, MOVE_COL_TO_LAST);
				break;
			case MOVE_COL_TO_RIGHT:
				_move_cols_to_right(drSub, MOVE_COL_TO_RIGHT);
				break;
			case MOVE_COL_TO_LEFT:
				_move_cols_to_left(drSub, MOVE_COL_TO_LEFT);
				break;
			///------ Folger 10/28/2010 ORG-1311-S2 COLMOVE_SUPPORT_MOVE_COLUMNS_TO_SPECIFIED_INDEX
			case MOVE_COL_TO_INDEX:
				{
					MoveColumnsToPositionHelper	clHelper;
					clHelper.MoveColumns(drSub, col);
				}
				break;
			///------ End COLMOVE_SUPPORT_MOVE_COLUMNS_TO_SPECIFIED_INDEX
			default :
				break;
			}
		}
	}
	//------ End SUPPORT_MULTIPLE_SHEETS_OPERATION_WITH_SHEET_RANGE_LIST_CLASS
		
}

//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 colmove_event1(TreeNode& trGetN, int nRow, int nEventID, DWORD& dwEnables, LPCSTR lpcszNodeName, WndContainer& DynaCntrlContainer, string& strAux, string& strErrMsg)
{
	if( xf_is_extended_event(nEventID) )
		return colmove_event1_ex(trGetN, nRow, nEventID, dwEnables, lpcszNodeName, DynaCntrlContainer, strAux, strErrMsg);
	DECLARE_BUTTON_ENABLES
	///------ Folger 10/28/2010 ORG-1311-S2 COLMOVE_SUPPORT_MOVE_COLUMNS_TO_SPECIFIED_INDEX
	_check_show_position(trGetN);	
	///------ End COLMOVE_SUPPORT_MOVE_COLUMNS_TO_SPECIFIED_INDEX
	int c1, c2;
	Datasheet wks;
	int nErr = CER_NO_ERROR;
	
	Range rTemp;
	TreeNode trTemp = trGetN.rng;
	bool bRet = okxf_resolve_tree_construct_range(&trTemp, &rTemp);
	if(!bRet || !_get_columns_from_range(rTemp, wks, c1, c2))
		nErr = XFERR_NO_ACTIVE_COLUMN;
	
	///------ Folger 10/28/2010 ORG-1311-S2 COLMOVE_SUPPORT_MOVE_COLUMNS_TO_SPECIFIED_INDEX
	if ( 0 == nErr )
	{
		MoveColumnsToPositionHelper	clHelper;
		nErr = clHelper.IsValid(trGetN);
	}
	///------ End COLMOVE_SUPPORT_MOVE_COLUMNS_TO_SPECIFIED_INDEX
	if(nErr != CER_NO_ERROR)
	{
		bOKEnable = false;
		strErrMsg = nErr;
	}
	return false;

}

int colmove_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 colmove_before_execute(TreeNode& trGetN, int nGetNDialog, int& nRet, int dwCntrl)
{
	///------ Folger 10/28/2010 ORG-1311-S2 COLMOVE_SUPPORT_MOVE_COLUMNS_TO_SPECIFIED_INDEX
	if ( is_xf_startup(nGetNDialog, dwCntrl) )
	{
		if ( operation_pos == trGetN.operation.nVal )
		{
			Column		col;
			if ( !okxf_resolve_string_get_origin_object(trGetN.col.strVal, &col) || !col )
			{
				nRet = XFEVT_PROCEED_WITH_DLG;
				return;
			}	
		}
	}
	
	if ( is_xf_really_before_execute(nGetNDialog, dwCntrl) )
	{
		if ( operation_pos != trGetN.operation.nVal )
		{
			trGetN.col.strVal = STR_OPTIONAL;
		}
	}
	///------ End COLMOVE_SUPPORT_MOVE_COLUMNS_TO_SPECIFIED_INDEX
}
//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 colmove_event1_ex(TreeNode& trGetN, int nRow, int nEventID, DWORD& dwEnables, LPCSTR lpcszNodeName, WndContainer& DynaCntrlContainer, string& strAux, string& strErrMsg)
{
	return false;
}
