/*------------------------------------------------------------------------------*
 * File Name: ImageFindObjectDlg.h												*
 * Creation: CPY 1/15/2004														*
 * Purpose: Image Segmentation using Contouring analysis						*
 * Copyright (c) OriginLab Corp.2004											*
 * All Rights Reserved															*
 * 																				*
 * Modification Log:															*
 *------------------------------------------------------------------------------*/
 
 
#define LEVEL_SLIDE_MAX	200
enum
{
	ON_SEARCH_REPLACE = 0,
	ON_SEARCH_HOLD,
	ON_SEARCH_DESTROY,
};
#define STR_CONTOUR_LEGNTH	"Edge length"

class InfoObjList
{
public:
	virtual 	string MakeDisplayStr()
	{
		string strempty;
		return strempty;
	}
protected:
	string addTimeStamp(int ii, bool bAddDivider = true)
	{
		string str;
		str = "\r\nTime: " + m_vsTime[ii] + "\r\n";
		if(bAddDivider)
			str += "--------------------\r\n";
		return str;
	}
	string strCurrentTime()
	{
		SYSTEMTIME st;
		/*
		GetSystemTime(&st);              // gets current time
		double dDate;
		SystemTimeToJulianDate(&dDate, &st);
		*/
		TM tmLocal;
		time_t aclock;
		time( &aclock );                 // Get time in seconds 

		convert_time_to_local( &aclock , &tmLocal);
        tm_to_systemtime(&tmLocal, &st);
        
		double dDate;
		SystemTimeToJulianDate(&dDate, &st);
		return get_date_str(dDate, LDF_SHORT_AND_HHMMSS_SEPARCOLON);
	}
	vector<string>	m_vsTime;
};

typedef struct tagPolygonInfo
{
	uint	nSize;
	double 	area;
	double	roundness;
	double	aveIntensity;
	double	IntensitySD;
	double	complexity;
	double	parallelness;
	double	minLength;
	double	maxLength;
} PolygonInfo;
	
class PolygonObjList : public InfoObjList
{
public:
	PolygonObjList() {}
	~PolygonObjList() {}
	
	
	string MakeDisplayStr()
	{
		string str;
		for(int ii = 0; ii < m_vsTime.GetSize(); ii++)
		{
			str += addTimeStamp(ii);
			str += "       Area = " + m_vfAreas[ii] + "\r\n";
			str += STR_CONTOUR_LEGNTH " = " + m_vnPts[ii] + "\r\n";
			str += "  Roundness = " + m_vfRoundness[ii] + "\r\n";
			str += "  Intensity = " + m_vfMeanIntensity[ii] + "\r\n";
			str += "Intensity SD= " + m_vfSDIntensity[ii] + "\r\n";
			if(m_complexity[ii] > 0)
			{
			//str += " Complexity = " + m_complexity[ii] + "\r\n";
			//str += "Parallelness= " + m_parallelness[ii] + "\r\n";
			str += "   Min Axis = " + m_minLength[ii] + "\r\n";
			str += "   Max Axis = " + m_maxLength[ii] + "\r\n";
			double rr = m_minLength[ii]/m_maxLength[ii];
			str += "Short/Long  = " + rr + "\r\n";
			}
		}
			
		return str;
	}
	//void AddObject(double area, int nSize, double roundness, double aveIntensity)
	void AddObject(PolygonInfo& stInfo)
	{
		m_vsTime.Add(strCurrentTime());
		m_vfAreas.Add(stInfo.area);
		m_vnPts.Add(stInfo.nSize);
		m_vfRoundness.Add(stInfo.roundness);
		m_vfMeanIntensity.Add(stInfo.aveIntensity);
		m_vfSDIntensity.Add(stInfo.IntensitySD);
		m_complexity.Add(stInfo.complexity);
		m_parallelness.Add(stInfo.parallelness);
		m_minLength.Add(stInfo.minLength);
		m_maxLength.Add(stInfo.maxLength);
	}
private:
	vector<int> 	m_vnPts;
	vector<double>	m_vfAreas;
	vector<double>	m_vfRoundness;
	vector<double>	m_vfMeanIntensity;
	vector<double>	m_vfSDIntensity;
	vector<double>	m_complexity;
	vector<double>	m_parallelness;
	vector<double>	m_minLength;
	vector<double>	m_maxLength;
};

