/*------------------------------------------------------------------------------*
 * File Name: FunctionFormulaDlg.cpp												*
 * Creation: Kyle 11-10-2009													*
 * Purpose: OriginC Source C file												*
 * Copyright (c) ABCD Corp.	2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010		*
 * All Rights Reserved															*
 * 																				*
 * Modification Log:															*
 *------------------------------------------------------------------------------*/
 
#include <Origin.h>
#include <Array.h>
#include "GraphPageControl.h"
#include <..\Originlab\DialogEx.h>
#define BASE_WINDOW		MultiPaneDlg
#include <..\Originlab\DynaDlg.h>
#include <analysis_utils.h>

#include <..\Originlab\nlsf_utils.h>
#include <ONLSF.h>
#include <ocGDI.h>
#include <event_utils.h>
#include "FunctionFormulaDlg.h"

////////////////////////////////////////////////////////////////////////////////////
#define	_SUPPORT_INTERACTIVE_CONTROL_

#define STR_DLG_NAME				_L("Insert Formula")
#define	STR_PARAMETERS				_L("Parameters")
#define	STR_DESCRIPTION				_L("Description")
#define	STR_FORMULA_PREVIEW			_L("Formula to insert:")
#define STR_ARGU_OPTIONAL			_L("optional")

#define STR_TAB_TAB_PREVIEW_CURVE		_L("Preview Curve")
#define STR_TAB_EQUATION				_L("Equation")
#define STR_TAB_SAMPLE_CURVE			_L("Sample Curve")

#define	CHAR_ARGU_SEP				','
enum
{
	SIMULATE_CURVE_PREVIEW_CURVE		= 0x0001,
	SIMULATE_CURVE_EQUATION				= 0x0002,
	SIMULATE_CURVE_SAMPLE_CURVE			= 0x0004,
};

enum
{
	DIRTY_NEED_RELOAD_GRAPH_TEMPLATE = 0x0100,
};

enum
{
	SIMULATE_CURVE_TAB_PREVIEW_CURVE,
	SIMULATE_CURVE_TAB_EQUATION,
	SIMULATE_CURVE_TAB_SAMPLE_CURVE,
	SIMULATE_CURVE_TAB_TOTAL_TABS,
};

//scale x data type
enum {
	SIMULATE_LINEAR,
	SIMULATE_LOG,
	SIMULATE_SRC_GRAPH,
};

enum {
	IDE_PARAMS_BRANCH	= 500,
	IDE_PARAMS_BEGIN	= IDE_PARAMS_BRANCH + 1,
};
//////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////  FunctionFormulaDlg  //////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////



FunctionFormulaDlg::FunctionFormulaDlg(const FunctionInfo& funcInfo, const SetValContext& stContext)
:DynaDlg(IDD_GETN_PREVIEW, NULL, "Odlg8", true)
{
	m_FuncInfo = funcInfo;
	m_Context = stContext;
	m_dwCtrl = 0;
	m_dwCtrl |= SIMULATE_CURVE_PREVIEW_CURVE;
	m_dwCtrl |= SIMULATE_CURVE_EQUATION;
	m_dwCtrl |= SIMULATE_CURVE_SAMPLE_CURVE;
	
	m_dwCtrl |= DIRTY_NEED_RELOAD_GRAPH_TEMPLATE;
}

int FunctionFormulaDlg::DoModalEx(HWND hParent) // = NULL
{
	InitMsgMap();// will be called from internal later
	return DynaDlg::DoModal(hParent);
}

BOOL	FunctionFormulaDlg::GetResult(string& strFormula)
{
	if ( !m_trGUI || !m_trGUI.formula )
		return FALSE;
	strFormula = m_trGUI.formula.strVal;
	return strFormula.IsEmpty() ? FALSE : TRUE;
}

