#include <origin.h>
#include <GetNBox.h>
#include <xfutils.h>
enum{
	op_dilation,
	op_erosion,
	op_open,
	op_close,
};
enum{
	emode_arb,
	emode_diamond,
	emode_line,
	emode_octagon,
	emode_pair,
	emode_pl,
	emode_rec,
	emode_square,
};
//put additional include files here
#include <GetNBox.h>
#include <image_utils.h>
#include <..\originlab\matdata_utils.h>

/***************************************************************************************
/// Modification Log :
//------ Folger 12/01/08 v8.0981 BETTER_HANDLING_OF_INPUT_OUTPUT_MATRIX_OBJECTS
***************************************************************************************/

//------ Folger 12/01/08 v8.0981 BETTER_HANDLING_OF_INPUT_OUTPUT_MATRIX_OBJECTS
#define		STR_AUTO_UNCHECK_DYNA_USE		"AutoDynaUseUncheck"
//------

//put your own support static functions here
bool _check_input_image(TreeNode trGetN, string& strErrMsg)
{
	MatrixObject mi;
	if(!okxf_resolve_string_get_origin_object(trGetN.im.strVal, &mi) || !mi.IsValid())	
	{
		strErrMsg = XFERR_INVALID_MATOBJ;
		return false;
	}
	
	if(!mi.HasData())
	{
		strErrMsg = XFERR_IS_NOT_GRAY_SCALE_IMAGE;
		return false;
	}
	
	return true;
}

static void _update_input_output_element_status(TreeNode& trGetN)
{
	//------ Folger 12/01/08 v8.0981 BETTER_HANDLING_OF_INPUT_OUTPUT_MATRIX_OBJECTS
	/*
	string strOut, strIn;
	trGetN.em.GetAttribute(STR_DATA_ATTRIB, strIn);
	trGetN.out_em.GetAttribute(STR_DATA_ATTRIB, strOut);

	if(trGetN.emode.nVal == ARBITRARY_SHAPE)
	{
		trGetN.out_em.SetAttribute(STR_ATTRIB_DYNACONTROL_USE_CHECK, 0);
		//trGetN.em.SetAttribute(STR_ATTRIB_DYNACONTROL_USE_CHECK, 1);
		if (okutil_cvt_str_to_predefined_type(strIn) != PDS_USER)
		{
			trGetN.em.strVal = STR_UNASSIGNED;
		}
	}
	else
	{
		int nUse;
		trGetN.out_em.GetAttribute(STR_ATTRIB_DYNACONTROL_USE_CHECK, nUse);
		//trGetN.em.SetAttribute(STR_ATTRIB_DYNACONTROL_USE_CHECK, 0);
		if (okutil_cvt_str_to_predefined_type(strOut) != PDS_USER && nUse )
		{
			trGetN.out_em.strVal = STR_UNASSIGNED;
		}
		if (okutil_cvt_str_to_predefined_type(strIn) != PDS_USER )
		{
			trGetN.out_em.strVal = STR_OPTIONAL;
		}
	}
	*/
	if(trGetN.emode.nVal == ARBITRARY_SHAPE)
	{
		int		nDynaUseCheck = 0;
		if ( trGetN.out_em.GetAttribute(STR_ATTRIB_DYNACONTROL_USE_CHECK, nDynaUseCheck) && nDynaUseCheck != 0 )
		{
			trGetN.out_em.SetAttribute(STR_ATTRIB_DYNACONTROL_USE_CHECK, 0);
			trGetN.out_em.SetAttribute(STR_AUTO_UNCHECK_DYNA_USE, 1);
		}
		if (okutil_cvt_str_to_predefined_type(trGetN.em.strVal) != PDS_USER)
		{
			trGetN.em.strVal = STR_UNASSIGNED;
		}
	}
	else
	{
		int		nAutoUncheckDynaUse;
		if ( trGetN.out_em.GetAttribute(STR_AUTO_UNCHECK_DYNA_USE, nAutoUncheckDynaUse) )
		{
			trGetN.out_em.SetAttribute(STR_ATTRIB_DYNACONTROL_USE_CHECK, 1);
			trGetN.out_em.RemoveAttribute(STR_AUTO_UNCHECK_DYNA_USE);
		}
		if (okutil_cvt_str_to_predefined_type(trGetN.em.strVal) != PDS_USER )
		{
			trGetN.em.strVal = STR_OPTIONAL;
		}
	}
	//------ End BETTER_HANDLING_OF_INPUT_OUTPUT_MATRIX_OBJECTS
}