class FoundObjList : public InfoObjList
{
public:
	void AddObject(int nCount, vector<float>& vfLevels)
	{
		m_vsTime.Add(strCurrentTime());
		m_vnCount.Add(nCount);
		int nLC = vfLevels.GetSize();
		m_vnL1.Add((int) vfLevels[0]);
		m_vnL2.Add((int) vfLevels[nLC-1]);
		m_vnLCount.Add(nLC);
	}
	string MakeDisplayStr()
	{
		string str;
		for(int ii = 0; ii < m_vsTime.GetSize(); ii++)
		{
			str += addTimeStamp(ii, false);
			str += "Search range = " + m_vnL1[ii] + " to " +  m_vnL2[ii] + ", total of " + m_vnLCount[ii] + " levels\r\n";
			str += "Num Found = " + m_vnCount[ii] + "\r\n";
		}
			
		return str;
	}
private:
	vector<int> m_vnCount;
	vector<int> m_vnL1;
	vector<int> m_vnL2;
	vector<int> m_vnLCount;
};

	
// info display
enum {IMSG_INFO_IMAGE, IMSG_INFO_FINDINGS, IMSG_INFO_SELECTION};
 
class ImageSegmentDlg : public TreeEditDlg
{
public:
	ImageSegmentDlg() : TreeEditDlg( IDD_IMGOBJ_DLG, "oDlg8" )
	{
		m_bHasActivatedSearching = false;
	}
	// maybe not usful, but keep this for testing
	DWORD	DoModalEx(HWND hParent)
	{
		InitMsgMap();// will be called from internal later
		invalidateStats();
		int nRet = DoModal(hParent);
		if(IDOK == nRet)
		{
			return 1;
		}
		return 0;
	}
	
	virtual int  Create(HWND hParent = NULL)
	{
		InitMsgMap();
		invalidateStats();
		//Launch the modeless dialog
		int nRet = TreeEditDlg::Create(hParent);
		
		return nRet;
	}

protected:
	
EVENTS_BEGIN
	ON_INIT( OnInitDialog )
	ON_DESTROY(OnDestroy)
	ON_CHANGE_LAYER( OnActiveLayerChange )
	ON_CHANGE_PAGE( OnActivePageChange )
	ON_CHANGE_SELECTION( OnSelectionChange )
	ON_SLIDE( IDC_IMGOBJ_LEVEL_SLIDER, OnContourLevelSlide )
	ON_BN_CLICKED(IDC_IMGOBJ_SCAN_BTN, OnScanLevels)
	ON_BN_CLICKED(IDC_IMGOBJ_GETDATA_BTN, OnConvertTo8BitsData)
	ON_BN_CLICKED(IDC_IMGOBJ_SEARCH_BTN, OnSearchAgain)
	ON_BN_CLICKED(IDC_IMGOBJ_REPORT_BTN, OnCreateReport)
	
	ON_BN_CLICKED(IDC_IMGOBJ_MORE, OnShowMoreOptions)
	
	ON_SIZE(OnDlgResize)
	
	ON_GETNDLG_MSGS(IDC_IMGOBJ_OPTIONS)
	
	ON_TAB_SEL_CHANGE(IDC_IMGOBJ_INFO_TABS, OnInfoTabChange)
	
EVENTS_END

	BOOL OnInitDialog()
	{
		vector<string>  vstrTipsUpDown;
		vstrTipsUpDown.SetSize(2);
		vstrTipsUpDown[0] = _L("Show More Options");
		vstrTipsUpDown[1] = _L("Hide More Options");
		TreeEditDlg::OnInitDialog(IDC_IMGOBJ_INFO_TABS, IDC_IMGOBJ_MORE, IDC_IMGOBJ_SEARCH_GROUP, vstrTipsUpDown, "ImageSegmentDlg");

		initParamTree();

		PEVENT_FUNC pfn = NULL;//getEventHandler();
		m_treeEditCntrl.Init(IDC_IMGOBJ_OPTIONS, false, true, pfn, *this);

		m_sldrContourLevel = GetItem(IDC_IMGOBJ_LEVEL_SLIDER);
		m_sldrContourLevel.RangeMin = 0;
		m_sldrContourLevel.RangeMax = LEVEL_SLIDE_MAX;
		
		m_tabInfo = GetItem(IDC_IMGOBJ_INFO_TABS);
		m_tabInfo.InsertItem(IMSG_INFO_IMAGE,"Image Info");
		m_tabInfo.InsertItem(IMSG_INFO_FINDINGS, "Search Results");
		m_tabInfo.InsertItem(IMSG_INFO_SELECTION, "Selection Info");
		
		m_editStart = GetItem(IDC_IMGOBJ_LEVEL);
		m_editRange = GetItem(IDC_IMGOBJ_LEVEL_RANGE);
		m_editRange.Text = "20";
		m_editInfo = GetItem(IDC_IMGOBJ_INFO);
		m_editInfo.SetFont(ANSI_FIXED_FONT);
		
		m_groupBox = GetItem(IDC_IMGOBJ_SEARCH_GROUP);
		checkPageUpdateStats();
		checkPageLoadSettings();
		updateDlgSize();
		ShowWindow(SW_NORMAL);
		return TRUE;
	}
	BOOL OnDestroy(void)
	{
		checkPageSaveSettings();
		TreeEditDlg::OnDestroy();
		return true;
	}
	void OnRowColChange(Control cntrl)
	{
		if(!IsInitReady())
			return;
		TreeEditDlg::OnRowColChange(cntrl);
	}	

