#ifndef _RAW_LINE_H_
#define _RAW_LINE_H_

#include <vector>
#include <list>
#include <map>
using namespace std;

#include "PreprocFuncs.h"
#include "TextPage.h"
#include "HoughTransform.h"

#define TOUCHES_LEFT	1
#define TOUCHES_RIGHT	2
#define TOUCHES_TOP		4
#define TOUCHES_BOTTOM	8

#define CANNOT_SUBDIVIDE_THE_PARAGRAPH 1

typedef enum {eUpSideDown = -1, eUnKnown, eUpSideUp} EPageOrientation;

class COCR;

struct CRawLine : public CRect, public vector<CRect>
{
	int m_nAvrgHeight;
	int m_nAvgGrey, m_nAvgBackGrnd;
	bool m_bIsSalient;
	int m_nTouchingFrame;
	int m_nParagraphIndx, m_nLineIndx;
	vector<CPoint> m_vSpine;
	vector<COcrTextLine*> m_vOcrTextLinesUSU, m_vOcrTextLinesUSD;
	void Copy(const UCHAR *pSrc, int nSrcWidth, UCHAR *pDest, int nDestWidth, CPoint shift)const;
	inline int GetHeight()const{return m_nAvrgHeight;}
	bool GetConnectionPoints(vector<pair<int, pair<int, int> > > & vPoints, int &nAvgTop, int &nAvgBottom)const;
	bool GetReferencePoints(CRect roi, vector<CPoint> & vXY, vector<int> & vY);
	bool GetReferencePoints(const CPoint & origin, vector<CPoint> & vXY, vector<int> & vY, int *pAvgTopPos = NULL)const;
	int GetAverageDistance(const CRawLine *pRl) const;
	int CalcOverlap(const vector<CRect> & vRects) const;
	int CountWords()const;
};

class CParagraph;

struct COcrChunk {
	COcrChunk(int nId, CParagraph *pParagraph,
		int nFirstLine, int nLastLine, IplImage *pGreyImg = NULL, UCHAR *p1BppImg = NULL,
			int nOrderX = 0, int nOrderY = 0, const LONGLONG *pCoefs = NULL, 
			const CRect *pBB = NULL, int nScale = 256, const CPoint *pOrigin = NULL) :
		m_nId(nId),
		m_pParagraph(pParagraph),
		m_nFirstLine(nFirstLine), 
		m_nLastLine(nLastLine),
		m_nOrderX(nOrderX), 
		m_nOrderY(nOrderY),
		m_bb(0, 0, 0, 0),
		m_origin(0, 0),
		m_nScale(nScale),
		m_pGreyImg(pGreyImg),
		m_p1BppImg(p1BppImg),
		m_bOcred(false)
		{
			if(m_pCoefs) {
				memcpy(m_pCoefs, pCoefs, sizeof(m_pCoefs));
			}
			if(pOrigin) m_origin = *pOrigin;
			if(pBB) m_bb.CopyRect(*pBB);
		}

	~COcrChunk() {
		if(m_pGreyImg) cvReleaseImage(&m_pGreyImg);
		if(m_p1BppImg) delete [] m_p1BppImg;
	}
	int m_nId;
	int m_nFirstLine, m_nLastLine;
	LONGLONG m_pCoefs[10];
	int m_nOrderX, m_nOrderY;
	CRect m_bb;
	CPoint m_origin;
	int m_nScale;
	IplImage *m_pGreyImg;
	UCHAR *m_p1BppImg;
	COcrTextPage m_upSideUpTp, m_upSideDownTp;
	CParagraph *m_pParagraph;
	bool m_bOcred;
	int GetId()const { return m_nId;}
	IplImage *GetGreyImg() { return m_pGreyImg;}
	void SetOcred(bool bOcred) { m_bOcred = bOcred;}
	bool GetOcred()const { return m_bOcred;}
	const IplImage *GetGreyImg() const { return m_pGreyImg;}
	
	CPoint Translate2OrigImgCoordinates(CPoint p) const {
		p.x = (p.x * (1 << SCALE_FXPNT)) / m_nScale;
		p.y = (p.y * (1 << SCALE_FXPNT)) / m_nScale;
		GET_VALUE_FUNCTION fCorrection = GetYCorrectionFunction(m_nOrderX, m_nOrderY);
		if(!fCorrection) fCorrection  = IdentityFunc;
		int nHW = m_origin.x - m_bb.left;
		int nHH = m_origin.y - m_bb.top;
		int nX = p.x + m_bb.left;
		int nY = int(fCorrection(p.x - nHW, p.y - nHH, m_pCoefs) >> CURVATURE_RES_SHIFT) + m_origin.y;
		return CPoint(nX, nY);
	}

