#include <origin.h>
#include <GetNBox.h>
#include <xfutils.h>
enum{
	bg_dark,
	bg_bright,
};


#include <GetNBox.h>
#include <image_utils.h>
#include <..\originlab\matdata_utils.h>
//put additional include files here

//put your own support static functions here
static bool s_bChangeParam;
enum {
	MASTER_CHANNEL = 0,
	RED_CHANNEL,
	GREED_CHANNEL,
	BLUE_CHANNEL,
	RGB_CHANNELS,
};

enum
{
	PKF_POSITIVE_DIRECTION = 1,
	PKF_NEGATIVE_DIRECTION,
  	PKF_BOTH_DIRECTION,
};


static int _image_replace_color(pBITMAPHANDLE phBmp, const int nLowThr, const int nHighThr, const int nFuzzi, const int nAdjBightness)
{
	
	HANDLE hData = GlobalAlloc(GHND,sizeof(COLORREPLACE) * (nHighThr - nLowThr + 1));
	LPCOLORREPLACE pColorReplace = (LPCOLORREPLACE)GlobalLock(hData); 
	
	int n = 0;
	for(int ii = nLowThr; ii <= nHighThr; ii++)
	{
		pColorReplace[n].uStructSize = sizeof(COLORREPLACE);
		pColorReplace[n].crColor    = RGB(ii,ii,ii);
		pColorReplace[n].uFuzziness = nFuzzi; 
		n ++;
	}
	//pBITMAPHANDLE phBmp = img.GetLBmp();
	int nRet;
	int nBightness = nAdjBightness*10;
	nRet = L_ColorReplaceBitmap(phBmp, pColorReplace,  (nHighThr - nLowThr + 1), 0, 0, nBightness);//uColorCount, nHue, nSaturation, nBrightness))

	GlobalUnlock(hData);
	GlobalFree(hData);
		
	return nRet;
}

static int _get_2_max_peaks_from_hist(vector& vy, vector<int>& vPeaksInd)
{
	if(vy.GetSize()!= vPeaksInd.GetSize() || vy.GetSize()<2)
		return -2;
	
	vector<int> v2(2);
	double dMax, dMin;
	int nMax, nMin;
	vy.GetMinMax(dMin, dMax, &nMin, &nMax);

	v2[0] = vPeaksInd[nMax];
	vy[nMax] = 0;
	
	vy.GetMinMax(dMin, dMax, &nMin, &nMax);
	v2[1] = vPeaksInd[nMax];
	
	vPeaksInd = v2;
	return CER_NO_ERROR;
}

static int _get_range_of_bg(pBITMAPHANDLE pBitmap, int& nLow, int& nHigh,bool bHiBG)
{
	
	vector<int> vnHist;
	int nLen=get_image_data_max_val(pBitmap)+1;
	vnHist.SetSize(nLen);
	int nRet = histogram_image(pBitmap, vnHist, nLen ,MASTER_CHANNEL);
	if( SUCCESS != nRet)
	{
		return nRet;
	}	
	
	vector vHistogram;
	vHistogram = vnHist;
	
	
	int nColorNum = nLen;
	vector vx;
	vx.Data(0,nLen-1,1);
	vector vxPeaks(nLen);
	vector vyPeaks(nLen);
	vector<int> vPeaksInd(nLen);
	
	//nRet = ocmath_find_peaks_1st_derivative(&nColorNum, vx, vHistogram, vxPeaks, vyPeaks, vPeaksInd, PKF_POSITIVE_DIRECTION);
	nRet =ocmath_find_peaks_by_local_maximum(&nColorNum, vx, vHistogram, vxPeaks, vyPeaks, vPeaksInd, PKF_POSITIVE_DIRECTION, 1);
	if(nRet != CER_NO_ERROR)
	{
		return nRet;
	}
	
	vxPeaks.SetSize(nColorNum);
	vyPeaks.SetSize(nColorNum);
	vPeaksInd.SetSize(nColorNum);
	
	
	nRet = _get_2_max_peaks_from_hist(vyPeaks, vPeaksInd);
	if(nRet != CER_NO_ERROR)
	{
		return nRet;
	}
	
	if(!bHiBG)
		vPeaksInd[0] = vPeaksInd[1];
	
	vPeaksInd.SetSize(1);
	vector<int> vFeetInd(2);
	double dFootHeight = sqrt(ocmath_get_noise_level(nLen, vHistogram));  // footheight defined at half the noise level

	nRet = ocmath_find_markers_of_peaks_by_height_and_width(nLen, vx, vHistogram, 1, vPeaksInd, 2, vFeetInd,  
														 dFootHeight, 200, 10);
	if(nRet != CER_NO_ERROR)												 
	{
		return nRet;
	}	
	
	nLow = vFeetInd[0];
	nHigh = vFeetInd[1];
	
	return CER_NO_ERROR;
}

static bool _compute_low_high_thresholds(TreeNode trGetN,  pBITMAPHANDLE pPreviewBitmap, string *pstrErrMsg)
{
	TreeNode trLow = trGetN.bgl;
	TreeNode trHigh = trGetN.bgh;
	if (!trLow || !trHigh)
	{
		if (pstrErrMsg!=NULL)
			*pstrErrMsg = CER_INVALID_TREENODE;
		return false;
	}
	
	int nLow, nHigh;
    int nRet = _get_range_of_bg(pPreviewBitmap, nLow, nHigh, trGetN.bg.nVal);
	if( CER_NO_ERROR != nRet)
	{
		*pstrErrMsg = nRet;
		return false;
	}	
		
	if(1==octree_get_auto_support(&trLow))
		trLow.nVal = nLow;
	
	if(1==octree_get_auto_support(&trHigh))
		trHigh.nVal = nHigh;
	
	//if(trLow.nVal<0 || trLow.nVal>255 || trHigh.nVal<0 ||  trHigh.nVal>255)
	//{
		//if (pstrErrMsg!=NULL)
			//*pstrErrMsg = XFERR_INTOPTN_OUT_OF_RANGE;
		//return false;
	//}
	//

	
	return true;
}