	BOOL OnInfoTabChange(Control ctrl)
	{
		if(!IsInitReady())
			return TRUE;
		int nType = m_tabInfo.GetCurSel();
		updateInfoDisplay(nType);
		return TRUE;
	}
	BOOL OnContourLevelSlide( Control ctrl, UINT nCode, UINT nPos )
	{
		if(!IsInitReady())
			return TRUE;
		double dbLevel = setContourLevel(m_sldrContourLevel.Position);
		if(SB_ENDSCROLL == nCode && !is_missing_value(dbLevel))
			find_objects(0);
		
		if(SB_ENDSCROLL == nCode)
			updateControls();// for SearchAgain button
		return TRUE;
	}
	BOOL OnDlgResize(int nType, int cx, int cy)
	{
		if(!IsInitReady())
			return TRUE;
		
		uint nButtonIDs[] = {IDOK, IDC_IMGOBJ_GETDATA_BTN, IDC_IMGOBJ_REPORT_BTN, IDC_IMGOBJ_SEARCH_BTN, 0};
		RECT rect;
		m_groupBox.GetWindowRect(&rect);
		ScreenToClient(&rect);
		int ny = rect.top + 2;
		//ArrangeMainItemAndControls(nButtonIDs, IDC_IMGOBJ_INFO_TABS, true, 0, ny);	///Danice DLG_SIZE_CONSIGER_RIGHT_BOTTOM_BTN : change in ResizeDialog
		ArrangeMainItemAndControls(nButtonIDs, IDC_IMGOBJ_INFO_TABS, NULL, true, 0, ny);
		moveInfoBoxInsideTab();
		ResizeMoveControlsRightBottom(IDC_IMGOBJ_SEARCH_GROUP, NULL, NULL, cx, cy);
		if(!isAdvancedOptions())
			return TRUE;
		ResizeMoveControlsRightBottom(IDC_IMGOBJ_OPTIONS, NULL, NULL, cx, cy);
		return TRUE;
	}
	// get the information of GraphLayer
	BOOL OnActiveLayerChange( void )
	{
		if(!IsInitReady())
			return TRUE;
		//out_str("OnActiveLayerChange");
		checkLayerUpdateDisplay();	
		return TRUE;
	}
	// get the name of the page
	BOOL OnActivePageChange( void )
	{
		if(!IsInitReady())
			return TRUE;
		//--- CPY 7/18/04 SAVE_SETTING_SWITCH_WINDOW
		checkPageSaveSettings(true);
		//----
		checkPageUpdateStats();
		//--- CPY 7/18/04 SAVE_SETTING_SWITCH_WINDOW
		//out_str("OnActivePageChange");
		checkPageLoadSettings();
		//---
		return TRUE;
	}
	// change the selection 
	BOOL OnSelectionChange(void)
	{
		if(!IsInitReady())
			return TRUE;
		//out_str("OnSelectionChange");
		foreach(OriginObject obj in Selection.Objects)
		{
			PolyPolylineGraphObject plobj;
			plobj = obj;
			if( !plobj )
				continue;
			
			//double area, round, aveIntensity =0;
			//int nSize;
			PolygonInfo st = {0};
			vector<int>	vInternalSelection;
			plobj.GetInternalSelection(vInternalSelection);
			for( int ii = 0; ii < vInternalSelection.GetSize(); ii++ )
			{
				vector xx;
				vector yy;
				plobj.GetPoints(xx, yy, vInternalSelection[ii]);
				if( getPolylineInfo(xx, yy, st.area, st.nSize, st.roundness, st.aveIntensity, st.IntensitySD) )	
				{
					double dDistADM;
					vector<float> xx_f;
					vector<float> yy_f;
					xx_f = xx;
					yy_f = yy;
					polygon_complexity(xx_f, yy_f, xx.GetSize(), &st.complexity, &st.minLength, &st.maxLength, &dDistADM, NULL, NULL, 5);
					
					st.parallelness = st.minLength / dDistADM;
					
					checkSavePolygon(xx_f, yy_f);
					
					m_selObjs.AddObject(st);
					updateInfoDisplay(IMSG_INFO_SELECTION);
				}
			}
		}

		return TRUE;
	}
	