	CvRect Translate2OrigImgCvRect(const CvRect & rect, BOOL bUpsideDown) {
		CPoint p[] = {
			CPoint(rect.x, rect.y),
			CPoint(rect.x + rect.width, rect.y + rect.height),
			CPoint(rect.x + rect.width, rect.y),
			CPoint(rect.x, rect.y + rect.height)
		};
		int i;
		if(bUpsideDown) {
			CPoint tmp(m_pGreyImg->width, m_pGreyImg->height);
			for(i = 0; i < 4; ++i) {
				p[i] = tmp - p[i];
			}
		}
		for(i = 0; i < 4; ++i) {
			p[i] = Translate2OrigImgCoordinates(p[i]);
		}
		int nLeft = p[0].x;
		int nTop = p[0].y;
		int nRight = p[0].x;
		int nBottom = p[0].y;
		for(i = 1; i < 4; ++i) {
			if(nLeft < p[i].x) nLeft = p[i].x;
			if(nTop < p[i].y) nTop = p[i].y;
			if(nRight > p[i].x) nRight = p[i].x;
			if(nBottom > p[i].y) nBottom = p[i].y;
		}
		return cvRect(nLeft, nTop, nRight - nLeft, nBottom - nTop);
	}
};

class CParagraph : public CRect, public vector<CRawLine *> {
	int m_nId;
	int m_nTotalHeight, m_nTotalCount;
	BOOL m_bIsEmbedded;
	bool m_bBoW;
	CRect m_linesTouchingFrame;
	bool m_bIsMainParagraph, m_bIsHeader; 
	int m_nArea;
	int m_nLinesOrientation;
	int m_nCoefs, m_nOrderX, m_nOrderY;
	CPoint m_roiCenter;
	int m_nFoundInDictWords;
	int m_nTotalWords, m_nTotalComponents;
	map<int, COcrChunk*> m_mapOcrChunks;
	vector<COcrChunk*> m_vLines2Chunks;
	set<CParagraph*> m_leftNeighbors, m_rightNeighbors, m_upNeighbors, m_downNeighbors; 
	BOOL m_bDoNotFilter;

public:
	CParagraph(int nId, bool bBoW, BOOL bIsEmbedded);
	~CParagraph() {
		for(map<int, COcrChunk*>::iterator i = m_mapOcrChunks.begin(); i != m_mapOcrChunks.end(); ++i) {
			if(i->second) delete i->second;
		}
	}
	void AddRawLine(CRawLine *pRl);
	inline int GetHeight()const{
		return (m_nTotalCount > 0) ? m_nTotalHeight / m_nTotalCount : 0;
	}
	inline int GetId()const { return m_nId; }
	inline BOOL IsEmbedded()const {return m_bIsEmbedded;}

	int GetReferencePoints(CRect roi, vector<CPoint> & vXY, vector<int> & vY)const;
	int GetReferencePoints(int nFirstLine, int nLastLine, 
			CPoint & origin, CRect & bb, vector<CPoint> & vXY, vector<int> & vY)const;
	//CRect GetCorrectedBoundBox(CRect roi, int nOrderX, int nOrderY, const LONGLONG *pCoefs)const;
	CRect GetCorrectedBoundBox(const CPoint & origin, CRect bb, int nFirstLine, int nLastLine, 
		int nOrderX, int nOrderY, const LONGLONG *pCoefs)const;

	int GetCoeficients(CRect roi, int & nOrderX, int & nOrderY, vector<float> & vCoefs)const;
	int GetCoeficients(int nFirstLine, int nLastLine, CPoint & origin, CRect &bb,
						int & nOrderX, int & nOrderY, vector<float> & vCoefs)const;
	inline bool GetBoW()const{return m_bBoW;}

	inline void SetLinesTouchingFrame(CRect linesTouchingFrame) {
		m_linesTouchingFrame = linesTouchingFrame;
	}
	inline void SetMainParagraph(bool bIsMainParagraph) {
		m_bIsMainParagraph = bIsMainParagraph;
	}

