#include <origin.h>
#include <GetNBox.h>
#include <xfutils.h>
#include <GetNBox.h> 
#include <control.h>
#include <ReportTree.h>
#include <event_utils.h>
#include <wks2mat.h>
#include <o8dlg.h>

/***************************************************************************************
/// Modification Log :
/// Folger 07/23/08 BETTER_OPREATION_OUTPUT_COLUMN_COMMENTS
/// Echo 07/28/08 v8.0911 PTS_LIMITATION_FOR_RD
***************************************************************************************/

//put additional include files here

//put your own support static functions here
#define	INTERP3_REPORT_TABLE 	0x01110010
#define IDST_INTERP3_X 	0x01120010	
#define IDST_INTERP3_Y	0x01120020	
#define IDST_INTERP3_Z	0x01120030	
#define IDST_INTERP3_F	0x01120040	

#define	INTERP3_MIN_PTS	10
#define INTERP3_MAX_PTS_PER_DIM	100
//enum
//{
	//ERR_NO_DATA_SELEC = XF_LOCAL_ERR_BEGIN,
	//ERR_MIN_MAX,
	//ERR_DIFF_SIZE,
	//ERR_TOO_FEW_PTS,
	//ERR_OUT_OF_RANGE,
	//
//};
/*
static void set_wks_format(Worksheet wks, int npts)
{
	wks.SetName(_L("3D Interpolate"), OCD_ENUM_NEXT);	
	wks.SetSize(npts, 4);
	
	wks.Columns(0).SetType(OKDATAOBJ_DESIGNATION_X);
	wks.Columns(1).SetType(OKDATAOBJ_DESIGNATION_Y);
	wks.Columns(2).SetType(OKDATAOBJ_DESIGNATION_Z);
	wks.Columns(3).SetType(OKDATAOBJ_DESIGNATION_Y);
	
	
}
*/
static bool _init_tree_show(TreeNode& trGetN, const Range& rData)
{
	vector vX, vY, vZ, vF;
	_get_data(rData, vX, vY, vZ, vF);
	if ( 0 != vX.GetSize() )
	{
		get_min_max(vX, trGetN.xmin, trGetN.xmax);
	}
	if (0 != vY.GetSize())
	{
		get_min_max(vY, trGetN.ymin, trGetN.ymax);
	}
	if (0 != vZ.GetSize())
	{
		get_min_max(vZ, trGetN.zmin, trGetN.zmax);
	}
	
	return true;
}

static int _trim_missing(vector& vX, vector& vY, vector& vZ, vector& vF)
{
	int nSize = min(vF.GetSize(), vX.GetSize());
	if (0 == nSize)
		return 0;
	
	matrix mTemp;
	mTemp.SetSize(nSize, 4);
	mTemp.SetColumn(vX, 0);
	mTemp.SetColumn(vY, 1);
	mTemp.SetColumn(vZ, 2);
	mTemp.SetColumn(vF, 3);
	mTemp.RemoveEmptyRows(false);
	mTemp.GetColumn(vX, 0);
	mTemp.GetColumn(vY, 1);
	mTemp.GetColumn(vZ, 2);
	mTemp.GetColumn(vF, 3);
	
	return nSize;
	
}

//------ Folger 07/23/08 BETTER_OPREATION_OUTPUT_COLUMN_COMMENTS
static	string	_get_output_range_comments(const Range& rng)
{
	string strRange;
	int nNumRange = rng.GetNumRanges();
	for ( int ii=0; ii<nNumRange; ++ii )
	{
		DataRange drSub;
		rng.GetSubRange(drSub, DRR_NO_FACTORS, ii);
		if ( drSub )
		{
			strRange += get_input_data_range_string(drSub);
			if ( ii != nNumRange - 1 )
				strRange += ", ";
		}
	}
	
	return "3D interp of " + strRange;
}
//------ End BETTER_OPREATION_OUTPUT_COLUMN_COMMENTS

static bool _interp3_update_report_data(ReportData& outData, const vector& vX, const vector& vY, const vector& vZ, const vector& vF, string strLabel)
{
	if ( !outData )
		return false;
	
	outData.ID = IDST_RESULT_CURVES;
	ReportTable rtInterp3 = outData.CreateTable("Interp3", strLabel, INTERP3_REPORT_TABLE);
	rtInterp3.AddColumn(vX, "X",IDST_INTERP3_X, "Interp X", OKDATAOBJ_DESIGNATION_X);
	rtInterp3.AddColumn(vY, "Y",IDST_INTERP3_Y, "Interp Y", OKDATAOBJ_DESIGNATION_Y);
	rtInterp3.AddColumn(vZ, "Z",IDST_INTERP3_Z, "Interp Z", OKDATAOBJ_DESIGNATION_Z);
	rtInterp3.AddColumn(vF, "F",IDST_INTERP3_F, "3D Interpolation", OKDATAOBJ_DESIGNATION_Y);
	
	return true;
	
}

