#ifndef _IMG_L_PROFILE_H
#define _IMG_L_PROFILE_H

enum {
	PROFILE_REF_LINE = 0,
	PROFILE_CTRL_LINE,
};

///Sandy add 2006-12-12
#define IMGLP_ROI_RECT_NAME "imgLPRoiRect"
///end


class LProfile
{
public:
	LProfile(MatrixObject& mo);
	~LProfile();
	
	bool ResetAll(int nLineNum, int nReferenceLineNum = 0, int nLineWidth = 1, int nChannelMode = CHANNEL_MODE_INTENSITY);
	bool UpdateLine(string strLineName);

	///add by sandy 2006-12-12
	void ShowROIRect(bool bShowROI);
	void UpdateROIRect(bool bFromML = true);
	///end

	int GetNumChannel();
private:
	void resetAllNumAndPosLines(int nNumRefLine, int nNumCtrlLine, bool& bIsNumChanged);
	void rebuildAllLineAsPosition();
	bool updateAllProfile(bool bReconstructData = true);
	int sortCtrlLines(bool& bOrderChanged);
	
	// nLineIndex = [input] -1 means reference line
	bool updateProfile(int nLineIndex);

	void resetNumAndPosLines(vector& vPosLines, int nLineNum, bool& bIsNumChanged, int nLineType = PROFILE_CTRL_LINE);
	void createLinesAsPosition(vector<string>& vsLineName, const vector& vPosLine, int nLineType = PROFILE_CTRL_LINE);
	bool addLine(vector& vPosLines, int nLineType = PROFILE_CTRL_LINE);
	bool deleteLine(vector& vPosLines);
	bool createNewLine(GraphObject& go, double x0, double y0, int nLineIndex, bool bRefLine = false, int nWidth = 1, int nDir = LN_VERTICAL, bool bFixW = true);
	
	// nIndex = [input] 0 means update all position of reference and control lines
	bool updatePos(int nIndex);
	bool updateAllPos(vector& vPos, const vector<string>& vsLineNames);
	int getIndex(const GraphObject& goLine);
	bool setIndex(GraphObject& goLine, int nIndex);
	
	double getLinePosition(const GraphObject& goLine);
	double getNewOffset(const vector& vPosLines, double dMin, double dMax);
	
	// return name of new line object when current input line object is replaced with the new one
	//string resetWidth(string strLineName, int nLineWidth);
	//bool updateAllLineWidth();
	
	bool getLineProfile(const GraphObject& goLine, vector& vIntensity, int nChannelMode);
	
	///Sandy 2006-12-12 ROI_RECTANGLE
	bool setImageROIRect(int xLeft, int xRight, LPCSTR lpcszName = IMGLP_ROI_RECT_NAME, int nDir = LN_HORIZONTAL);
	void destroyImageROIRect( LPCSTR lpcszName = IMGLP_ROI_RECT_NAME);
	///end
	
private:
	imgLProfileData	m_LProfileData;
	imgChannelData	m_ImgChannel;
	
	MatrixLayer m_ml;
	MatrixObject m_moImg;
	
	int m_nNumChannel;
	int m_nChannelMode;
	int m_nLineWidth;
	
	vector<string> m_vsRefLine;
	vector<string> m_vsControlLine;
	
	vector m_vPosRefLine;
	vector m_vPosControlLine;
	
	vector<string> m_vsChName;
	
	double	m_dROILeft, m_dROIRight;
};

LProfile::LProfile(MatrixObject& mo)
{
	m_nNumChannel = 0;
	m_nChannelMode = -1;
	m_nLineWidth = -1;
	
	m_vsRefLine.RemoveAll();
	m_vsControlLine.RemoveAll();
	
	m_vPosRefLine.RemoveAll();
	m_vPosControlLine.RemoveAll();
	
	m_moImg = mo;
	mo.GetParent(m_ml);
	
	m_vsChName.Add(WKS_INTENSITY_COL_PREFIX);
	m_vsChName.Add(WKS_RED_COL_PREFIX);
	m_vsChName.Add(WKS_GREEN_COL_PREFIX);
	m_vsChName.Add(WKS_BLUE_COL_PREFIX);
	
	m_nNumChannel = m_ImgChannel.SeparateChannelData(mo, m_vsChName);
	
	m_LProfileData.ResetAll(0, 0, 0, CHANNEL_MODE_INTENSITY, true);
	
	m_dROILeft = NANUM;
	m_dROIRight = NANUM;
}