	BOOL OnCreateReport(Control cntrl);
	BOOL OnSearchAgain(Control cntrl)
	{
		find_objects();
		return TRUE;
	}
	BOOL OnScanLevels(Control cntrl);
	BOOL OnConvertTo8BitsData(Control cntrl);
	BOOL OnShowMoreOptions(Control cntrl)
	{
		updateShowOptions();
		updateDlgSize(true);
		return TRUE;
	}
private:
	void checkLayerUpdateDisplay()
	{
		MatrixLayer mLayer = Project.ActiveLayer();
		if(mLayer)
		{
			updateInfoDisplay(IMSG_INFO_IMAGE);
			updateControls();
		}
		else
			updateControls(false);
	}
	void checkPageUpdateStats();
	double getAveIntensity(vector<float>& xx, vector<float>& yy, double& SD);
	bool getPolylineInfo(vector& xx, vector& yy, double& area, uint& nSize, double& roundness, double& aveIntensity, double& SDIntensity);
	
	double getContourLevel(int nRange = 0, double* pEndVal = NULL, int* pnSteps = NULL, int* pOnSearch = NULL, int* pColor = NULL)
	{
		double dStart = NANUM;
		double dEnd = NANUM;
		int	nSteps;
		int nOnSearch = ON_SEARCH_REPLACE;
		if(isAdvancedOptions())
		{
			updateParams(false);// grid->dlg
			TreeNode trRange = getSearchRange(nRange);
			dStart = trRange.min.dVal;
			dEnd = trRange.max.dVal;
			nSteps = trRange.searchLevels.nVal;
			nOnSearch = trRange.OnSearch.nVal;
			if(pColor)
				*pColor = trRange.Color.nVal;
		}
		else
		{
			if(!m_editStart.Text.IsEmpty())
			{
				TreeNode trRange = getSearchRange(0);
				dStart = atof(m_editStart.Text);
				dEnd = dStart + getContouringRange();
				nSteps = trRange.searchLevels.nVal;
				nOnSearch = ON_SEARCH_REPLACE;
			}
		}		
		if(!is_missing_value(dStart) && (dStart < m_statsData.min || dStart > m_statsData.max))
			dStart = NANUM;
		
		if(pEndVal)
			*pEndVal = dEnd;
		if(pnSteps)
			*pnSteps = nSteps;
		if(pOnSearch)
			*pOnSearch = nOnSearch;
		return dStart;
	}
	double getPercentMin() { return 0; }
	double getPercentMax() { return 100;}
	