static int _get_data(const Range& rData, vector& vX, vector& vY, vector& vZ, vector& vF)
{
	if (!rData)
		return -1;
	
	rData.GetData(vX, 0);
	rData.GetData(vY, 1);
	rData.GetData(vZ, 2);
	rData.GetData(vF, 3);		
	
\	return _trim_missing(vX, vY, vZ, vF);	
	
}

static void xyz_data(const int& nPts, const double xmin, const double xmax, const double ymin, const double ymax, const double zmin, const double zmax, vector& vX, vector& vY, vector& vZ)
{
	vector vXTemp, vYTemp, vZTemp, vYTemp1, vZTemp1;
	
	vYTemp1.SetSize(nPts);
	///Cheney 2007-8-15 FIX_SOME_BU_OF_INTERP
	//vZTemp1.Data(zmin, zmax, (zmax-zmin)/(nPts-1));
	if(nPts == 1)
		vZTemp1.Add(zmin); //as max's suggestion
	else
		vZTemp1.Data(zmin, zmax, (zmax-zmin)/(nPts-1));
	///end FIX_SOME_BU_OF_INTERP
	for (int ii = 0; ii < nPts; ii++)
	{
		vYTemp1 = ymin + ii*(ymax-ymin)/(nPts-1);
		vYTemp.Append(vYTemp1);
		vZTemp.Append(vZTemp1);				
	}
	vXTemp.SetSize(nPts * nPts);
	for (ii = 0; ii < nPts; ii++)
	{
		vXTemp = xmin + ii*(xmax-xmin)/(nPts-1);
		vX.Append(vXTemp);
		vY.Append(vYTemp);
		vZ.Append(vZTemp);
	}
 
}

///Cheney 2007-8-15 FIX_SOME_BU_OF_INTERP
bool _check_min_max(int nPts, double dMin, double dMax)
{
	if(nPts == 1)
		return dMin > dMax? false : true;
	
	return check_min_max(dMin, dMax);
}
///end FIX_SOME_BU_OF_INTERP

static int interp3_check_input(TreeNode& trGetN)
{
	Range rData;
	TreeNode tr = trGetN.irng;
	okxf_resolve_tree_construct_range(&tr, &rData);
	
	int nErrCode = CER_NO_ERROR;
	
	vector vX, vY, vZ, vF;
	int nSize = _get_data(rData, vX, vY, vZ, vF);
	if (nSize < 1)
		nErrCode = CER_NOT_INTERP3_DATA;
	else if (nSize < INTERP3_MIN_PTS)		
		nErrCode = CER_DATA_TOO_FEW;
	
	///Cheney 2007-8-15 FIX_SOME_BU_OF_INTERP
	//if (!check_min_max(trGetN.xmin.dVal, trGetN.xmax.dVal) || !check_min_max(trGetN.ymin.dVal, trGetN.ymax.dVal) || !check_min_max(trGetN.zmin.dVal, trGetN.zmax.dVal))
	else if(trGetN.pts.dVal < 1)
		nErrCode = CER_INPUT_ARGUMENT_LESS_THAN_SPECIFIED_VALUE;	
	
	///Echo 07/28/08 v8.0911 PTS_LIMITATION_FOR_RD	
	else if (trGetN.pts.dVal > INTERP3_MAX_PTS_PER_DIM)	
		nErrCode = CER_PTS_TOO_MANY_EACH_DIM;
		
	else if (!_check_min_max(trGetN.pts.dVal, trGetN.xmin.dVal, trGetN.xmax.dVal) || !_check_min_max(trGetN.pts.dVal, trGetN.ymin.dVal, trGetN.ymax.dVal) || !_check_min_max(trGetN.pts.dVal, trGetN.zmin.dVal, trGetN.zmax.dVal))
	///end FIX_SOME_BU_OF_INTERP
	{
		if (nErrCode == CER_NO_ERROR) nErrCode = CER_MIN_MAX;
	}
	
	return nErrCode;
	
}