void imgReplaceBg(const Image& img, Image& oimg, const int& bgl, const int& bgh, const int& fuzzi, const int& adj_b, const int& bg)
{
	
	oimg.SetLBmp(img.GetLBmp(), TRUE);
	pBITMAPHANDLE phBmp = oimg.GetLBmp();
	
	int nRet = _image_replace_color(phBmp, bgl, bgh, fuzzi, adj_b);
	
}

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

}

int imgReplaceBg_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 imgReplaceBg_before_execute(TreeNode& trGetN, int nGetNDialog, int& nRet, int dwCntrl)
{
	
	s_bChangeParam = false;
	if(dwCntrl&LTXF_CHANGE_PARAM) 	
	{
		s_bChangeParam = true;
	}
}
//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 imgReplaceBg_event1_ex(TreeNode& trGetN, int nRow, int nEventID, DWORD& dwEnables, LPCSTR lpcszNodeName, WndContainer& DynaCntrlContainer, string& strAux, string& strErrMsg)
{
	return false;
}


// this is called when the GetNImageBox dialog is open
// bUpdateGrid = false when entering this function
// you need to set bUpdateGrid = true if you need to modify trGetN data and want to see the
// grid updated, for example
// trGetN.angle.nVal = 0; // force rotation to init to 0
// bUpdateGrid = true;
// bOKEnable is default = true, you may add code to check trGetN and decide that the OK button should be disabled, then set this to false
// example: 
// if(0==trGetN.angle.nVal) // no change, no point to click Ok to do nothing
//    bOKEnable = false; 
static void GetNImageBox_OnInit(TreeNode& trGetN, ImageControlBase* pimgCntrl, bool& bUpdateGrid, bool& bOKEnable, string& strErrMsg)
{
	pBITMAPHANDLE pPreviewBitmap;
	pPreviewBitmap=pimgCntrl->GetBitmapHandlePtr();

	int nRet;
	if(!s_bChangeParam)
	{
		//int nLow, nHigh;
		//nRet = _get_range_of_bg(pPreviewBitmap, nLow, nHigh, trGetN.bg.nVal);
		//if( CER_NO_ERROR != nRet)
		//{
			//bOKEnable = false;
			//strErrMsg = nRet;
			//return;
		//}	
		//
		//trGetN.bgl.nVal = nLow;
		//trGetN.bgh.nVal = nHigh;
		bOKEnable = _compute_low_high_thresholds(trGetN,  pPreviewBitmap, &strErrMsg);
		s_bChangeParam = false;
	}
	
	nRet = _image_replace_color(pPreviewBitmap, trGetN.bgl.nVal, trGetN.bgh.nVal, trGetN.fuzzi.nVal, trGetN.adj_b.nVal);
	if( SUCCESS != nRet)
	{
		bOKEnable = false;
		strErrMsg = nRet;
	}
	bUpdateGrid = true;
}

// this is called whenever any controls in the grid is modified by the user
// upon entering function
// bUpdatePreview = true;// to update the preview image pPreviewBitmap
// bUpdateGrid = false; // event triggered by grid change, so default no need to update again
// if you want to modify other controls in the grid, then you need to set bUpdateGrid to true
// bOKEnable is default = true, you may add code to check trGetN and decide that the OK button should be disabled, then set this to false
static void GetNImageBox_OnChange(BITMAPHANDLE* pPreviewBitmap, TreeNode& trGetN, TreeNode& trNode, ImageControlBase* pimgCntrl, bool& bUpdatePreview, bool& bUpdateGrid, bool& bOKEnable, string& strErrMsg)
{


	if(!trNode.IsValid() || (trNode.tagName == "bg"||trNode.tagName == "bgl"|| trNode.tagName == "bgh"))
	{
		//int nLow, nHigh;
		//int nRet = _get_range_of_bg(pPreviewBitmap, nLow, nHigh, trGetN.bg.nVal);
		//if( CER_NO_ERROR != nRet)
		//{
			//bOKEnable = false;
			//strErrMsg = nRet;
			//return;
		//}	
		//
		//trGetN.bgl.nVal = nLow;
		//trGetN.bgh.nVal = nHigh;	
		bOKEnable = _compute_low_high_thresholds(trGetN,  pPreviewBitmap, &strErrMsg);
	}
	
	if(trGetN.bgl.nVal >= trGetN.bgh.nVal)
	{
		bOKEnable = false;
		strErrMsg = XFERR_MIN_LARGER_THAN_MAX;
		return;
	}
	
	
	int nRet = _image_replace_color(pPreviewBitmap, trGetN.bgl.nVal, trGetN.bgh.nVal, trGetN.fuzzi.nVal, trGetN.adj_b.nVal);
	if( SUCCESS != nRet)
	{
		bOKEnable = false;
		strErrMsg = nRet;
	}
	
	bUpdatePreview = true;
	bUpdateGrid = true;
}
	
int GetNImageBox_imgReplaceBg(TreeNode& trGetN, int nRow, int nCol, TreeNode& trNode, DWORD dwCntrl, int nType, WndContainer& theDlg, ImageControlBase* pimgCntrl)
{
	return xf_GetNImageBox_event_handler(trGetN, nRow, nCol, trNode, dwCntrl, nType, pimgCntrl,
								GetNImageBox_OnInit, GetNImageBox_OnChange);	
}