	void getSliderPercentRange(double& dMin, double& dMax)
	{
		double pctMin = getPercentMin();//m_paramTree.LevelSlider.fromPercent.dVal;
		double pctMax = getPercentMax();//m_paramTree.LevelSlider.toPercent.dVal;
		if(pctMax == pctMin) pctMax = pctMin + 1;
		dMin = pctMin;
		dMax = pctMax;
	}
	double getSliderFraction(double dPos)
	{
		double pctMin, pctMax;
		getSliderPercentRange(pctMin, pctMax);
		dPos = dPos / LEVEL_SLIDE_MAX;
		return pctMin/100.0 + dPos * (pctMax - pctMin) /100.0;
	}
	double percentToRange(double pct)
	{
		return pct * (m_statsData.max - m_statsData.min)/100.;
	}
	double rangeToPercent(double dRange)
	{
		return 100.0 * dRange / (m_statsData.max - m_statsData.min);
	}
	double percentToLevel(double pct)
	{
		return m_statsData.min + pct * (m_statsData.max - m_statsData.min) / 100.0;
	}
	double levelToPercent(double dLevel)
	{
		return 100.0 * (dLevel - m_statsData.min)/(m_statsData.max - m_statsData.min);
	}
	void setSliderPos(double dLevelVal)
	{
		double pctMin, pctMax;
		getSliderPercentRange(pctMin, pctMax);
		double pctLevel = levelToPercent(dLevelVal);
		double fraction = (pctLevel - pctMin) / (pctMax - pctMin);
		int nVal = LEVEL_SLIDE_MAX * fraction;
		m_sldrContourLevel.Position = nVal;
		
	}
	void updateLevelRangeInGrid(double dBegin, double dEnd)
	{
		/*
		// show Slider Percent Range only if 16bits
		if(2==m_nDataType && m_paramTree.LevelSlider.Show == 0)
		{
			m_paramTree.LevelSlider.SetAttribute(STR_CHANGED_ATTRIB, 2);
			m_paramTree.LevelSlider.Show = true;
			m_paramTree.LevelSlider.fromPercent.dVal = 5;
			m_paramTree.LevelSlider.toPercent.dVal = 95;
		}
		else if(1 == m_nDataType && m_paramTree.LevelSlider.Show)
		{
			m_paramTree.LevelSlider.SetAttribute(STR_CHANGED_ATTRIB, 2);
			m_paramTree.LevelSlider.Show = false;
			m_paramTree.LevelSlider.fromPercent.dVal = 0;
			m_paramTree.LevelSlider.toPercent.dVal = 100;
		}
		*/
		double pctMin, pctMax;
		getSliderPercentRange(pctMin, pctMax);
		string strSlideRange;
		double dMin = percentToLevel(pctMin);
		double dMax = percentToLevel(pctMax);
		double dInc;
		int nSteps = 255;
		RoundLimits(&dMin, &dMax, &dInc, nSteps);
		if(dInc < 1)
		{
			dInc = 1;
			RoundLimits(&dMin, &dMax, &dInc, 0);
		}
		nSteps = (dMax - dMin)/dInc;
			
		if(dBegin < dMin) dBegin = dMin;
		if(dEnd > dMax) dEnd = dMax;
		string strTemp = range_to_str(dMin, dMax, nSteps);
		TreeNode trRange = getSearchRange();
		setTreeNodeVal(trRange.min, dBegin, strTemp);
		setTreeNodeVal(trRange.max, dEnd, strTemp);
		m_treeEditCntrl.UpdateGridValues(false, true);// no need to resize cell width as it should not be needed
	}
	void updateParams(bool bDlgToGrid)
	{
		if(bDlgToGrid)
		{
			double dStart = atof(m_editStart.Text);
			double dEnd = dStart + getContouringRange(true);
			updateLevelRangeInGrid(dStart, dEnd);
		}
		else // update controls that are on the main dlg
		{
			TreeNode trRange = getSearchRange();
			double dStartLevel = trRange.min.dVal;
			m_editStart.Text = ftoa(dStartLevel);
			setSliderPos(dStartLevel);
			double dRange = 100.0 * (trRange.max.dVal - trRange.min.dVal)/(m_statsData.max - m_statsData.min);
			if(dRange < 5)
				dRange = 5;
			
			m_editRange.Text = ftoa(dRange);
		}
	}
	//dbEndVal is valid only if bValInSliderPos = false 
	double setContourLevel(double dbVal, bool bValInSliderPos = true, bool bFromSlider = true, double dbEndVal = 0)
	{
		if(bValInSliderPos) // dbVal is slider pos, and dbEndVal not specified
		{
			double dbFraction = getSliderFraction(dbVal);
			dbVal = m_statsData.min + (m_statsData.max - m_statsData.min)*dbFraction;
			dbEndVal = dbVal + getContouringRange(true);
		}
		dbVal = floor(dbVal);// this is because we work mostly with integer matrix data
		dbEndVal = ceil(dbEndVal);// convert to upper side to be int
		if(!bFromSlider)
			setSliderPos(dbVal);
		
		if(isAdvancedOptions())
		{
			updateLevelRangeInGrid(dbVal, dbEndVal);
		}
		else
			m_editStart.Text = dbVal;
		return getContourLevel();
	}
	void setTreeNodeVal(TreeNode& trNode, double dbVal, LPCSTR lpcszNewComboStr = NULL)
	{
		string strOldVal = trNode.strVal;
		trNode.dVal = dbVal;
		trNode.SetAttribute(STR_CHANGED_ATTRIB, strOldVal);
		if(lpcszNewComboStr)
		{
			string strOldCombo;
			if(trNode.GetAttribute(STR_COMBO_ATTRIB, strOldCombo) && strOldCombo.CompareNoCase(lpcszNewComboStr) != 0)
			{
				trNode.SetAttribute(STR_COMBO_ATTRIB, lpcszNewComboStr);
				trNode.SetAttribute(STR_COMBO_CHANGED, strOldCombo);
			}
		}
	}
	double getContouringRange(bool bFromDlgOnly = false)
	{
		if(!bFromDlgOnly && isAdvancedOptions())
			updateParams(false);// grid->dlg
		
		int nRange = atoi(m_editRange.Text);// this is always in percent
		double pct = nRange > 0? nRange: 1;
		return percentToRange(pct);
	}
	TreeNode getSearchRange(int nRange = 0)
	{
		if(nRange > 0)// support only 3, may have more later
		{
			if(2 == nRange)
				return m_paramTree.Range3;
			
			return m_paramTree.Range2;
		}
		else
			return m_paramTree.Range;
	}
	int getNumContouringLevels(int nRange = 0) // this is in grid only
	{
		TreeNode trRange = getSearchRange(nRange);
		return trRange.searchLevels.nVal;//atoi(m_cbNumLevels.Text);
	}
	void initParamTree();
	int getMinSize()
	{
		return m_paramTree.Limits.minPts.nVal;
	}
	bool isConsolidateRelatedContoursToOne()
	{
		return m_paramTree.Consolidate.nVal; //m_chkConsolidate.Check;
	}
	double getMinRoundness()
	{
		double ff = m_paramTree.Limits.minRoundness.dVal;
		if(ff < 0)
			ff = 0.1;
		if(ff > 2)
			ff = 2;
		return ff;
	}
	bool find_image(float& fMin, float& fMax)
	{
		MatrixLayer ml = getMatrixLayer();
		updateStats(false);
		if(!isStatsValid())
		{
			out_str("please convert image to data first");
			return false;
		}
		// especially for bytes, must smooth first
		//smooth_matrix(mm);
		fMin = m_statsData.min;
		fMax = m_statsData.max;
		return true;
	}
	//fOffsetLevels is to make contouring routine to avoid having grid value exactly the same as contour level, which will result in
	// dealoop in the searching routine. This is really to assumet that grid values are really ints
	void getContourLevels(double fMinPos, double fMaxPos, int nSearchLevels, vector<float>& fLevels, float fOffsetLevels = 1E-4)
	{
		//int nSearchLevels = getNumContouringLevels();
		fLevels.SetSize(nSearchLevels);
		if(fMinPos == 0 && fMaxPos <= fMinPos)
		{
			fLevels.SetSize(0);
			return;
		}
		if(nSearchLevels > 1)
		{
			float finc = (fMaxPos - fMinPos)/(float)(fLevels.GetSize() - 1);
			for(int ii = 0; ii < fLevels.GetSize(); ii++)
				fLevels[ii] = ii*finc + fMinPos + fOffsetLevels;
			//printf("Searching countours from %f to %f\n", fLevels[0], fLevels[fLevels.GetSize()-1]);
		}
		else if(1==nSearchLevels)
		{
			fLevels[0] = fMinPos + fOffsetLevels;
			//printf("Searching countours at %f\n", fLevels[0]);
		}
	}
	int find_objects(TreeNode& tr, vector<float>& fLevels, int nRange, DWORD dwOptions, int nColor = -1);
	void find_objects(int nRange = -1);
	void searchUpdateMatrixROI(MatrixLayer& ml, LPCSTR lpcszROIName, vector<float>& fLevels, int nRange, DWORD dwOptions, int nColor = -1)
	{
		Tree	trTemp;
		int nFound = find_objects(trTemp, fLevels, nRange, dwOptions, nColor);
		if( nFound > 0 )
		{
			ml.CreateUpdatePolypolygon(lpcszROIName, trTemp);
			if(fLevels.GetSize() > 0)
				m_foundObjs.AddObject(nFound, fLevels);
		}
		else
		{
			ml.CreateUpdatePolypolygon(lpcszROIName); // no contours found - destroy corresponding ROI
		}
	}
	void invalidateStats()
	{
		m_nDataType = 0; // 1 or 2 bytes only, all others are invalid
		m_statsData.min = NANUM;
	}
	bool isStatsValid()
	{		
		return is_missing_value(m_statsData.min)? false:true;
	}
	// if true, we analyze and decide which to enable
	// if false, then all disabled
	void updateControls(bool bEnable = true)
	{
		uint ucHasGoodData[] = {IDC_IMGOBJ_LEVEL_SLIDER, IDC_IMGOBJ_SCAN_BTN, IDC_IMGOBJ_REPORT_BTN, IDC_IMGOBJ_MORE, 
			IDC_IMGOBJ_LEVEL, IDC_IMGOBJ_LEVEL_TEXT, 
			IDC_IMGOBJ_LEVEL_RANGE, IDC_IMGOBJ_LEVEL_RANGE_TEXT,
		0};
		uint ucAdvanced[] = {IDC_IMGOBJ_OPTIONS, 0};
			
		uint ucConvertData[] = {IDC_IMGOBJ_GETDATA_BTN, IDC_IMGOBJ_SEARCH_BTN, 0};
		
		if(bEnable)
		{
			bool bHasGoodData = isStatsValid()? true:false;
			EnableControls(ucHasGoodData, bHasGoodData);
			EnableControls(ucConvertData, true);

			//if(m_nDataType == 1 || m_nDataType == 2)
			if(m_nDataType == 1)
				GetItem(IDC_IMGOBJ_GETDATA_BTN).Enable = false;
			
			if(is_missing_value(getContourLevel()))
				GetItem(IDC_IMGOBJ_SEARCH_BTN).Enable = false;
		}
		else
		{
			EnableControls(ucConvertData, false);
			EnableControls(ucHasGoodData, false);
		}
	}
	void updateShowOptions(bool bTransferSettings = true)
	{
		uint ucSimple[] = {IDC_IMGOBJ_LEVEL_SLIDER, IDC_IMGOBJ_SCAN_BTN,
			IDC_IMGOBJ_LEVEL, IDC_IMGOBJ_LEVEL_TEXT, 
			IDC_IMGOBJ_LEVEL_RANGE, IDC_IMGOBJ_LEVEL_RANGE_TEXT,
		0};
		bool bShowAdv = isAdvancedOptions()? true:false;
		
		ShowControls(ucSimple, bShowAdv? false : true);
		if(bTransferSettings)
			updateParams(bShowAdv);
			
		m_treeEditCntrl.SetVisible(bShowAdv);
	}
	void updateDlgSize(bool bFromButton = false)
	{
		bool bIsMoreOptions = isAdvancedOptions();
		if(bIsMoreOptions)
			MoveControlToBottomOfGroup(IDC_IMGOBJ_SEARCH_GROUP, IDC_IMGOBJ_OPTIONS, IDC_IMGOBJ_MORE, GetBottomPaneHeight());
		else
		{
			if(bFromButton)
				SetBottomPaneHeight(m_treeEditCntrl.GetHeight()); // save it for later when expand is needed
			
			MoveControlToBottomOfGroup(IDC_IMGOBJ_SEARCH_GROUP, 0, IDC_IMGOBJ_LEVEL_RANGE);
		}

		ResizeDlgToBottomOfControl(IDC_IMGOBJ_SEARCH_GROUP);
	}
	MatrixLayer getMatrixLayer()
	{
		Page pg(m_strMatrix);
		if(!pg)
			return NULL;
		
		return pg.Layers();// assume 1st layer
	}
	bool isValidMatrixData()
	{
		Page pg(m_strMatrix);
		if(!pg)
			return false;
		MatrixLayer mlay = getMatrixLayer();
		if(!mlay)
			return false;
		if(!mlay.HasData())
			return false;
		return true;
	}
	