LProfile::~LProfile()
{
	ShowROIRect(false);
	ResetAll(0, 0);
}

///////////////////////////////////////
// public member function
///////////////////////////////////////
bool LProfile::ResetAll(int nLineNum, int nReferenceLineNum, int nLineWidth, int nChannelMode) // = 0, 1, CHANNEL_MODE_INTENSITY
{
	bool bReconstructData;
	bool bIsNumLineChanged, bIsWidthChanged, bIsChannelModeChanged;
	
	resetAllNumAndPosLines(nReferenceLineNum, nLineNum, bIsNumLineChanged); // only setup position, not create new line
	
	bIsWidthChanged = ( m_nLineWidth != nLineWidth );
	m_nLineWidth = nLineWidth;
	
	if ( bIsNumLineChanged || bIsWidthChanged )
		rebuildAllLineAsPosition();  // create new lines according to ready position
		
	bIsChannelModeChanged |= (m_nChannelMode != nChannelMode);
	m_nChannelMode = nChannelMode;
	
	bReconstructData = ( bIsNumLineChanged || bIsChannelModeChanged );
	updateAllProfile(bReconstructData);
	
	UpdateROIRect(true);
	
	return true;
}

bool LProfile::UpdateLine(string strLineName)
{
	GraphObject goLine = m_ml.GraphObjects(strLineName);
	if ( !goLine )
		return false;

	int nIndex = getIndex(goLine);
	if ( 0 == nIndex )
		return error_report("Invalid line index");
	
	updatePos(nIndex);
	
	if ( -1 == nIndex )
	{	// reference line
		updateProfile(nIndex);
	}
	else
	{	// control line
		bool bOrderChanged;
		if ( sortCtrlLines(bOrderChanged) )
		{
			if ( bOrderChanged )
				updateAllProfile(false);
			else
				updateProfile(nIndex);
		}
	}
	
	UpdateROIRect(true);
	
	return true;
}

int LProfile::GetNumChannel()
{
	return m_nNumChannel;
}

///Sandy 2006-12-12 ROI_RECTANGLE
void LProfile::ShowROIRect(bool bShowROI)
{
	int xLeft, xRight; 
	if(bShowROI)
	{
		//m_LProfileData.GetROILinesX(xLeft, xRight);
		if(m_dROILeft == NANUM || m_dROIRight == NANUM)
		{
			m_dROILeft = m_moImg.GetNumRows()/3;
			m_dROIRight = m_moImg.GetNumRows()*2/3;
		}
		setImageROIRect(m_dROILeft, m_dROIRight);
	}
	else
	{
		destroyImageROIRect();
		m_dROILeft = m_dROIRight = NANUM;
	}
	//UpdateROIRect(true);
	m_LProfileData.UpdateROI(m_dROILeft, m_dROIRight);
}

void LProfile::UpdateROIRect(bool bFromML)
{
	//double dLeft, dRight;
	if(bFromML)
	{
		GraphObject go = m_ml.GraphObjects(IMGLP_ROI_RECT_NAME);
		if(go)
			get_thick_line_width(go, &m_dROILeft, &m_dROIRight);
		else
			m_dROILeft = m_dROIRight = NANUM;
	}                                                                                                                
	else// from GraphLayer
	{
		m_LProfileData.GetROILinesX(m_dROILeft, m_dROIRight);
		setImageROIRect(m_dROILeft, m_dROIRight);
	}
	
	m_LProfileData.UpdateROI(m_dROILeft, m_dROIRight);
}
///end