static void _update_height_matrix_status(TreeNode& trGetN)
{
	//------ Folger 12/01/08 v8.0981 BETTER_HANDLING_OF_INPUT_OUTPUT_MATRIX_OBJECTS
	/*
	string strHM;
	trGetN.hm.GetAttribute(STR_DATA_ATTRIB, strHM);

	if(trGetN.flat.nVal)
	{
		if (okutil_cvt_str_to_predefined_type(strHM) != PDS_USER)
		{
			trGetN.hm.strVal = STR_OPTIONAL;
		}
	}
	else
	{
		if (okutil_cvt_str_to_predefined_type(strHM) != PDS_USER )
		{
			trGetN.hm.strVal = STR_UNASSIGNED;
		}
	}
	*/
	if(trGetN.flat.nVal)
	{
		if (okutil_cvt_str_to_predefined_type(trGetN.hm.strVal) != PDS_USER)
		{
			trGetN.hm.strVal = STR_OPTIONAL;
		}
	}
	else
	{
		if (okutil_cvt_str_to_predefined_type(trGetN.hm.strVal) != PDS_USER )
		{
			trGetN.hm.strVal = STR_UNASSIGNED;
		}
	}
	//------ End BETTER_HANDLING_OF_INPUT_OUTPUT_MATRIX_OBJECTS
}

