#include <origin.h>
#include <GetNBox.h>
#include <xfutils.h>
enum{
	action_repeat,
	action_enum,
	action_ran,
	action_ins,
};

/*------------------------------------------------------------------------------*
 * Modification Log:															*
 *	CPY 2/13/2008 QA70-11016 AUTO_FILL_CHECK_CELL_TYPE							*
 *	Folger 08/17/09 RUNTIME_ERROR_WHEN_TRY_TO_AUTOFILL_FROM_ROW_HEADER			*
 *------------------------------------------------------------------------------*/


static double _get_ran(double x1, double x2)
{
	double xx = ran();
	return x1 + xx * (x2-x1);
}

//action_ran, uniform random number between x and inc
//action_enum, start with x and inc each next
static void _fill_numeric(Datasheet& wks, int r1, int c1, int nRows, int nCols, double x, double inc, int nAction)
{
	double x1 = x;
	double x2 = inc; // for action_ran case
	vector vv;
	for(int ii = 0, nr = r1; ii < nRows; ii++, nr++)
	{
		for(int jj = 0, nc = c1; jj < nCols; jj++, nc++)
		{
			wks.SaveRangeUndo(nr, nc, nr, nc); /// TD 02-08-2008 QA80-11016 AUTO_FILL_AND_EMBEDDINGS_OR_LINK
			if(action_enum == nAction)
			{
				wks.SetCell(nr, nc, x);
				x += inc;
			}
			else
			{
				wks.SetCell(nr, nc, _get_ran(x1, x2));
			}
		}
	}

}
static void _fill_text(Datasheet& wks, int r1, int c1, int nRows, int nCols, vector<string>& vs, int nAction)
{
	int nn = 0;
	string str;
	for(int ii = 0, nr = r1; ii < nRows; ii++, nr++)
	{
		for(int jj = 0, nc = c1; jj < nCols; jj++, nc++)
		{
			if(nn >= vs.GetSize())
				nn = 0;
			str = vs[nn++];
			
			wks.SaveRangeUndo(nr, nc, nr, nc); /// TD 02-08-2008 QA80-11016 AUTO_FILL_AND_EMBEDDINGS_OR_LINK
			wks.SetCell(nr, nc, str);
		}
	}
}
static void _fill_text_enum(Datasheet& wks, int r1, int c1, int nRows, int nCols, LPCSTR lpcszRoot, int x, int inc, int nAction)
{
	string str;
	for(int ii = 0, nr = r1; ii < nRows; ii++, nr++)
	{
		for(int jj = 0, nc = c1; jj < nCols; jj++, nc++)
		{
			str.Format("%s%d", lpcszRoot, x);
			wks.SaveRangeUndo(nr, nc, nr, nc); /// TD 02-08-2008 QA80-11016 AUTO_FILL_AND_EMBEDDINGS_OR_LINK
			wks.SetCell(nr, nc, str);
			x += inc;
		}
	}
}
//---- CPY 2/13/2008 QA70-11016 AUTO_FILL_CHECK_CELL_TYPE
#define STR_CELL_REF_LEFT "cell://left" // use this as standard signature for any linking to be extended
//----
static string _get_cell_text_chk_link(Datasheet& wks, int nr, int nc)
{
	DWORD dwExtraInfo = 0;
	string str = wks.TCell(nr, nc, TCELL_VALUE, &dwExtraInfo);
	if(dwExtraInfo) // any type of link, embed cell
		str = STR_CELL_REF_LEFT;
	
	return str;
}
	
// return true if all numeric
static bool _get_values(Datasheet& wks, int r1, int c1, int r2, int c2, vector<string>& vs, vector& vv)
{
	int nCount = 0;
	vv.SetSize(0); vs.SetSize(0);
	for(int nr = r1; nr <= r2; nr++)
	{
		for(int nc = c1; nc <= c2; nc++)
		{
			double xx = wks.Cell(nr, nc);
			if(is_missing_value(xx))
				nCount++;
			vv.Add(xx);
			//---- CPY 2/13/2008 QA70-11016 AUTO_FILL_CHECK_CELL_TYPE
			//vs.Add(wks.TCell(nr, nc));
			vs.Add(_get_cell_text_chk_link(wks, nr, nc));
			//----
		}
	}
	return nCount==0?true:false;
}