BOOL FunctionFormulaDlg::OnInitDialog()
{
	vector<string>  vstrTipsUpDown;
	vstrTipsUpDown.SetSize(2);
	vstrTipsUpDown[0] = _L("Show Preview Panel (slower)");
	vstrTipsUpDown[1] = _L("Hide Preview Panel (faster)");
	vector<uint> vnBottomTab={IDC_GRAPH_PREVIEW, IDC_GRAPH_CNTRL, IDC_PICTURE_CNTRL, 0};
	DynaDlg::OnInitDialog(IDC_GRID, IDC_SHOW_BOTTOM, IDC_GRAPH_TAB, vstrTipsUpDown, STR_DLG_NAME);

	InitDynaControl(IDC_GRID);
	
	InitGUI();
	
	initDlgControls();
		
	OnReady();
	
	return true;
}

BOOL FunctionFormulaDlg::OnClickOK()
{
	return true;
}

BOOL FunctionFormulaDlg::OnClickCancel()
{
	return true;
}

BOOL FunctionFormulaDlg::OnPreview(Control cntrl)
{
	bool bRet = UpdatePreviewGraphs();
	updatePreviewButton(m_dwCtrl != 0);
	return bRet;
}

BOOL FunctionFormulaDlg::OnAutoPreview(Control cntrl)
{
	Button autoPreview = GetItem(IDC_GETN_AUTO_PREVIEW_BTN);
	int nAutoPreview = autoPreview.Check;
	if ( nAutoPreview == 1 )
	{
		UpdatePreviewGraphs();
		updatePreviewButton(m_dwCtrl != 0);
	}

	return true;
}


void FunctionFormulaDlg::InitPreviewTabs()
{
	m_tabCtrl = GetItem(IDC_GRAPH_TAB);
	m_tabCtrl.InsertItem(SIMULATE_CURVE_TAB_PREVIEW_CURVE, STR_TAB_TAB_PREVIEW_CURVE);
	if( m_FuncInfo.dwCtrl & FI_FIT_FUNCTION )
	{
		m_tabCtrl.InsertItem(SIMULATE_CURVE_TAB_EQUATION, STR_TAB_EQUATION);
		m_tabCtrl.InsertItem(SIMULATE_CURVE_TAB_SAMPLE_CURVE, STR_TAB_SAMPLE_CURVE);
	}
	m_tabCtrl.SetCurSel(0);
	
	//create temp worksheet will invoke selection chagne event, SCV dialog should ignore this message.
	//create GraphPageControl will create temp graph page for preview, it will invoke active frame change event.
	okutil_dialog_event_special_bits_access(DIALOGEVENTSPECIALBITSOPTIONS_SET, DIALOGEVENTSPECIALBITS_SELECTION_CHANGE);
	okutil_dialog_event_special_bits_access(DIALOGEVENTSPECIALBITSOPTIONS_SET, DIALOGEVENTSPECIALBITS_ACTIVE_FRAME_CHANGE);
	
	
	//init controls
	Control ctrlCurCurve = GetItem(getTabGraphControlID(SIMULATE_CURVE_TAB_PREVIEW_CURVE));
	m_CurCurvePreview.Init(ctrlCurCurve);
	
	Control ctrlFormula = GetItem(getTabGraphControlID(SIMULATE_CURVE_TAB_EQUATION));
	m_FormulaPreview.Init(ctrlFormula);
	
	Control ctrlSampleCurve = GetItem(getTabGraphControlID(SIMULATE_CURVE_TAB_SAMPLE_CURVE));
	m_SampleCurvePreview.Init(ctrlSampleCurve);
	
	OnBottomTabChange(m_tabCtrl);
}

void FunctionFormulaDlg::initDlgControls()
{
	// hide the unneeded controls
	vector<uint> vnIDCtrlToHide = {IDC_PARAMS_DESCRIPTION, IDC_ERR_MESSAGE_BOX, IDC_APPLY, IDC_GETN_CUSTOM_BTN1, IDC_GETN_CUSTOM_BTN2, IDC_GETN_CUSTOM_BTN3, IDC_GETN_CUSTOM_BTN4};
	for(int ii = vnIDCtrlToHide.GetSize()-1; ii>=0; ii--)
	{
		Control ctrl = GetItem(vnIDCtrlToHide[ii]);
		if(ctrl)
			ctrl.Visible = false;
	}

	string strCurrentTitle;
	strCurrentTitle.Format("%s - %s", STR_DLG_NAME, m_FuncInfo.strFuncName);
	SetDialogTitle(strCurrentTitle);

	InitDynaControl(IDC_GRID);

	InitPreviewTabs();
	
	CheckEnablePreview();
}