	bool IsTouchingFrame(int nMaxLinesToIgnore = -1)const;
	bool IsRunningIntoFrame()const;
	bool IsMainParagraph()const{ return m_bIsMainParagraph;}
	CRect GetLinesTouchingFrame()const {
		return m_linesTouchingFrame;
	}
	void SetArea(int nArea){m_nArea = nArea;}
	int GetArea()const{return m_nArea;}

	void SetLinesOrientation(int nOrientation) {m_nLinesOrientation = nOrientation;}
	int GetLinesOrientation() const {return m_nLinesOrientation;}
	int GetNumTotalWords();
	COcrChunk *AddOcrChunk(int nFirstLine, int nLastLine, IplImage *pGreyImg, UCHAR *p1BppImg,
		int nOrderX, int nOrderY, const LONGLONG *pCoefs, const CRect & bb, int nScale, const CPoint & origin);
	bool NoText() const;	
	bool OCredAlready(int nLineIndx) const;
	void AddLeftNeighbor(CParagraph *pLeftNeighbor) {m_leftNeighbors.insert(pLeftNeighbor);}
	void AddRightNeighbor(CParagraph *pRightNeighbor){m_rightNeighbors.insert(pRightNeighbor);}
	void AddUpNeighbor(CParagraph *pUpNeighbor) {m_upNeighbors.insert(pUpNeighbor);}
	void AddDownNeighbor(CParagraph *pDownNeighbor){m_downNeighbors.insert(pDownNeighbor);}
	BOOL CheckIfHeader() const;
	void SetHeader(bool bIsHeader = true) { m_bIsHeader = bIsHeader;}
	int GetTotalNumOfComponents();
	int GetMaxNumOfComponentsInLine() const;
	void SetDoNotFilter(BOOL bDoNotFilter = TRUE);
	BOOL GetDoNotFilter() const;
	int GetOptimalBinarizationThreshold(int nFirstLine, int nLastLine) const;
};

struct CRawLines : public vector<CRawLine*>
{
	vector<CParagraph*> m_vParagraphs;
	int m_nTotalParagraphsArea;
	int m_nMainParagraphIndx;
	vector<CRawLine*> m_vRegularOrder, m_vForceSingleOrder;
	BOOL m_bUpSideDown, m_bSparseText, m_bColumnLike;
	int m_nColumns, m_nHeaders;
	CRawLines() : m_nTotalParagraphsArea(0), m_nMainParagraphIndx(-1), m_bUpSideDown(FALSE) {}
	~CRawLines();
	virtual void clear();
	UCHAR* GetParagraphNBitmap(int nParagraph, int nFirstLine, int nLastLine, const UCHAR *p8bppImg, const ImgRotationInfo &iri, 
				  int nMinBitmapHeight, int nMargin, int nThreshold,
				  int &nBitmapWidth, int &nBitmapHeight, bool bFixCurvature, int nFixCurvatureOptimalLinesCount,
				  UCHAR *pDest, IplImage **pGreyImg, int nMaxDestBufSize, int & nStatus, 
				  int *pOrderX, int *pOrderY, LONGLONG *pCoefs, int *pScale, CRect *pBB, CPoint *pOrigin)const;
	inline int GetNumOfParagraphs()const{return int(m_vParagraphs.size());}
	void Print(const char *fileName)const;
	int MarkLinesTouchingFrame(const ImgRotationInfo &iri, int nMinDistanceHoriz, int nMinDistanceVert);
	BOOL GetPageDescription( int &nSections, int &nTotalWords, int &nTextAreaFrac, int & nColumns, int & nHeaders);
	float ReProcessParagraphs(const UCHAR *p8bppImg, const ImgRotationInfo &iri, bool bFixCurvature = true,  COCR *pOcr = NULL, bool bUpSideDown = false);
	BOOL SetForceSingleOrderAndRegularOrder();
	bool NoText() const;
	void SetUpSideDown(BOOL bUpSideDown); 
	void FindParagraphNeighbors();
	BOOL AnalyzePage(const ImgRotationInfo &iri, int nMinDistanceHoriz, int nMinDistanceVert);

};

int LocateTextLines(const UCHAR *p8bppImg, const UCHAR* p1bppBoW, const UCHAR* p1bppWoB, 
					const ImgRotationInfo &iri, int nBverWThreshold, CRawLines& lines, 
					int & nActualAngle);
#endif