enum {
	RNG_INVALID,		///------ Folger 08/17/09 RUNTIME_ERROR_WHEN_TRY_TO_AUTOFILL_FROM_ROW_HEADER
	RNG_NUMERIC,
	RNG_TEXT,
	RNG_TEXT_ENUM,
};

static int _get_enum_val(const string& str, string& strRoot)
{
	if(str.GetLength() > MAXLINE)
		return -1;
	char szTemp[MAXLINE];
	int nn = string_to_prefix_end_number(szTemp, str);
	if(nn <= 0)
		return -2;
	
	strRoot = szTemp;
	return nn;
}

static int _get_range_type(Datasheet& wks, int r1, int c1, int r2, int c2, vector<string>& vs, double& x0, double& inc, int nAction)
{
	vector vv;
	bool bNumeric = _get_values(wks, r1, c1, r2, c2, vs, vv);
	///------ Folger 08/17/09 RUNTIME_ERROR_WHEN_TRY_TO_AUTOFILL_FROM_ROW_HEADER
	if ( vv.GetSize() == 0 )
		return RNG_INVALID;
	///------ End RUNTIME_ERROR_WHEN_TRY_TO_AUTOFILL_FROM_ROW_HEADER
	if(bNumeric)
	{
		inc = 0;
		x0 = vv[0];
		if(vv.GetSize() > 1)
		{
			if(action_ran == nAction)
			{
				vv.GetMinMax(x0, inc);
			}
			else
			{
				inc = vv[1] - vv[0];
				x0 = vv[vv.GetSize() - 1] + inc;
			}
		}
		return RNG_NUMERIC;
	}
	// checn if terminate with number
	string str;
	int n0 = _get_enum_val(vs[0], str);
	if((nAction != action_enum && nAction != action_ran) || n0 < 0)
		return RNG_TEXT;
	inc = 1;
	if(vv.GetSize() > 1)
	{
		string str1;
		int n1 = _get_enum_val(vs[1], str1);
		int nn = _get_enum_val(vs[vv.GetSize() - 1], str1);
		if(n1 < 0 || nn < 0)
			return RNG_TEXT;
		if(action_ran == nAction)
		{
			x0 = n0;
			inc = nn;
			vs[0] = str;
			return RNG_TEXT_ENUM;
		}
		inc = n1 - n0;
		n0 = nn;
	}
	x0 = n0 + inc;
	vs[0] = str;
	return RNG_TEXT_ENUM;
}
static void _auto_fill(bool bFillAcrossCol, Datasheet& wks, int nn, int i1, int i2, int nNew, int nAction)
{
	vector<string> vs;
	double x0, xinc;
	int nRngType;
	if(bFillAcrossCol)
		nRngType = _get_range_type(wks, nn, i1, nn, i2, vs, x0, xinc, nAction);
	else
		nRngType = _get_range_type(wks, i1, nn, i2, nn, vs, x0, xinc, nAction);
	
	if(action_repeat == nAction)
		nRngType = RNG_TEXT;
		
	if(RNG_NUMERIC == nRngType)
	{
		if(bFillAcrossCol)
			_fill_numeric(wks, nn, i2+1, 1, nNew - i2, x0, xinc, nAction);	
		else
			_fill_numeric(wks, i2+1, nn, nNew - i2, 1, x0, xinc, nAction);
	}
	else if(RNG_TEXT == nRngType)
	{
		if(bFillAcrossCol)
			_fill_text(wks, nn, i2+1, 1, nNew - i2, vs, nAction);	
		else
		//--- CPY 2/13/2008 QA70-11016 AUTO_FILL_CHECK_CELL_TYPE
		//	_fill_text(wks, i2+1, nn, nNew - i2, 1, vs, nAction);
		{
			if(is_in_list(STR_CELL_REF_LEFT, vs, true))
			{
				for(int kk = 0; kk < vs.GetSize(); kk++)
					vs[kk] = "cell://top";
			}
			_fill_text(wks, i2+1, nn, nNew - i2, 1, vs, nAction);
		}
		//---
	}
	///------ Folger 08/17/09 RUNTIME_ERROR_WHEN_TRY_TO_AUTOFILL_FROM_ROW_HEADER
	//else //RNG_TEXT_ENUM
	else if ( RNG_TEXT_ENUM == nRngType )
	///------ End RUNTIME_ERROR_WHEN_TRY_TO_AUTOFILL_FROM_ROW_HEADER
	{
		int n0 = 0.5+x0;
		int inc = 0.5+xinc;
		if(bFillAcrossCol)
			_fill_text_enum(wks, nn, i2+1, 1, nNew - i2, vs[0], n0, inc, nAction);	
		else
			_fill_text_enum(wks, i2+1, nn, nNew - i2, 1, vs[0], n0, inc, nAction);
	}
}
//---- CPY 2/13/2008 QA70-11016 AUTO_FILL_CHECK_CELL_TYPE
/*
static BOOL _AutoFillEmbedLinks(Worksheet wks, int rSrc, int cSrc, int r2, int c2)
{
	string strFill;
	strFill.Format("Cell://%d[%d]", cSrc+1, rSrc+1);
	for(int c = cSrc; c <= c2; c++)
	{
		for(int r = rSrc; r <= r2; r++)
		{
			if(!(r == rSrc && c == cSrc))
			{
				wks.SaveRangeUndo(r, c, r, c); 
				wks.SetCell(r, c, strFill);
			}
		}
	}
	return TRUE;
}
*/
//-----