bool FunctionFormulaDlg::InitGUI()
{
	m_trGUI = InitTree();
	
	int iPara, nParams = m_FuncInfo.vsArguNames.GetSize();
	int nParamID = IDE_PARAMS_BEGIN;
	GETN_USE(m_trGUI)
	GETN_STR(description, STR_DESCRIPTION, m_FuncInfo.strDescription) GETN_OPTION_COLOR_READONLY(RGB(0, 0, 0)) GETN_READ_ONLY_EX(2)
	GETN_BEGIN_BRANCH(parameters, STR_PARAMETERS) GETN_ID_BRANCH(IDE_PARAMS_BRANCH) GETN_OPTION_BRANCH(GETNBRANCH_OPEN )
		int nDefArgBeginPos = m_FuncInfo.vsArguNames.GetSize() - m_FuncInfo.nNumDefArgs;
		ASSERT(nDefArgBeginPos >= 0);
		for ( iPara = 0; iPara < nParams; iPara++ )
		{
#ifdef		_SUPPORT_INTERACTIVE_CONTROL_
			string strLabel = m_FuncInfo.vsArguNames[iPara];
			if( iPara >= nDefArgBeginPos)		// optional argu
				strLabel = strLabel + " (" + STR_ARGU_OPTIONAL + ")";
			GETN_INTERACTIVE(Param, strLabel, "") GETN_ID(nParamID++)
			
			int nInteractiveOptions = ICOPT_SUPPORT_LABEL_AREA | ICOPT_NO_DROP_DOWN_BUTTON;
			GETN_CURRENT_SUBNODE.SetAttribute(STR_INTERACTIVE_CONTROL_OPTIONS_ATTRIB, nInteractiveOptions);
#else
			switch(m_FuncInfo.vnArguTypes[iPara])
			{
			case LTVAR_TYPE_VAR_INT:
				GETN_NUM(Param, m_FuncInfo.vsArguNames[iPara], 0) GETN_OPTION_NUM_FORMAT("%d") GETN_ID(nParamID++)
				break;
				
			case LTVAR_TYPE_VAR_DOUBLE:
				GETN_NUM(Param, m_FuncInfo.vsArguNames[iPara], 0) GETN_OPTION_NUM_FORMAT("%.2f") GETN_ID(nParamID++)
				break;
				
			case LTVAR_TYPE_VAR_STR:
				GETN_STR(Param, m_FuncInfo.vsArguNames[iPara], "") GETN_ID(nParamID++)
				break;
				
			default:
				ASSERT(false);
				GETN_STR(Param, m_FuncInfo.vsArguNames[iPara], "") GETN_ID(nParamID++)
				break;
			}
#endif		//_SUPPORT_INTERACTIVE_CONTROL_
		}
	GETN_END_BRANCH(parameters)
		
	GETN_SEPARATOR_LINE
	
	GETN_STR(formula, STR_FORMULA_PREVIEW, "") GETN_READ_ONLY_EX(2)
	
	return true;
}

BOOL FunctionFormulaDlg::OnReady()
{	
	UpdateExpression(); //update formula on dialog open
	
	UpdateDynaControl(true, GETNEVENT_ON_INIT);
	
	SetInitReady();

	return true;
}

BOOL FunctionFormulaDlg::OnDestroy()
{
	//remove durative bits
	okutil_dialog_event_special_bits_access(DIALOGEVENTSPECIALBITSOPTIONS_REMOVEALL);
	//set temporary bits
	okutil_dialog_event_special_bits_access(DIALOGEVENTSPECIALBITSOPTIONS_SET, DIALOGEVENTSPECIALBITS_FRAME_CHANGE_TEMPORARY);//because preview graph destroy will invoke this layer changed event
	return DynaDlg::OnDestroy();
}