///////////////////////////////////////
// private member function
///////////////////////////////////////
void LProfile::rebuildAllLineAsPosition()
{
	// remove all reference and control line
	LT_execute("label -ra line");
	LT_execute("label -ra rect");
	LT_execute("label -ra tag");
	
	createLinesAsPosition(m_vsRefLine, m_vPosRefLine, PROFILE_REF_LINE);
	createLinesAsPosition(m_vsControlLine, m_vPosControlLine, PROFILE_CTRL_LINE);
}

void LProfile::createLinesAsPosition(vector<string>& vsLineName, const vector& vPosLine, int nLineType)  // = PROFILE_CTRL_LINE
{
	vsLineName.RemoveAll();
	
	bool bRefLine = ( PROFILE_REF_LINE == nLineType );
	for ( int nIndex = 0; nIndex < vPosLine.GetSize(); nIndex++ )
	{
		GraphObject go;
		createNewLine(go, vPosLine[nIndex], 0, nIndex+1, bRefLine, m_nLineWidth);
		vsLineName.Add(go.GetName());
	}
}

LProfile::resetAllNumAndPosLines(int nNumRefLine, int nNumCtrlLine, bool& bIsNumChanged)
{
	bIsNumChanged = false;
	
	if ( !updatePos(0) ) // update all position of lines
	{
		error_report("update position fail");
		return;
	}
	
	bool bIsChanged;
	
	resetNumAndPosLines(m_vPosRefLine, nNumRefLine, bIsChanged, PROFILE_REF_LINE);
	bIsNumChanged |= bIsChanged;
	
	resetNumAndPosLines(m_vPosControlLine, nNumCtrlLine, bIsChanged, PROFILE_CTRL_LINE);
	bIsNumChanged |= bIsChanged;
}

void LProfile::resetNumAndPosLines(vector& vPosLines, int nLineNum, bool& bIsNumChanged, int nLineType)  // = PROFILE_CTRL_LINE
{
	bIsNumChanged = false;
	
	int nCurLineNum = vPosLines.GetSize();
	if ( nCurLineNum < nLineNum ) // add line
	{
		for( int ii = nCurLineNum + 1; ii <= nLineNum; ii++ )
		{
			addLine(vPosLines, nLineType);
		}
		bIsNumChanged = true;
	}
	else if ( nCurLineNum > nLineNum ) // delete line
	{
		for ( int ii = nLineNum + 1; ii <= nCurLineNum; ii++ )
		{
			deleteLine(vPosLines);
		}
		bIsNumChanged = true;
	}
}
bool LProfile::addLine(vector& vPosLines, int nLineType) // = PROFILE_CTRL_LINE
{
	//vector<int> vnPType = {LN_HORIZONTAL, LN_VERTICAL};
	//int nLineDir = LN_VERTICAL;
	
	int nCols, nRows;
	nCols = m_moImg.GetNumCols();
	//nRows = m_moImg.GetNumRows();

	double x0 = 0, y0 = 0;
	
	if ( PROFILE_REF_LINE == nLineType )
	{
		//y0 = (double)nRows/2;
		x0 = (double)nCols/2;
	}
	else
	if ( PROFILE_CTRL_LINE == nLineType )
	{
		//double dHOffset = 0
		//double dVOffset = 0;
		double dVOffset = getNewOffset(vPosLines, 0, nCols-1);
		
		//y0 = 0;
		x0 = dVOffset + (double)nCols/10;
		
		//if(LN_VERTICAL == nLineDir)
		//{
			//y0 = 0;
			//x0 = dVOffset + (double)nCols/10;
			//y1 = nCols;;
			//x1 = x0 + nLineWidth;
		//}
		//else
		//if(LN_HORIZONTAL == nLineDir)
		//{
			//y0 = dHOffset + (double)nRows/10;
			//x0 = 0;
			//y1 = y0 + nLineWidth;
			//x1 = nCols;
		//}
		//else
			//return false;
	}
	
	vPosLines.Add(x0);
	
	return true;
}
bool LProfile::deleteLine(vector& vPosLines)
{
	int nSize = vPosLines.GetSize();
	if ( nSize <= 0 )
		return false;
	
	vPosLines.RemoveAt(nSize-1);
	return true;
}
	