	bool updateStats(bool bUpdateContourStartLevel = true)
	{
		if(!isValidMatrixData())
		{
			invalidateStats();
			return false;
		}
		if(isAdvancedOptions())
			updateParams(false);// so all will be in percent
		
		double	sum;
		UINT	nSize = 0;
		UINT	nMin, nMax;
		MatrixLayer ml = getMatrixLayer();
		if(ml.GetInternalData() == FSI_USHORT)
		{
			Matrix<ushort> mus(m_strMatrix);
			if(mus)
			{
				USHORT usmin, usmax;
				m_nDataType = 2;
				m_nRows = mus.GetNumRows();
				m_nCols = mus.GetNumCols();
				nSize = m_nRows * m_nCols;
				sum = ocmath_us_sum(mus,nSize, &usmin, &usmax, &nMin, &nMax);
				m_statsData.min = usmin;
				m_statsData.max = usmax;
				m_mfImageData = mus;
			}
		}
		else if(ml.GetInternalData() == FSI_BYTE)
		{
			Matrix<byte> mb(m_strMatrix);
			if(mb)
			{
				byte bmin, bmax;
				m_nDataType = 1;
				m_nRows = mb.GetNumRows();
				m_nCols = mb.GetNumCols();
				nSize = m_nRows * m_nCols;
				sum = ocmath_b_sum(mb, nSize, &bmin, &bmax, &nMin, &nMax);
				m_statsData.min = bmin;
				m_statsData.max = bmax;
				m_mfImageData = mb;
			}
		}
		if(0 == nSize)
		{
			invalidateStats();
			return false;
		}
		m_statsData.mean = sum/nSize;
		m_statsData.N = nSize;
		m_statsData.Missing = 0;
		m_statsData.iMax = nMax;
		m_statsData.iMin = nMin;
		if(bUpdateContourStartLevel)
		{
			setContourLevel(m_sldrContourLevel.Position, true, false);
		}
		return true;
	}
	string makeImageInfoStr()
	{
		string strInfo;
		Page pg(m_strMatrix);
		if(pg)
		{
			MatrixLayer mlay = getMatrixLayer();
			strInfo = m_strMatrix + " (RC=" + mlay.GetNumRows() + "x" + mlay.GetNumCols() + ")\r\n";
			strInfo += pg.Label + "\r\n------------------------\r\n";
			if(mlay)
				strInfo += makeImageStatsInfoStr();

		}
		return strInfo;
	}
	string makeInfoStr(int nType)
	{
		switch(nType)
		{
		case IMSG_INFO_IMAGE:
			return makeImageInfoStr();
		case IMSG_INFO_FINDINGS:
			return m_foundObjs.MakeDisplayStr();
		case IMSG_INFO_SELECTION:
			return m_selObjs.MakeDisplayStr();
		}
		string strEmpty;
		return strEmpty;
	}
	void updateInfoDisplay(int nType)
	{
		string strInfo = makeInfoStr(nType);
		setInfoDisplayType(nType);
		m_editInfo.Text = strInfo;
		m_editInfo.SetSel(32000, 32000); // move to end of all text
	}
	string makeImageStatsInfoStr()
	{
		string str;
		if(isStatsValid())
		{
			str  = "    Min = " + db2str(m_statsData.min);
			str += "    Max = " + db2str(m_statsData.max);
			str += "   Mean = " + db2str(m_statsData.mean);
		}
		return str;
	}
	string db2str(double vv, bool bAddCRLF = true)
	{
		char szTemp[MAXLINE];
		int nDigits = 3 + m_nDataType; // 4 or 5
		string strFmt = "*" + nDigits + "*";
		DoubleToStr(vv, szTemp, MAXLINE, strFmt);
		if(bAddCRLF)
		{
			string str = szTemp;
			str += "\r\n";
			return str;
		}
		return szTemp;
	}
	void smooth_matrix(matrix<float>& mf, double fN = 0.2, double fD = 0.15)
	{
		// the following cannot work
		/*
		matrix mfilter = {
		{fD, fN, fD},
		{fN, 1,  fN},
		{fD, fN, fD}
		};
		*/
		matrix mfilter(3,3);
		mfilter = fN;
		mfilter[0][0] = fD; // 0 1 0
		mfilter[2][0] = fD; 
		mfilter[0][2] = fD; // 0 1 0
		mfilter[2][2] = fD;
		mfilter[1][1] = 1;
		mf.ApplyFilter(mfilter, 1);
	}
	void moveInfoBoxInsideTab()
	{
		RECT rect;
		m_tabInfo.GetWindowRect(&rect);
		ScreenToClient(&rect);
		m_tabInfo.AdjustRect(FALSE, &rect);
		m_editInfo.MoveWindow(&rect);
	}
	bool isAdvancedOptions() {return IsBottomPaneShown();}
	