BOOL FunctionFormulaDlg::OnDlgResize(int nType, int cx, int cy)
{
	if(!IsInitReady())
		return TRUE;

	MoveControlsHelper	_temp(this);		
	_temp.Exclude(IDC_GRID);

	vector<uint> vnBtns = {IDCANCEL, IDOK, IDC_GETN_PREVIEW_BTN, IDC_GETN_AUTO_PREVIEW_BTN, 0};

	MultiPaneDlg::OnDlgResize(vnBtns, cx, cy, false);

	resizeTopPaneControl();
	resizeBottomPaneControl();

	return TRUE;
}

void FunctionFormulaDlg::SetDialogTitle(LPCSTR lpcszTitle)
{
	Window wDlg = DynaDlg::GetWindow();//GetWindow();
	wDlg.Text = lpcszTitle;
}

void FunctionFormulaDlg::resizeTopPaneControl()
{	
	//return;
	Control ctrlGrid = GetItem(IDC_GRID);
	RECT rr;
	GetClientRect(ctrlGrid, rr);
	
	int nEdge=GetControlGap(), nx=nEdge, ny=nEdge, cx=rr.right, cy=rr.bottom;
	//ny = ResizeThemeControls(nEdge, cx, ny);

	int		nErrMessageHeight = GetDlgStatusMsgBoxHeight(RECT_WIDTH(rr));
	rr.top = ny + nEdge;

	RECT rrOK, rrDlg;
	m_wndDlg.GetClientRect(&rrDlg);
	Control btnOK = GetItem(IDCANCEL);//IDOK);
	GetClientRect(btnOK, rrOK);	
	rr.bottom = rrDlg.bottom - RECT_HEIGHT(rrOK) - 2 * nEdge;

	if(nErrMessageHeight>0)
	{
		RECT rr2;
		rr2 = rr;
		rr.bottom = rr.bottom - nErrMessageHeight - nEdge;
		ctrlGrid.MoveWindow(&rr);
		rr2.top = rr.bottom + nEdge;
		m_btnErrMessage.MoveWindow(&rr2);
		m_btnErrMessage.Visible = true;
	}
	else
	{
		ctrlGrid.MoveWindow(&rr);
		m_btnErrMessage.Visible = false;
	}
}

void FunctionFormulaDlg::resizeBottomPaneControl()
{
	if(!m_tabCtrl)
		return;
	if( IsBottomPaneShown() )
	{

		RECT rTab;
		GetClientRect(m_tabCtrl, rTab);
		m_tabCtrl.AdjustRect(FALSE, &rTab);
		for(int nTab = 0; nTab < SIMULATE_CURVE_TAB_TOTAL_TABS; nTab++)
		{
			Control ctrl = GetItem( getTabGraphControlID(nTab) );
			if(ctrl)
				ctrl.MoveWindow(&rTab);
		}

		Control ctrlShown = GetItem( getTabGraphControlID(m_tabCtrl.GetCurSel()) );
		if(ctrlShown)
			ctrlShown.Visible = true;
	}
	else
	{
		for(int nTab = 0; nTab < SIMULATE_CURVE_TAB_TOTAL_TABS; nTab++)
		{
			Control ctrl = GetItem( getTabGraphControlID(nTab) );
			if(ctrl)
				ctrl.Visible = false;
		}
		
	}
}

BOOL FunctionFormulaDlg::OnRestoreSize(DWORD dwSizeInfo)
{
	void * p = (void*)dwSizeInfo;
	DLGSIZEINFO *pSz = (DLGSIZEINFO*)p;
	lstrcpyn(pSz->szDialogName, STR_DLG_NAME, MAXLINE);
	pSz->top = -1;
	pSz->left = -1;
	pSz->width = 800; //hard code, just for only one function
	pSz->height = 400;

	if(0 == HasPreviousSize(STR_DLG_NAME))
	{
		SetBottomPaneHeight(300);
	}
	return TRUE;
}

BOOL	FunctionFormulaDlg::OnInitSize(int &left, int &top, int &right, int  &bottom)
{
	return FALSE;
}

BOOL	FunctionFormulaDlg::OnSelectionChange()
{
	okutil_dialog_event_special_bits_access(DIALOGEVENTSPECIALBITSOPTIONS_SET, DIALOGEVENTSPECIALBITS_SELECTION_CHANGE);
	return TRUE;
}

