#include <origin.h>
#include <GetNBox.h>
#include <xfutils.h>
enum{
	method_linear,
	method_spline,
	method_bspline,
};
#include <GetNBox.h> 
#include <event_utils.h>
#include <..\originlab\XFunctionEx.h>
#include <wks2mat.h>
#include <o8dlg.h>
static bool s_bInit = false;
static string s_strErrMsg = "";
/***************************************************************************************
/// Modification Log :
/// 4/27/2010 Gareth Create the XFunction.
///	8/16/2010 Gareth Rewrite the whole XFunction.
***************************************************************************************/

//put your own support static functions here
//#define Y_INDEX_IN_XYZRange 1

#define MIN_BSPLINE_PTS 4
#define MIN_SPLINE_PTS 2
#define MIN_LINEAR_PTS 2
#define MIN_OUTPUT_INTERPOLATION_POINTS 2
#define MIN_SMOOTHING_FACTOR 0
#define STR_ERR_THROW1 _L("Minimum interpolation points")
#define STR_ERR_THROW2 _L("Smoothing Factor")

static string _get_interp_method_name(int nMethod)
{
	string strInterpName;
	switch(nMethod)
	{
	case INTERP_TYPE_LINEAR:
		strInterpName = _L("Linear");
		break;
	case INTERP_TYPE_SPLINE:
		strInterpName = _L("Spline");
		break;
	case INTERP_TYPE_BSPLINE:
		strInterpName = _L("B-Spline");
		break;
	default:
	}	
	return strInterpName;
}

static int _get_min_pts(int nMethod)
{
	int nMinPts;
	switch(nMethod)
	{
	case INTERP_TYPE_LINEAR:
		nMinPts = MIN_LINEAR_PTS;
		break;
	case INTERP_TYPE_SPLINE:
		nMinPts = MIN_SPLINE_PTS;
		break;
	case INTERP_TYPE_BSPLINE:
		nMinPts = MIN_BSPLINE_PTS;
		break;
	default:
	}	
	return nMinPts;
}

static int _check_pts_enough(const TreeNode& trGetN, int nSize)
{
	int nMinPts = _get_min_pts(trGetN.method.nVal);

	if(nSize < nMinPts)
		return nMinPts;

	return OE_NOERROR;
}



static bool _interpolate_by_parameter(vector& vxIn, vector& vyIn, vector& vzIn, vector& vxOut, vector& vyOut, vector& vzOut, int nMethod)
{
	double dPrecision = 1E-8;
	//xyz_remove_duplicates(vxIn.GetSize(), vxIn, vyIn, vzIn, dPrecision);///Gareth 8/20/2010 ORG-57 DEL_XYZ_REMOVE_DUPLICATES
	
	int nSize = vxIn.GetSize();
	if(nSize < _get_min_pts(nMethod))
	{
		XF_THROW(XFERR_UNDUPLICATED_DATA_TOO_FEW);
		return false;
	}
	matrix mIn(3, nSize);
	mIn.SetRow(vxIn, 0);
	mIn.SetRow(vyIn, 1);
	mIn.SetRow(vzIn, 2);
	
	//get curve's cumulate curve length
	vector vCumLen(nSize); 
	//ocmath_cumulative_curve_length(nSize, vCumLen, vxIn, vyIn); 
	ocmath_cumulative_curve_length_3d(nSize, vCumLen, vxIn, vyIn, vzIn);/// Gareth 8/12/2010 ORG-57 CUMULATIVE_CURVE_LENGTH_3D
	
	double dCurveLen = vCumLen[nSize - 1];
	
	//generate data for interp
	int nOutSize = vzOut.GetSize();
	vector vEvalCumLen;
	vEvalCumLen.Data(0, dCurveLen, (dCurveLen-0)/(nOutSize-1.0));
	
	
	matrix mOut(3, nOutSize);
	if(OE_NOERROR != ocmath_parametric_interpolate_eval(vEvalCumLen, mOut, nOutSize, vCumLen, mIn, nSize, 3, nMethod, 0))
	{
		XF_THROW(XFERR_BSPLINE_INTERP_FAILED);
		return false;
	}
	mOut.GetRow(vxOut, 0);
	mOut.GetRow(vyOut, 1);
	mOut.GetRow(vzOut, 2);
	return true;
}

static int _check_err_condition(TreeNode& trGetN, int nSize)
{
	int nErr = CER_NO_ERROR;
	
	XYZRange xyz;
	TreeNode trIz = trGetN.iz;
	okxf_resolve_tree_construct_range(&trIz, &xyz);

	int nn = xyz.GetNumData(DRR_GET_DEPENDENT);
	if(xyz.GetNumData(DRR_GET_DEPENDENT) != 1)
		nErr = CER_NOT_ONE_XYZRANGE;
	
	else if(0>= get_input_xyzrange_data_size(xyz))
		nErr = CER_NO_DATA;
	
	else if(_check_pts_enough(trGetN, nSize) != OE_NOERROR)
		nErr = CER_DATA_TOO_FEW;
	
	else if(trGetN.npts.dVal < MIN_OUTPUT_INTERPOLATION_POINTS)
		nErr = CER_INPUT_ARGUMENT_LESS_THAN_SPECIFIED_VALUE;
	
	return nErr;
}

static bool _get_err_condition(TreeNode trGetN, int nSize, string& strErrMsg)
{
	strErrMsg = "";
	int nErr = CER_NO_ERROR;
	nErr = _check_err_condition(trGetN, nSize);
	if(nErr != CER_NO_ERROR)
	{
		
		strErrMsg = nErr;
		
		if(trGetN.npts.dVal < MIN_OUTPUT_INTERPOLATION_POINTS)
		{
			strErrMsg += ":";
			strErrMsg += _get_interp_method_name(trGetN.method.nVal) + " " + STR_ERR_THROW1;
			strErrMsg += "|";
			strErrMsg += MIN_OUTPUT_INTERPOLATION_POINTS;
		}
		return false;
	}
	
	return true;
}