void imgMorph(const MatrixObject& im, MatrixObject& om, const int& op, const int& flat, const MatrixObject& hm, const int& emode, const MatrixObject& em, const int& col, const int& row, const int& w, const int& r, const int& roffset, const int& coffset, const int& len, const int& deg, const int& np, MatrixObject& out_em)
{
	
	//matrix& mIn = mi.GetDataObject();
	//matrix& mOut = mo.GetDataObject();
	//
	//mOut = mIn;
	if(!im.HasData())
	{
		Image img(im);
		img.CopyTo(om, true);
	}
	else
		matobj_copy(om, im);
	matrixbase& mbo = om.GetDataObject();
	matrix mOut;
	mOut = mbo;	
	
	int nRows = mbo.GetNumRows();
	int nCols = mbo.GetNumCols();	
	
	int nRet;
	///make element 
	StrelParams sp;
	StrelResult se;
	
	switch(emode)
	{
	case ARBITRARY_SHAPE:
		if(!em.IsValid()&&!hm.IsValid())
			XF_THROW(XFERR_INVALID_MATOBJ);
		sp.nRows = em.GetNumRows();
		sp.nCols = em.GetNumCols();
		
		if(hm.IsValid())
		{
			int nRows = hm.GetNumRows();
			int nCols = hm.GetNumCols();
			if(sp.nRows != nRows || sp.nCols != nCols)
				XF_THROW(XFERR_INVALID_MATOBJ);
		}
		
		se.nRows = sp.nRows;
		se.nCols = sp.nCols;		
		matrix& matE = em.GetDataObject();
		matrix& matH = hm.GetDataObject();
		sp.pNHood = matE;
		sp.pHeight = matH;
		break;
	case RECTANGLE_SHAPE:
		if(col<=0||row<=0)
			XF_THROW(XFERR_INVALID_ARG);
		sp.nRows = col;
		sp.nCols = row;
		se.nRows = sp.nRows;
		se.nCols = sp.nCols;		
		break;
	case SQUARE_SHAPE:
		if(w<=0)
			XF_THROW(XFERR_INVALID_ARG);
		sp.nWidth = w;
		se.nRows = sp.nWidth;
		se.nCols = sp.nWidth;		
		break;
	
	case OCTAGON_SHAPE:
		if(r<=0 || (r%3 != 0))
			XF_THROW(XFERR_INVALID_ARG);
		
	case DIAMOND_SHAPE:
		sp.nRadius = r;
		se.nRows = sp.nRadius*2+1;
		se.nCols = sp.nRadius*2+1;
		
		break;
	case PAIR_SHAPE:
		sp.colOffset = coffset;
		sp.rowOffset = roffset;
		se.nRows = abs(sp.rowOffset*2)+1;
		se.nCols = abs(sp.colOffset*2)+1;		
		
		break;
	case LINE_SHAPE:
		sp.nLen = len;
		sp.thetaDeg = deg;
		double theta = deg*PI/180; //In oc sin and cos use angle in radians.
		se.nRows = 2*abs(round(-1.0*(sp.nLen-1)/2*sin(theta), 0))+1;	
		se.nCols = 2*abs(round(1.0*(sp.nLen-1)/2*cos(theta), 0))+1;
		break;
	case PERIODICLINE_SHAPE:
		sp.colOffset = coffset;
		sp.rowOffset = roffset;
		
		se.nRows = abs(sp.rowOffset*2*np)+1;
		se.nCols = abs(sp.colOffset*2*np)+1;	
		if(np<=0)
			XF_THROW(XFERR_INVALID_ARG);		
		sp.nP = np;
		break;
	}
		//matrix matHeight;
		//matrix matHood;
		//matHeight = mElement;
		//matHood = matHeight;
		//matHood.Replace( 1, 0, MATREPL_TEST_GREATER|MATREPL_TEST_LESSTHAN , 0);
	//
		
		matrix matHood, matHeight;
		matHood.SetSize(se.nRows, se.nCols);
		matHeight.SetSize(se.nRows, se.nCols);
	
		se.pNHoodMat = matHood;
		se.pHeightMat = matHeight;	
		nRet = ocmath_make_strel(&se, &sp, emode);
		if(nRet != OE_NOERROR)
		{
			//printf("  Error: Make strel failed. Error Code=%d\n", nRet);
			//return;
			XF_THROW_EX(XFERR_MORPH_FAIL_MAKE_STREL, nRet);
		}
		
	
	///create element preview
	//MatrixPage mp;
	//mp.Create();
	//mp.Rename("strel");
	//MatrixLayer ml = mp.Layers(0);
	//ml.SetName("Element");
	//ml.SetSize(1, se.nRows, se.nCols);
	//MatrixObject moElement = ml.MatrixObjects(0);
	//moElement.SetSize(se.nRows, se.nCols);
	//matrix& matE = moElement.GetDataObject();
	//matE = matHood;
	if(out_em)
	{
		out_em.SetSize(se.nRows, se.nCols);
		matrix& matE = out_em.GetDataObject();
		matE = matHood;
	}
	///end
	
	
	switch(op)
	{
	case op_dilation:
		nRet = ocmath_dilate_matrix_with_strel(nRows, nCols, mOut,&se);
		break;
	case op_erosion:
		nRet = ocmath_erode_matrix_with_strel(nRows, nCols, mOut, &se);
		break;
	case op_open:
		nRet = ocmath_erode_matrix_with_strel(nRows, nCols, mOut, &se);
		if(nRet != 0)
			XF_THROW(nRet);
		nRet = ocmath_dilate_matrix_with_strel(nRows, nCols, mOut, &se);
		//mOut.ReplaceGreaterThan(255, 255);
		//mOut.ReplaceLessThan(0, 0);
		break;
	case op_close:
		nRet = ocmath_dilate_matrix_with_strel(nRows, nCols, mOut, &se);
		if(nRet != 0)
			XF_THROW(nRet);	
		//mOut.ReplaceGreaterThan(255, 255);
		//mOut.ReplaceLessThan(0, 0);	
		nRet = ocmath_erode_matrix_with_strel(nRows, nCols, mOut, &se);		
		break;
	default:
		nRet = INTOPTN_OUT_OF_RANGE;
	}
	if(nRet != 0 && nRet != INTOPTN_OUT_OF_RANGE)
		XF_THROW_EX(XFERR_MORPH_FAIL_IN_OPERATION, nRet);
	
	//mOut.ReplaceGreaterThan(255, 255);
	//mOut.ReplaceLessThan(0, 0);
	
	if(om.HasData(false))
		mbo = mOut;	
	else
	{
		if( 0!= set_image_data(mOut, om, 0, 255, 8))
			XF_THROW(XFERR_FAIL_SET_IMAGE_DATA);
	}
			
}