BOOL	FunctionFormulaDlg::OnIdle()
{
	return TRUE;
}

BOOL	FunctionFormulaDlg::OnActiveLayerChange()
{
	okutil_dialog_event_special_bits_access(DIALOGEVENTSPECIALBITSOPTIONS_SET, DIALOGEVENTSPECIALBITS_ACTIVE_FRAME_CHANGE);
	return TRUE;
}

BOOL	FunctionFormulaDlg::OnActivePageChange()
{
	okutil_dialog_event_special_bits_access(DIALOGEVENTSPECIALBITSOPTIONS_SET, DIALOGEVENTSPECIALBITS_ACTIVE_FRAME_CHANGE);
	return TRUE;
}

int	FunctionFormulaDlg::GetTotalHeight(bool bMin)
{
	return 350;
}

int	FunctionFormulaDlg::GetTotalWidth(bool bMin)
{
	if( bMin )
	{
		int nWidth;
		if( IsBottomPaneShown() )
		{
			nWidth = GetExactTopPaneHeight() + 300;					// min bottom width is 300
		}
		else
			nWidth = 460;
		return nWidth;
	}
	else
		return MultiPaneDlg::GetTotalWidth(bMin);
}

void	FunctionFormulaDlg::updatePreviewButton(BOOL bEnable)
{
	Button btnPreview = GetItem(IDC_GETN_PREVIEW_BTN);
	if(btnPreview)
		btnPreview.Enable = bEnable;
}

uint		FunctionFormulaDlg::getTabGraphControlID(int nTab)
{
	switch(nTab)
	{
	case SIMULATE_CURVE_TAB_PREVIEW_CURVE:
		return IDC_GRAPH_PREVIEW;

	case SIMULATE_CURVE_TAB_EQUATION:
		return IDC_GRAPH_CNTRL;

	case SIMULATE_CURVE_TAB_SAMPLE_CURVE:
		return IDC_PICTURE_CNTRL;
		
	}
	ASSERT(false);
	return 0;
}

BOOL FunctionFormulaDlg::OnShowBottomPane(Control ctrl)
{
	bool bRet = DynaDlg::OnShowBottomPane(ctrl);
	resizeBottomPaneControl();
	return bRet;
}

BOOL FunctionFormulaDlg::OnBottomTabChange(Control ctrl)
{
	int nCurSel = m_tabCtrl.GetCurSel();
	for(int nTab = 0; nTab < SIMULATE_CURVE_TAB_TOTAL_TABS; nTab++)
	{
		Control ctrl = GetItem( getTabGraphControlID(nTab) );
		if(ctrl)
			ctrl.Visible = nTab==nCurSel;
	}
	return true;
}

void FunctionFormulaDlg::OnAfterValueChange(int nRow, int nCol)
{
	Button autoPreview = GetItem(IDC_GETN_AUTO_PREVIEW_BTN);
	bool bEnablePreview = autoPreview.Enable;
	int nAutoPreview = autoPreview.Check;
	if ( bEnablePreview )
	{
	if ( nAutoPreview == 0 )
		{
		m_dwCtrl |= SIMULATE_CURVE_SAMPLE_CURVE; //set dirty bit
			updatePreviewButton(true);
		}
	else //auto update
	{
		UpdatePreviewCurve();
	}
	}
	
	UpdateExpression();
	
	return;
}

bool FunctionFormulaDlg::getInputRange(XYRange& dr)
{
	TreeNode trInputData= GetTree().iy;
	if(!okxf_resolve_tree_construct_range(&trInputData, &dr))
	{
		return false;
	}	
	return true;
}

bool FunctionFormulaDlg::SetInput()
{
	XYRange dr;
	getInputRange(dr);
	
	return false;
}