bool LProfile::createNewLine(GraphObject& go, double x0, double y0, int nLineIndex, bool bRefLine, int nWidth, int nDir, bool bFixW) // = false, 1, LN_VERTICAL, true
{
	string strText;
	
	if ( bRefLine )
		strText.Format(" R ");
	else
		strText.Format(" %d ", nLineIndex);
	
	GraphObject goLine;
	GraphObject goText;
	if ( !add_x_tag(m_ml, goLine, goText, x0, strText, ( 1 == nWidth ? 0 : nWidth )) )
		return false;
	if ( bFixW )
	{
		disable_go_move(goLine, false, false, false);
	}
	disable_go_move(goText, false, false, false);
	
	string strLT;
	strLT.Format("string nn$=this.name$;event_update_line_profile(nn$, %%1);");
	set_LT_script(goLine, strLT, GRCT_ANY_EVENT);
	
	go = goLine;
	
	return true;
}

double LProfile::getNewOffset(const vector& vPosLines, double dMin, double dMax)
{
	vector vPos;
	vPos = vPosLines;
	
	vPos.InsertAt(0, dMin);
	vPos.Add(dMax);
	
	vPos.Sort();
	
	int nIndex = find_largest_gap(vPos);
	return vPos[nIndex];
}

bool LProfile::updateProfile(int nLineIndex)
{
	string strLineName;
	if ( -1 == nLineIndex )
	{	// reference line
		int bBaseIndex = ( -1 * nLineIndex ) - 1;
		strLineName = m_vsRefLine[bBaseIndex];
	}
	else if ( 0 < nLineIndex )
	{	// control line
		int bBaseIndex = nLineIndex - 1;
		strLineName = m_vsControlLine[bBaseIndex];
		nLineIndex--; // adjust index, base on 0
	}
	else
		return error_report("Invalid line index");

	GraphObject goLine = m_ml.GraphObjects(strLineName);
	
	vector  vIntensity;
	
	int nChModeSt, nChModeEd;
	nChModeSt = nChModeEd = m_nChannelMode;
	if ( CHANNEL_MODE_ALL == m_nChannelMode )
	{
		nChModeSt = CHANNEL_MODE_RED;
		nChModeEd = CHANNEL_MODE_BLUE;
	}

	for ( int nMode = nChModeSt; nMode <= nChModeEd; nMode++ )
	{
		if ( getLineProfile(goLine, vIntensity, nMode) )
			m_LProfileData.NewData(vIntensity, nLineIndex, m_vsChName[nMode]);
	}
	
	return true;
}

bool LProfile::updateAllProfile(bool bReconstructData) // = true;
{
	int nNumRefLine = m_vsRefLine.GetSize();
	int nNumCtrlLine = m_vsControlLine.GetSize();
	
	if ( bReconstructData )
		m_LProfileData.ResetAll(nNumRefLine, nNumCtrlLine, 0, m_nChannelMode);
	
	for (int iR = 1; iR <= nNumRefLine; iR++)
		updateProfile(-1*iR);
	for (int iC = 1; iC <= nNumCtrlLine; iC++)
		updateProfile(iC);
	
	return true;
}

bool LProfile::getLineProfile(const GraphObject& goLine, vector& vIntensity, int nChannelMode)
{

	if ( CHANNEL_MODE_ALL == nChannelMode )
		nChannelMode = CHANNEL_MODE_INTENSITY;
	
	MatrixObject mo;
	if ( !m_ImgChannel.GetChannelMatrix(mo, m_vsChName[nChannelMode]) )
		return false;
	
	
	vector vxPoints;
	vector vyPoints; 
	
	///Sandy 2006-12-22 rewrite get_matrix_line_profile and move to matdata_utils
	//return get_matrix_line_profile(goLine, mo, vxPoints, vyPoints, vIntensity);
	if ( !goLine.IsValid() || !mo.IsValid() )
		return false;
	
	double dLeft, dTop, dRight, dBottom;
	if(!rect_get_position(goLine, dLeft, dTop, dRight, dBottom))
		return false;
	///end
	
	if(0 == get_matrix_upright_line_profile(mo, dLeft, dTop, dRight, dBottom, vxPoints, vyPoints, vIntensity))
		return true;
	else
		return false;	
	///end
}