void wautofill(const Range& irng, Range& orng, const int& action)
{
	
	Datasheet wks1, wks2;
	int r1,r2,c1,c2;
	if(!irng.GetRange(0, r1, c1, r2, c2, wks1))
		XF_THROW(XFERR_INVALID_ARG);
	int i1,i2,n1,n2;
	if(!orng.GetRange(0, i1, n1, i2, n2, wks2))
		XF_THROW(XFERR_INVALID_ARG);
	
	//printf("starting row=(%d:%d), col=(%d:%d)\n", r1,r2, c1,c2);
	//printf("ending row=(%d:%d), col=(%d:%d)\n", i1,i2,n1,n2);
	
	/// TD 02-08-2008 QA80-11016 AUTO_FILL_AND_EMBEDDINGS_OR_LINK
	//wks2.SaveRangeUndo(i1, n1, i2, n2);
	UndoBlock block;
	/// end AUTO_FILL_AND_EMBEDDINGS_OR_LINK
	//---- CPY 2/13/2008 QA70-11016 AUTO_FILL_CHECK_CELL_TYPE
	/*
	/// TD 02-08-2008 QA80-11016 AUTO_FILL_AND_EMBEDDINGS_OR_LINK
	DWORD dwEmbedParams = 0;
	Worksheet wksheet = wks1;
	if(wksheet && r1 == r2 && c1 == c2 && wksheet.GetEmbeddingParams(r1, c1, dwEmbedParams))
	{
		_AutoFillEmbedLinks(wksheet, r1, c1, i2, n2);
		return;
	}
	*/
	/// end AUTO_FILL_AND_EMBEDDINGS_OR_LINK
	
	int ii;
	if(n2 > c2) // fill horizontally
	{
		
		for(ii = r1; ii <= r2; ii++)
			_auto_fill(true, wks2, ii, c1, c2, n2, action);
		
		// next check to also fill vertically
		if(i2 > r2)
		{
			for(ii = c1; ii <=n2; ii++)
				_auto_fill(false, wks2, ii, r1, r2, i2, action);
		}
	}
	else
	{
		for(int ii = c1; ii <=c2; ii++)
			_auto_fill(false, wks2, ii, r1, r2, i2, action);
	}
	
}

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

}

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