void interp3(const Range& irng, const int& pts, const double& xmin, const double& xmax, const double& ymin, const double& ymax, const double& zmin, const double& zmax, ReportData& rd)
{
	
	if (!irng.IsValid())
	{
		XF_THROW(CER_NO_DATA);
	}
	///Cheney 2007-8-15 FIX_SOME_BU_OF_INTERP
	//if (!check_min_max(xmin, xmax) || !check_min_max(ymin, ymax) || !check_min_max(zmin, zmax))
	if (!_check_min_max(pts, xmin, xmax) || !_check_min_max(pts, ymin, ymax) || !_check_min_max(pts, zmin, zmax))
	///end FIX_SOME_BU_OF_INTERP
	{
		XF_THROW(CER_MIN_MAX);
	}
	
	vector vX, vY, vZ, vF, vIntpX, vIntpY, vIntpZ, vQ;
	int nSize = _get_data(irng, vX, vY, vZ, vF);
	int nMethod = Remove_With_Mean; 
	double dPrecision = 1.0e-8;
	nSize = ocmath_xyzv_remove_duplicates(nSize, vX, vY, vZ, vF, nMethod, dPrecision);
	vX.SetSize(nSize);
	vY.SetSize(nSize);
	vZ.SetSize(nSize);
	vF.SetSize(nSize);
	//data size should be larger than 10
	if (nSize < 10)
	{
		XF_THROW_EX(CER_DATA_TOO_FEW, 10);
		
	}
	///*** Begin 3d interpolating
	///Echo 11/23/06 MESHGRID_X_Y_Z
	xyz_data(pts, xmin, xmax, ymin, ymax, zmin, zmax, vIntpX, vIntpY, vIntpZ);
	/*
	vIntpX.Data(xmin, xmax, (xmax-xmin)/(pts-1));
	vIntpY.Data(ymin, ymax, (ymax-ymin)/(pts-1));
	vIntpZ.Data(zmin, zmax, (zmax-zmin)/(pts-1));
	*/
	///END MESHGRID_X_Y_Z
	
	int nInterpSize = pts*pts*pts;
	vQ.SetSize(nInterpSize);
	int nRet = ocmath_3d_interpolate(vIntpX, vIntpY, vIntpZ, vQ, nInterpSize, vX, vY, vZ, vF, nSize);
	///Arvin 11/17/06 DETAIL_ERROR_MESSAGE
	/*
	if (nRet == 2085)
	{
		XF_THROW(CER_BAD_POINT);
	}else if (nRet != 0)
	{
		XF_THROW_EX(CER_UNKNOWN_ERROR_EX, nRet);
	}
	*/
	switch(nRet)
	{
	case 2083:
		XF_THROW(CER_DATA_COPLANAR);
	case 2084:
		XF_THROW(CER_DUPLICATE_NODE);
	case 2085:
		XF_THROW(CER_BAD_POINT);
	}
	if (nRet != 0)
	{
		XF_THROW_EX(CER_UNKNOWN_ERROR_EX, nRet);
	}
	
	//------ Folger 07/23/08 BETTER_OPREATION_OUTPUT_COLUMN_COMMENTS
	_interp3_update_report_data(rd, vIntpX, vIntpY, vIntpZ, vQ, _get_output_range_comments(irng));
	//------
	
}

//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 interp3_event1(TreeNode& trGetN, int nRow, int nEventID, DWORD& dwEnables, LPCSTR lpcszNodeName, WndContainer& DynaCntrlContainer, string& strAux, string& strErrMsg)
{
	if( xf_is_extended_event(nEventID) )
		return interp3_event1_ex(trGetN, nRow, nEventID, dwEnables, lpcszNodeName, DynaCntrlContainer, strAux, strErrMsg);
	DECLARE_BUTTON_ENABLES
	
	Range rData;
	TreeNode tr = trGetN.irng;
	okxf_resolve_tree_construct_range(&tr, &rData);
	if(rData)
	{
		_init_tree_show(trGetN, rData);
	}
	
	int nErrCode = interp3_check_input(trGetN);
	if(nErrCode != CER_NO_ERROR)
	{
		bOKEnable = false;
		strErrMsg = nErrCode;
		if (CER_DATA_TOO_FEW == nErrCode) strErrMsg += ":" + INTERP3_MIN_PTS;
	}
	
	///END INPUT_CHECK_ERROR
	return true;
	

}

int interp3_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 interp3_before_execute(TreeNode& trGetN, int nGetNDialog, int& nRet, int dwCntrl)
{
	
	//------ fisher 11/16/07 ADD_MAP_ID_TO_CHM
	if(is_use_origin_chm())
		trGetN.SetAttribute(STR_HELPID_ATTRIB, IDD_XF_interp3);	
	
	if(0 == nGetNDialog || -1 == nGetNDialog)
	{
		Range rData;
		TreeNode tr = trGetN.irng;
		okxf_resolve_tree_construct_range(&tr, &rData);		
		_init_tree_show(trGetN, rData);
		
		///Cheney 2007-6-14 PROCEED_WITH_DLG_SHOULD_IF_ERR
		//if ((dwCntrl&LTXF_THEME_USED) && interp3_check_input(trGetN) != CER_NO_ERROR)
		if ((dwCntrl & LTXF_FROM_GUI_MENU) && (dwCntrl & LTXF_THEME_USED) 
			&& 0 == nGetNDialog && interp3_check_input(trGetN) != CER_NO_ERROR)
		///end PROCEED_WITH_DLG_SHOULD_IF_ERR
			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 interp3_event1_ex(TreeNode& trGetN, int nRow, int nEventID, DWORD& dwEnables, LPCSTR lpcszNodeName, WndContainer& DynaCntrlContainer, string& strAux, string& strErrMsg)
{
	return false;
}