//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 imgMorph_event1(TreeNode& trGetN, int nRow, int nEventID, DWORD& dwEnables, LPCSTR lpcszNodeName, WndContainer& DynaCntrlContainer, string& strAux, string& strErrMsg)
{
	if( xf_is_extended_event(nEventID) )
		return imgMorph_event1_ex(trGetN, nRow, nEventID, dwEnables, lpcszNodeName, DynaCntrlContainer, strAux, strErrMsg);
	DECLARE_BUTTON_ENABLES
	
	if(!lstrcmpi(lpcszNodeName, "emode") ||  nEventID == GETNE_ON_THEME || nEventID == GETNE_ON_INIT)
	{
		_update_input_output_element_status(trGetN);
	}
	
	if(!lstrcmpi(lpcszNodeName, "flat") ||  nEventID == GETNE_ON_THEME || nEventID == GETNE_ON_INIT)
	{
		_update_height_matrix_status(trGetN);	
	}
	trGetN.hm.Show = (trGetN.flat.nVal == 1)? false : true; 
	trGetN.em.Show = (trGetN.emode.nVal == emode_arb)? true : false; 
	trGetN.out_em.Show = (trGetN.emode.nVal == emode_arb)? false : true; 
	
	switch(trGetN.emode.nVal)
	{
	case ARBITRARY_SHAPE:
		trGetN.em.Show = true;
		trGetN.col.Show = false;
		trGetN.row.Show = false;
		trGetN.w.Show = false;
		trGetN.r.Show = false;
		trGetN.roffset.Show = false;
		trGetN.coffset.Show = false;
		trGetN.len.Show = false;
		trGetN.deg.Show = false;
		trGetN.np.Show = false;
		
		
		///add by sandy 2007-3-9
		MatrixObject me;
		if(!okxf_resolve_string_get_origin_object(trGetN.em.strVal, &me) || !me.IsValid() || !me.HasData())	
		{
			strErrMsg = XFERR_INVALID_MATOBJ;
			bOKEnable = false;
			return false;
		}
		//end
		break;
	case RECTANGLE_SHAPE:
		trGetN.em.Show = false;
		trGetN.col.Show = true;
		trGetN.row.Show = true;
		trGetN.w.Show = false;
		trGetN.r.Show = false;
		trGetN.roffset.Show = false;
		trGetN.coffset.Show = false;
		trGetN.len.Show = false;
		trGetN.deg.Show = false;
		trGetN.np.Show = false;
		break;
	case SQUARE_SHAPE:
		trGetN.em.Show = false;
		trGetN.col.Show = false;
		trGetN.row.Show = false;
		trGetN.w.Show = true;
		trGetN.r.Show = false;
		trGetN.roffset.Show = false;
		trGetN.coffset.Show = false;
		trGetN.len.Show = false;
		trGetN.deg.Show = false;
		trGetN.np.Show = false;
		break;
	case DIAMOND_SHAPE:
	case OCTAGON_SHAPE:
		trGetN.em.Show = false;
		trGetN.col.Show = false;
		trGetN.row.Show = false;
		trGetN.w.Show = false;
		trGetN.r.Show = true;
		trGetN.roffset.Show = false;
		trGetN.coffset.Show = false;
		trGetN.len.Show = false;
		trGetN.deg.Show = false;
		trGetN.np.Show = false;
		break;
	case PAIR_SHAPE:
		trGetN.em.Show = false;
		trGetN.col.Show = false;
		trGetN.row.Show = false;
		trGetN.w.Show = false;
		trGetN.r.Show = false;
		trGetN.roffset.Show = true;
		trGetN.coffset.Show = true;
		trGetN.len.Show = false;
		trGetN.deg.Show = false;
		trGetN.np.Show = false;
		break;
	case LINE_SHAPE:
		trGetN.em.Show = false;
		trGetN.col.Show = false;
		trGetN.row.Show = false;
		trGetN.w.Show = false;
		trGetN.r.Show = false;
		trGetN.roffset.Show = false;
		trGetN.coffset.Show = false;
		trGetN.len.Show = true;
		trGetN.deg.Show = true;
		trGetN.np.Show = false;
		break;
	case PERIODICLINE_SHAPE:
		trGetN.em.Show = false;
		trGetN.col.Show = false;
		trGetN.row.Show = false;
		trGetN.w.Show = false;
		trGetN.r.Show = false;
		trGetN.roffset.Show = true;
		trGetN.coffset.Show = true;
		trGetN.len.Show = false;
		trGetN.deg.Show = false;
		trGetN.np.Show = true;
		break;
	}
	
	//------ Folger 12/01/08 v8.0981 BETTER_HANDLING_OF_INPUT_OUTPUT_MATRIX_OBJECTS
	if ( trGetN.hm.Show )
	{
		MatrixObject moHM;
		if(!okxf_resolve_string_get_origin_object(trGetN.hm.strVal, &moHM) || !moHM.IsValid() || !moHM.HasData())	
		{
			strErrMsg = XFERR_INVALID_MATOBJ;
			bOKEnable = false;
			return false;
		}
	}
	//------ End BETTER_HANDLING_OF_INPUT_OUTPUT_MATRIX_OBJECTS
	
	int r = trGetN.r.nVal;
	if(	   (trGetN.col.Show == true)&&(trGetN.col.nVal<=0)
		|| (trGetN.row.Show == true)&&(trGetN.row.nVal<=0)
		|| (trGetN.w.Show == true)&&(trGetN.w.nVal<=0)
		|| (trGetN.r.Show == true)&&(r<=0)
		|| (trGetN.np.Show == true)&&(trGetN.np.nVal<=0)
		|| (trGetN.len.Show == true)&&(trGetN.len.nVal<=0)
		|| (trGetN.emode.nVal == OCTAGON_SHAPE) && (r%3 != 0))
	{
		strErrMsg = XFERR_INVALID_ARG;
		bOKEnable = false;
		return false;
	}
	
	//if(nEventID == GETNE_ON_INIT || !lstrcmpi(lpcszNodeName, "im") || nEventID == GETNE_ON_THEME)
	{
		
		bOKEnable = _check_input_image(trGetN, strErrMsg);
		
	}	
		
	
	
	return true;

}

int imgMorph_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 imgMorph_before_execute(TreeNode& trGetN, int nGetNDialog, int& nRet, int dwCntrl)
{
	
	if(nGetNDialog == 0)
	{
		string strErrMsg;
		if(! _check_input_image(trGetN, strErrMsg))
			nRet = XFEVT_ABORT;
	}
	
	if(nGetNDialog > 0)
	{
		_update_input_output_element_status(trGetN);
		_update_height_matrix_status(trGetN);	
	}
}
//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 imgMorph_event1_ex(TreeNode& trGetN, int nRow, int nEventID, DWORD& dwEnables, LPCSTR lpcszNodeName, WndContainer& DynaCntrlContainer, string& strAux, string& strErrMsg)
{
	return false;
}