double LProfile::getLinePosition(const GraphObject& goLine)
{
	int nDir = get_thick_line_dir(goLine);
	
	double x0 = -1;
	switch ( nDir )
	{
	case LN_VERTICAL:
		x0 = goLine.X; 
		break;
	case LN_HORIZONTAL:
		x0 = goLine.Y; 
		break;
	}
	
	return x0;
}

int LProfile::getIndex(const GraphObject& goLine)
{
	int nIndex = 0;
	int nSign, nNo;
	
	GraphObject goText;
	if ( !find_connected_object(goLine, goText) )
	{
		error_report("Find connected object fail");
		return nIndex;
	}
	
	string strIndex = goText.Text;
	strIndex.TrimLeft();
	strIndex.TrimRight();
	
	nSign = 1;
	if ( 'R' == strIndex[0] )
	{
		nSign = -1;
		strIndex.Delete(0);
	}
	
	nNo = 1;
	if ( !strIndex.IsEmpty() && is_numeric(strIndex) )
	{
		nNo = atoi(strIndex);
	}
	
	return nSign * nNo;
}

bool LProfile::setIndex(GraphObject& goLine, int nIndex)
{
	GraphObject goText;
	if ( !find_connected_object(goLine, goText) )
		return error_report("Find connected object fail");;
	
	if ( 0 == nIndex )
		return error_report("Invalid line index");;
		
	string strText;
	if ( nIndex < 0 )
		strText = " R ";
	else
		strText = " " + nIndex + " ";
	
	goText.Text = strText;
	
	return true;
}

int LProfile::sortCtrlLines(bool& bOrderChanged)
{
	bOrderChanged = false;
	
	if ( m_vsControlLine.GetSize() <= 1 ) // no need to sort
		return true;
	
	if ( !updatePos(0) ) // update all position of lines
		return error_report("update position fail");
	
	vector<uint> vnIndeces;
	vector<double> vdSortedPos;
	vdSortedPos = m_vPosControlLine;
	if ( !vdSortedPos.Sort(SORT_ASCENDING, FALSE, vnIndeces) )
		return false;
	
	//if ( vdPos == vdSortedPos ) // order is not changed
		//return false;
	
	// update line order by sorted position
	int nNumLine = m_vsControlLine.GetSize();
	vector<string> vsSortedControlLine();
	vsSortedControlLine.SetSize(nNumLine);
	for ( int ii = 0; ii < nNumLine; ii++ )
	{
		int nIndexBeforeSort = vnIndeces[ii]; // need get corresponding line object from current sorted positions
		
		// update line list
		vsSortedControlLine[ii] = m_vsControlLine[nIndexBeforeSort];
		
		if ( nIndexBeforeSort != ii ) // order is changed, update text of line tag
		{
			bOrderChanged = true;
			
			GraphObject goLine = m_ml.GraphObjects(vsSortedControlLine[ii]);
			setIndex(goLine, ii + 1);
		}
	}
	
	if ( bOrderChanged )
		m_vsControlLine = vsSortedControlLine;
	
	return true;
}

//bool LProfile::updateAllLineWidth()
//{
	//if ( !m_strRefLine.IsEmpty() )
	//{
		//string strNewLine = resetWidth(m_strRefLine, m_nLineWidth);
		//if ( strNewLine.IsEmpty() )
			//error_report("resetWidth failed");
			//
		//m_strRefLine = strNewLine;
	//}
	//
	//for ( int ii = 0; ii < m_vsControlLine.GetSize(); ii++ )
	//{
		//string strNewLine = resetWidth(m_vsControlLine[ii], m_nLineWidth);
		//if ( strNewLine.IsEmpty() )
			//error_report("resetWidth failed");
		//
		//m_vsControlLine[ii] = strNewLine;
	//}
	//
	//return true;