bool  FunctionFormulaDlg::UpdatePreviewGraphs()
{
	string strFunctionName = m_FuncInfo.strFuncName;
	if ( m_dwCtrl & SIMULATE_CURVE_PREVIEW_CURVE )
	{
		m_SampleCurvePreview.LoadFunctionPreview(strFunctionName, false);
	}
	
	if ( m_dwCtrl & SIMULATE_CURVE_EQUATION )
	{
		m_FormulaPreview.LoadFunctionPreview(strFunctionName, true);
	}
	
	if ( m_dwCtrl & DIRTY_NEED_RELOAD_GRAPH_TEMPLATE )
	{
		//need to reload template
		m_CurCurvePreview.UpdateGraphTemplate("Origin");
		//prepare temp data range for preview
		m_CurCurvePreview.AddPlot(0, 1, IDM_PLOT_LINE);
		
	}
	if ( m_dwCtrl & SIMULATE_CURVE_SAMPLE_CURVE )
	{
		UpdatePreviewCurve();
	}
	
	m_dwCtrl = 0; //reset;
	
	return true;
}


bool	FunctionFormulaDlg::UpdateExpression()
{
	if ( !m_trGUI )
		return false;
	
	int iPara = 0;
	vector<string> vsArguVals(m_FuncInfo.vsArguNames.GetSize());
	TreeNode trParams = m_trGUI.parameters;
	foreach(TreeNode trPara in trParams.Children)
	{
		string strVal = "";
#ifdef	_SUPPORT_INTERACTIVE_CONTROL_
		strVal = trPara.strVal;
		UpdateParameter(strVal, strVal);
#else
		switch(m_FuncInfo.vnArguTypes[iPara])
		{
		case LTVAR_TYPE_VAR_INT:
			strVal = trPara.nVal;
			break;
			
		case LTVAR_TYPE_VAR_DOUBLE:
			strVal = ftoa(trPara.dVal, "%.2lf");
			break;
			
		case LTVAR_TYPE_VAR_STR:
			strVal = trPara.strVal;
			break;
			
		default:
			strVal = trPara.strVal;
			break;
		}
#endif	//_SUPPORT_INTERACTIVE_CONTROL_
		vsArguVals[iPara++] = strVal;
	}
	ASSERT(vsArguVals.GetSize() == m_FuncInfo.vsArguNames.GetSize());
	if( m_FuncInfo.nNumDefArgs > 0 )			// remove default args
	{
		ASSERT( m_FuncInfo.nNumDefArgs <= m_FuncInfo.vsArguNames.GetSize() );
		int nNumDefArgs = m_FuncInfo.nNumDefArgs;
		for( int ii = vsArguVals.GetSize() - 1; nNumDefArgs && ii >= 0 && vsArguVals[ii].IsEmpty(); ii--, nNumDefArgs--)
			vsArguVals.RemoveAt(ii);
	}

	string strFormula;
	strFormula.SetTokens(vsArguVals, CHAR_ARGU_SEP);
	strFormula = m_FuncInfo.strFuncName + "(" + strFormula + ")";
	if ( FI_FIT_FUNCTION & m_FuncInfo.dwCtrl )
		strFormula = STR_FIT_FUNC_PREFIX + strFormula;
	if( m_FuncInfo.nReturnType == LTVAR_TYPE_VAR_STR)
		strFormula += '$';

	m_trGUI.formula.strVal = strFormula;
	return true;
}