	int	getInfoDisplayType();
	void setInfoDisplayType(int nType);
	bool isCombineRanges()
	{
		if(!isAdvancedOptions())
			return false;
		
		return m_paramTree.CombineRanges.nVal > 0? true:false;
	}
	void checkPageLoadSettings(bool bUpdateDisplay = true);
	void checkPageSaveSettings(bool bSwitchWin = false);
	bool savePageSettings(const string& strPageName);
	void checkSavePolygon(vector<float>& vx, vector<float>& vy)
	{
		Worksheet wks("ROI");
		if(wks)
		{
			Dataset aa(wks, 0);
			Dataset bb(wks, 1);
			aa = vx;
			bb = vy;
		}
	}
private:
	PolygonObjList	m_selObjs;
	FoundObjList	m_foundObjs;

	Slider	m_sldrContourLevel;
	Edit	m_editStart;
	Edit	m_editRange;
	Edit	m_editInfo;
	TabControl m_tabInfo;
	Control	m_groupBox;
	string	m_strMatrix;
	BasicStats	m_statsData;
	int		m_nDataType;
	int		m_nRows;
	int		m_nCols;
	matrix<float> m_mfImageData;// save this to save time, no need to copy every time doing a search
	string	m_strPageOldSettings;
	bool	m_bHasActivatedSearching;
};