//}
//
//// return name of new line object when current input line object is replaced with the new one
//string LProfile::resetWidth(string strLineName, int nLineWidth)
//{
 	//GraphObject goLine = m_ml.GraphObjects(strLineName);
	//
	//if ( get_thick_line_width(goLine) != nLineWidth )
	//{
	  	//double dx = goLine.X;
	  	//double dy = goLine.Y;
	  	//
	  	//int nLineIndex = getIndex(goLine);
  		//if ( 0 == nLineIndex )
  			//return error_report("Invalid line index");
	  //
	  	//bool bRefLine = (nLineIndex < 0) ? true : false;
	  	//
	 	//// update the width of line,
	 	//// remove original line, and create new other one at same position
	 	//GraphObject goNewLine;
		//if ( !createNewLine(goNewLine, dx, dy, nLineIndex, bRefLine, nLineWidth) )
			//return error_report("createNewLine failed"); // failed to create new line
		//
		//GraphObject goText;
		//if ( !find_connected_object(goLine, goText) )
			//error_report("Find connected object fail");;
		//goLine.Destroy();
		//goText.Destroy();
		//
		//goLine = goNewLine;		
	//}
	//
	//return goLine.GetName();
//}

bool LProfile::updatePos(int nIndex)
{
	if ( nIndex < 0 )
	{	// reference line
		int nBaseIndex = ( -1 * nIndex ) - 1;
		
		if ( nBaseIndex > (m_vsRefLine.GetSize() - 1)
			|| nBaseIndex > (m_vPosRefLine.GetSize() - 1) )
			return false;
		
		GraphObject go = m_ml.GraphObjects(m_vsRefLine[nBaseIndex]);
		m_vPosRefLine[nBaseIndex] = getLinePosition(go);
	}
	else if ( nIndex > 0 )
	{	// control line
		int nBaseIndex = nIndex - 1;
		
		if ( nBaseIndex > (m_vsControlLine.GetSize() - 1)
			|| nBaseIndex > (m_vPosControlLine.GetSize() - 1) )
			return false;
		
		GraphObject go = m_ml.GraphObjects(m_vsControlLine[nBaseIndex]);
		m_vPosControlLine[nBaseIndex] = getLinePosition(go);
	}
	else
	{	// update all position of line
		if ( !updateAllPos(m_vPosRefLine, m_vsRefLine) )
			return false;
		if ( !updateAllPos(m_vPosControlLine, m_vsControlLine) )
			return false;
	}
	return true;
}

bool LProfile::updateAllPos(vector& vPos, const vector<string>& vsLineNames)
{
	if ( vPos.GetSize() != vsLineNames.GetSize() )
		return error_report("length of reference line and position is not same!");
		
	for ( int ii = 0; ii < vsLineNames.GetSize(); ii++ )
	{
		GraphObject go = m_ml.GraphObjects(vsLineNames[ii]);
		vPos[ii] = getLinePosition(go);
	}
	
	return true;
}

///Sandy 2006-12-12 ROI_RECTANGLE
bool LProfile::setImageROIRect(int xLeft, int xRight, LPCSTR lpcszName, int nDir) // = IMGLP_ROI_RECT_NAME, LN_HORIZONTAL
{
	GraphObject go = m_ml.GraphObjects(lpcszName);
	if(go)
	{
		if(nDir == LN_HORIZONTAL)
			rect_move(go, 0, xLeft, UNITS_SCALE, 100, xRight);
		else
			rect_move(go, xLeft, 0, UNITS_SCALE, xRight, 100);
		return true;
	}
	
	double xx = 0;
	double xx2 = 100;
	int nAttach = 2;// Scale
	//int nDir = 1; // horizontal
	int nSpan = 1;
	//int nColor = SYSCOLOR_GREEN;
	if(add_rect(m_ml, go, xx, xLeft, xx2, xRight, INDEX_COLOR_TRANSPARENT, nAttach, nDir, nSpan, false))
	{
		set_LT_script(go, "event_update_ROI_rect_from_MatrixLayer();",GRCT_SIZEMOVE);
		go.SetName(lpcszName);
		return true;
	}
	return false;	
}

void LProfile::destroyImageROIRect(LPCSTR lpcszName)
{
	GraphObject go = m_ml.GraphObjects(lpcszName);
	if(go)
		go.Destroy();	
}
///end

#endif // _IMG_L_PROFILE_H