static	void	_set_output_range_comments(XYZRange &oz, const XYZRange& iz, int nMethod)
{
	vector<string>		vsMethods = {"Linear", "Spline", "B-Spline"};
	string	strComments;
	strComments.Format(_L("%s trace interp of %s"), vsMethods[nMethod], get_input_data_range_string(iz));
	oz.SetOutputComment(strComments);
}

static int get_input_xyzrange_data_size(XYZRange xyzRng)
{
	if( xyzRng.IsValid() )
	{
		vector vx, vy, vz;
		if(xyzRng.GetData(vz, vy, vx,NULL, 0, 0))
			return vz.GetSize();
	}
	return -1;
}

static bool _handle_evenSampling(const XYZRange& iz, vector& vxIn, vector& vyIn, vector& vzIn)
{
	
	if(!iz.IsValid())
		return false;
	
	int r1, r2, c1, c2;
	string strRange;
	bool bRes;
	double d = 2;
	d = d/3;
	d = d*3;
	Worksheet wks;
	bRes = iz.GetRange(0, r1, c1, r2, c2, wks, &strRange);
	if(bRes)
	{
		Column col(wks, c1);
		double dX0, dXInc;
	    string strXUnits, strXLongName;
	    if(col.IsEvenSampling(&dX0, &dXInc, strXUnits, strXLongName))
	    {
	    	vxIn.Data(dX0, dX0+(vzIn.GetSize()-1)*dXInc, dXInc);

	    }
	}
	 
	bRes = iz.GetRange(1, r1, c1, r2, c2, wks, &strRange);
	if(bRes)
	{
		Column col(wks, c1);
		double dX0, dXInc;
	    string strXUnits, strXLongName;
	    if(col.IsEvenSampling(&dX0, &dXInc, strXUnits, strXLongName))
	    {
	    	vyIn.Data(dX0, dX0+(vzIn.GetSize()-1)*dXInc, dXInc);
	    }
	}
	    
	return true;
}


void interpxyz(const XYZRange& iz, const int& method, const int& npts, XYZRange& oz)
{
	vector vxIn, vyIn, vzIn, vxOut, vyOut, vzOut;
	
	if(!iz.IsValid() || iz.GetNumData(DRR_GET_DEPENDENT) != 1 || !iz.GetData(vzIn, vyIn, vxIn))
		XF_THROW(CER_NOT_ONE_XYZRANGE)
		
	_handle_evenSampling(iz, vxIn, vyIn, vzIn);
	
	if(0>= vzIn.GetSize())
		XF_THROW(CER_NO_DATA);
		
	if (npts < MIN_OUTPUT_INTERPOLATION_POINTS)
		XF_THROW_EX2(CER_INPUT_ARGUMENT_LESS_THAN_SPECIFIED_VALUE, STR_ERR_THROW1, MIN_OUTPUT_INTERPOLATION_POINTS);
	
	vzOut.SetSize(npts);
	
	if(!_interpolate_by_parameter(vxIn, vyIn, vzIn, vxOut, vyOut, vzOut, method))
		return;
	
	oz.SetData(&vzOut, &vyOut, &vxOut);
	_set_output_range_comments(oz, iz, method);
	
	
}

//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 interpxyz_event1(TreeNode& trGetN, int nRow, int nEventID, DWORD& dwEnables, LPCSTR lpcszNodeName, WndContainer& DynaCntrlContainer, string& strAux, string& strErrMsg)
{
	if( xf_is_extended_event(nEventID) )
		return interpxyz_event1_ex(trGetN, nRow, nEventID, dwEnables, lpcszNodeName, DynaCntrlContainer, strAux, strErrMsg);
	DECLARE_BUTTON_ENABLES
	XYZRange xyz;
	TreeNode trIz = trGetN.iz;
	TreeNode trNpts = trGetN.npts;
	okxf_resolve_tree_construct_range(&trIz, &xyz);
	int	nSize = get_input_xyzrange_data_size(xyz);
	bOKEnable = _get_err_condition(trGetN, nSize, strErrMsg);
	if ( octree_get_auto_support(&trNpts) == 1 )
	{
		
		int	nSize = get_input_xyzrange_data_size(xyz);
		if ( nSize > 0 )
			trNpts.nVal = min(nSize * 5, 1000);
	}
	return false;

}

int interpxyz_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 interpxyz_before_execute(TreeNode& trGetN, int nGetNDialog, int& nRet, int dwCntrl)
{
	
	TreeNode trNpts = trGetN.npts;
	XYZRange xyz;
	TreeNode trIz = trGetN.iz;
	okxf_resolve_tree_construct_range(&trIz, &xyz);
	if ( octree_get_auto_support(&trNpts) == 1 )
	{
		
		int	nSize = get_input_xyzrange_data_size(xyz);
		if ( nSize > 0 )
			trNpts.nVal = min(nSize * 5, 1000);
	}
	
	
	if((dwCntrl & LTXF_FROM_GUI_MENU) && (dwCntrl & LTXF_THEME_USED) && 0 == nGetNDialog)
	{
		int nSize = get_input_xyzrange_data_size(xyz);
		if(_check_err_condition(trGetN, nSize) != CER_NO_ERROR)
			nRet = XFEVT_PROCEED_WITH_DLG;	
	}
	
}
//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 interpxyz_event1_ex(TreeNode& trGetN, int nRow, int nEventID, DWORD& dwEnables, LPCSTR lpcszNodeName, WndContainer& DynaCntrlContainer, string& strAux, string& strErrMsg)
{
	return false;
}