typedef bool(*PFN_GET_FDF_TREE_FROM_FUNC_NAME)(TreeNode &trFDF, LPCSTR lpcszFuncName, string& strCategory, DWORD dwCtrl = 0, bool bCustomized = true); 
typedef int(*PFN_GET_INDEP_VARIABLES)(LPCSTR lpcszFuncName, vector<string>* pvsIndepVarNames = NULL);
bool	FunctionFormulaDlg::UpdatePreviewCurve()
{
	/*
	if ( FI_FIT_FUNCTION & m_FuncInfo.dwCtrl )
	{
		string strFuncName = m_FuncInfo.strFuncName;
		string strCategory;
		Tree trFDF;
		
		PFN_GET_FDF_TREE_FROM_FUNC_NAME pfn_get_FDF_tree_from_func_name = Project.FindFunction("nlsf_get_FDF_tree_from_func_name", "Originlab\\nlsf_utils.c");
		if ( pfn_get_FDF_tree_from_func_name && !pfn_get_FDF_tree_from_func_name(trFDF, strFuncName, strCategory) )
			return false;
		
		NumericFunction nf;
		nf.SetTree(trFDF);
		
		//update m_vX, m_vY, and update data in temporary worksheet
		int nXMin = 1, nXMax = 32, nXInc = 1;
		vector vParams(0);
		TreeNode trParams = m_trGUI.parameters;
		if ( !trParams )
			return false;
	
		string strParamLabel;
		vector<string> vsIndepNames;
		PFN_GET_INDEP_VARIABLES pfn_get_independent_variables = Project.FindFunction("nlf_get_independent_variables", "Originlab\\nlsf_utils.c");
		if ( pfn_get_independent_variables != NULL )
			pfn_get_independent_variables(strFuncName, &vsIndepNames);
		foreach(TreeNode trPara in trParams.Children)
		{
			trPara.GetAttribute(STR_LABEL_ATTRIB, strParamLabel);
			if ( vsIndepNames.Find(strParamLabel) >= 0 ) //skip independent variables
				continue;
			
			double dVal;
	#ifdef	_SUPPORT_INTERACTIVE_CONTROL_
			EvaluateParameter(trPara.strVal, dVal);
	#else
			dVal = trPara.dVal;
	#endif	//_SUPPORT_INTERACTIVE_CONTROL_
			vParams.Add(dVal);
		}
		
		m_vX.Data(nXMin, nXMax, nXInc);
		m_vY = nf.Evaluate(m_vX, vParams);
	}
	//update data into worksheet
	m_CurCurvePreview.SetData(m_vX, 0);
	m_CurCurvePreview.SetData(m_vY, 1);
	*/
	string strFuncName = m_FuncInfo.strFuncName;

	TreeNode trParams = m_trGUI.parameters;
	if ( !trParams )
		return false;

	vector vX, vParams;
	int nParamPos = 0,
		nDefArgBegPos = m_FuncInfo.vsArguNames.GetSize() - m_FuncInfo.nNumDefArgs;

	foreach(TreeNode trPara in trParams.Children)
	{
		if( nParamPos == 0)			// get as independent variable
		{
			if( !EvaluateParameter(trPara.strVal, vX) )
				return false;
		}
		else
		{
			double dVal;
			if( EvaluateParameter(trPara.strVal, dVal) )
			{
				
				vParams.Add(dVal);
			}
			else
			{
				if(nParamPos < nDefArgBegPos)		// 
					return false;
				else								// default arg
					break;
			}
		}
		nParamPos++;
	}
	if( nParamPos == 0 )
	{
		ASSERT(false);
		return false;
	}

	// 
	Worksheet wksTmp = m_CurCurvePreview.GetWorksheet();
	if( !wksTmp )
		return false;
	ASSERT(wksTmp.GetNumCols() >= 2);
	Dataset ds(wksTmp, 0);
	ds = vX;

	Column col(wksTmp, 1);
	if(!col)
		return false;

	// temp formula
	string strFormula;
	strFormula = strFuncName + "(col(A)";
	for(int nParam = 0; nParam < vParams.GetSize(); nParam++)
		strFormula += "," + ftoa(vParams[nParam]);
	strFormula += ")";
	if ( FI_FIT_FUNCTION & m_FuncInfo.dwCtrl )
		strFormula = STR_FIT_FUNC_PREFIX + strFormula;

	if( !col.SetFormula(strFormula) )
		return false;
	col.ExecuteFormula(0, vX.GetSize()-1, false);

	//rescale the graph.
	m_CurCurvePreview.PlotRescale();

	return true;
}

bool	FunctionFormulaDlg::EvaluateParameter(LPCSTR lpcszParamValue, vector& vVals)
{
	vVals.SetSize(0);

	bool bRet = false;

	double dResult = atof(lpcszParamValue);
	if ( !is_missing_value(dResult) ) //numeric value typed in control directly
	{
		vVals.Add(dResult);
		bRet = true;
	}
	else //need to parse range string and get cell value
	{
		DataRange dr;
		if ( okxf_init_range_from_string(&dr, lpcszParamValue) && dr.GetNumData() > 0 )
		{
			dr.GetData(vVals, 0);
			if ( vVals.GetSize() > 0 )
				bRet = true;
		}
	}
	return bRet;
}

bool	FunctionFormulaDlg::EvaluateParameter(LPCSTR lpcszParamValue, double& dVal)
{
	double dResult = atof(lpcszParamValue);
	if ( !is_missing_value(dResult) ) //numeric value typed in control directly
		dVal = dResult;
	else //need to parse range string and get cell value
	{
		DataRange dr;
		if ( okxf_init_range_from_string(&dr, lpcszParamValue) && dr.GetNumData() > 0 )
		{
			vector vv;
			dr.GetData(vv, 0);
			if ( vv.GetSize() == 0 )
				return false;
			dVal = vv[0];
		}
	}
	return true;
}

void	FunctionFormulaDlg::UpdateParameter(LPCSTR lpcszParam, string& strNewParamVal)
{
	strNewParamVal = lpcszParam;
	if ( is_missing_value(atof(lpcszParam)) ) //lpcszParam is range string, need to be updated.
	{
		string strNewRangeStr = lpcszParam;
		string strBook, strSheet, strObj;
		string strC1, strR1, strC2, strR2;
		if ( okutil_parse_complete_range_string(lpcszParam, &strBook, &strSheet, &strObj) && okutil_parse_range_string(strObj, &strC1, &strR1, &strC2, &strR2) )
		{
			string strWksName = okutil_make_book_sheet_string(strBook, strSheet);
			Worksheet wks(strWksName);
			if ( wks )
			{
				Column colObj = wks.Columns(strC1);
				colObj.GetRangeString(strObj, NTYPE_GOOD_LN_OR_SN | NTYPE_NO_BOOKSHEET);
				if ( !strR1.IsEmpty() )
					strObj.Format("%s[%s]", strObj, strR1);
				Column colSel;
				colSel = (Column)Project.GetObject((uint)m_Context.nObjUID);
				if ( colSel )
				{
					Worksheet ly;
					colSel.GetParent(ly);
					if ( is_same_layer(wks, ly) ) //same sheet
					{
						strBook = strSheet = "";
					}
					else 
					{
						Page pg;
						ly.GetParent(pg);
						if ( strBook.Compare(pg.GetName()) == 0 )// same book, different sheet
						{
							strBook = "";
						}

					}
					okutil_create_complete_range_string_obj(&strNewRangeStr, strBook, strSheet, strObj);
				}
			}
		}
		strNewParamVal = strNewRangeStr;
	}
}

void	FunctionFormulaDlg::CheckEnablePreview()
{
	Button autoPreview = GetItem(IDC_GETN_AUTO_PREVIEW_BTN);
	Button btnPreview = GetItem(IDC_GETN_PREVIEW_BTN);
	vector<int> vnArguTypes;
	vnArguTypes = m_FuncInfo.vnArguTypes;
	if ( m_FuncInfo.nReturnType == LTVAR_TYPE_VAR_STR || find_in_list(LTVAR_TYPE_VAR_STR, vnArguTypes, false) >= 0 )
	{
		autoPreview.Enable = false;
		btnPreview.Enable = false;
	}
	else
	{
		autoPreview.Enable = true;
		btnPreview.Enable = true;
	}
}


BOOL	InsertFunctionFormula(string& strFormula, const FunctionInfo& fi, const SetValContext& stContext, HWND hParent = NULL)
{
	InsertFormulaHelper IFHelper(stContext);
	FunctionFormulaDlg myDlg(fi, stContext);
	int nRet = myDlg.DoModalEx(hParent);
	if ( nRet == IDOK )
	{
		myDlg.GetResult(strFormula);
		return TRUE;
	}
	return FALSE;
}



void test_simulatecurve_dlg()
{
	FunctionInfo fi;
	string strFunction = "Gauss";
	string strDescription = "Fit data with gauss function";
	vector<string> vsArguNames = {"y0", "xc", "w", "A"}
	vector<uint>  vnArguTypes = {4, 6, 4, 4};
	
	fi.strFuncName = "Gauss";
	fi.vsArguNames = vsArguNames;
	fi.vnArguTypes = vnArguTypes;
	fi.strDescription = strDescription;
	fi.dwCtrl = 0;
	
	string strFormula;
	InsertFunctionFormula(strFormula, fi, NULL);
	return;
	
}
