#include "TextPage.h"
#include "stdlib.h"

//#define REGION_DEBUG

#define _New_(DerivedClassName)\
	COcrBasicTextRegion *DerivedClassName::New(COcrBasicTextRegion *pParent)const{\
		COcrBasicTextRegion *pBtr = new DerivedClassName(*this, pParent);\
		pBtr->SetTextFeatures(GetTextFeatures());\
		for(const_iterator i = begin(); i != end(); ++i){\
			pBtr->push_back((*i)->New(pBtr));\
		}\
		return pBtr;\
	}

#define _Split_(DerivedClassName)\
	COcrBasicTextRegion *DerivedClassName::Split(UINT nSplit){\
		if(nSplit >= size()) return NULL;\
		COcrBasicTextRegion *pBtr = new DerivedClassName(CRect(0,0, 0,0), GetParent());\
		pBtr->SetTextFeatures(GetTextFeatures());\
		iterator i;\
		for(i = begin(); i !=  begin() + nSplit; ++i){\
			if(*i){\
				(*i)->SetParent(pBtr);\
				pBtr->push_back(*i);\
				pBtr->UnionRect(*pBtr, **i);\
				*i = NULL;\
			}\
		}\
		SetRectEmpty();\
		for(i = begin(); i !=  end(); ){\
			if(*i){\
				UnionRect(*this, **i);\
				++i;\
			}\
			else{\
				i = erase(i);\
			}\
		}\
		return pBtr;\
	}

COcrBasicTextRegion::COcrBasicTextRegion(CRect rect, COcrBasicTextRegion *pParent) : CRect(rect), m_pParent(pParent), m_flgFeatures(0){}
COcrBasicTextRegion::COcrBasicTextRegion(const COcrBasicTextRegion & btr) : CRect(btr), m_pParent(btr.m_pParent), m_flgFeatures(btr.m_flgFeatures){
	for(const_iterator i = btr.begin(); i != btr.end(); ++i){
		if(*i){
			push_back((*i)->New(this));
		}
	}
}
		
COcrBasicTextRegion::~COcrBasicTextRegion(){
	clear();
}

COcrBasicTextRegion *COcrBasicTextRegion::GetParent(){return m_pParent;}
const COcrBasicTextRegion *COcrBasicTextRegion::GetParent()const{return m_pParent;}
void COcrBasicTextRegion::SetParent(COcrBasicTextRegion *pParent){m_pParent = pParent;}
void COcrBasicTextRegion::SetTextFeature(UINT nFeature, bool bOn){if(bOn)m_flgFeatures |= nFeature; else m_flgFeatures &= ~nFeature;}
bool COcrBasicTextRegion::GetTextFeature(UINT nFeature)const{return (m_flgFeatures & nFeature) == nFeature;}
void COcrBasicTextRegion::SetTextFeatures(UINT nFeatures){m_flgFeatures = nFeatures;}
UINT COcrBasicTextRegion::GetTextFeatures()const{return m_flgFeatures;}
	
const COcrBasicTextRegion *COcrBasicTextRegion::GetTheRootParent()const{
	if(m_pParent == NULL){
		return this;
	}
	return m_pParent->GetTheRootParent();
}

BOOL COcrBasicTextRegion::GetText(wstring & sText) const{
	sText.erase();
	if(empty()) return FALSE;
	wstring sBtrText;
	for(const_iterator i = begin(); i != end(); ++i){
		if(*i && (*i)->GetText(sBtrText)){
			sText += sBtrText;
		}
	}
	return TRUE;
}

BOOL COcrBasicTextRegion::GetTaggedText(wstring & sText) const{
	sText.erase();
	if(empty()) return FALSE;
	wstring sBtrText;
	for(const_iterator i = begin(); i != end(); ++i){
		if(*i && (*i)->GetTaggedText(sBtrText)){
			sText += sBtrText;
		}
	}
	return TRUE;
}

void COcrBasicTextRegion::GetFilteredText(wstring & sText, Dictionary* pDict) const{
	sText.erase();
	if(empty()) return;
	wstring sBtrText;
	for(const_iterator i = begin(); i != end(); ++i){
		if(*i) {
			(*i)->GetFilteredText(sBtrText, pDict);
			sText += sBtrText;
		}
	}
}

void COcrBasicTextRegion::clear(){
	SetRectEmpty();
	//static int n = 0;
	for(iterator i = begin(); i != end(); ++i){
		if(*i){
			delete *i;
		}
	}
	vector<COcrBasicTextRegion *>::clear();
}
	
BOOL COcrBasicTextRegion::SaveToDisk(const char *sFileName)const{
	FILE *pFile = fopen(sFileName, "w");
	if(!pFile){
		return FALSE;
	}
	for(const_iterator i = begin(); i != end(); ++i){
		if(*i){
			(*i)->DumpToFile(pFile);
		}
	}
	fclose(pFile);
	return TRUE;
}

BOOL COcrBasicTextRegion::DumpToFile(FILE *pFile)const{
	if(fprintf(pFile, "%d %d %d %d\n", left, top, right, bottom) <= 0){
		return FALSE;
	}
	return TRUE;
}

void COcrBasicTextRegion::Merge(COcrBasicTextRegion *pBtr){
	UnionRect(*this, *pBtr);
	for(iterator i = pBtr->begin(); i != pBtr->end(); ++i){
		if(*i){
			push_back(*i);
			(*i)->SetParent(this);
			*i = NULL;
		}
	}
}

COcrBasicTextRegion *COcrBasicTextRegion::Split(UINT nSplit){return NULL;}
void COcrBasicTextRegion::Linearize(vector<COcrBasicTextRegion *> & vLine){
	for(iterator i = begin(); i != end(); ++i){
		if(*i){
			(*i)->Linearize(vLine);
		}
	}
}		

UCHAR COcrBasicTextRegion::GetConfidence()const{
	int nConfidence = 0, nCount = 0;
	for(const_iterator i = begin(); i != end(); ++i){
		if(*i){
			nConfidence +=  int((*i)->GetConfidence())*(*i)->size(); 
			nCount += (*i)->size();
		}
	}
	if(nCount < 1) return 255;
	return nConfidence / nCount;
}

BOOL COcrBasicTextRegion::Swap(int nPos1, int nPos2){
	if(nPos1 == nPos2 || nPos1 >= int(size()) || nPos2 >= int(size())){
		return FALSE;
	}
	COcrBasicTextRegion *pTmp = operator [](nPos1);
	operator [](nPos1) = operator [](nPos2);
	operator [](nPos2) = pTmp;
	return TRUE;
}
UINT COcrBasicTextRegion::size()const{
	return vector<COcrBasicTextRegion *>::size();
}

int COcrBasicTextRegion::GetHeight()const{
	int nHeight = 0;
	int nTotSize = 0;
	for(const_iterator i = begin(); i != end(); ++i){
		if(*i){
			nHeight += (*i)->GetHeight() * (*i)->size();
			nTotSize += (*i)->size();
		}
	}
	return (nTotSize > 0)? nHeight / nTotSize : 0;
}

int COcrBasicTextRegion::GetMeanY()const{
	int nYPos = 0;
	int nTotSize = 0;
	for(const_iterator i = begin(); i != end(); ++i){
		if(*i){
			nYPos += (*i)->GetMeanY() * (*i)->size();
			nTotSize += (*i)->size();
		}
	}
	return (nTotSize > 0)? nYPos/nTotSize : (top+bottom)/2;
}

UINT COcrBasicTextRegion::GetFeatures()const{
	int nItalic = 0;
	int nBold = 0;
	int nTot = 0;
	for(const_iterator i = begin(); i != end(); ++i){
		if(*i){
			UINT nFeatures = (*i)->GetFeatures();
			if(nFeatures & ITALIC_FEATURE)
				nItalic++;
			if(nFeatures & BOLD_FEATURE)
				nBold++;
			nTot++;
		}
	}
	UINT nFeatures(0);
	if(nBold > nTot/2) nFeatures |= BOLD_FEATURE;
	if(nItalic > nTot/2) nFeatures |= ITALIC_FEATURE;
	return nFeatures;
}

void COcrBasicTextRegion::OffsetRect(int x, int y){
	OffsetRect(CPoint(x, y));
}

void COcrBasicTextRegion::OffsetRect(CPoint p){
	CRect::OffsetRect(p);
	for(iterator i = begin(); i != end(); ++i){
		if(*i){
			(*i)->OffsetRect(p);
		}
	}
}

COcrBasicTextRegion::iterator COcrBasicTextRegion::erase(iterator i){
	if(*i) {
		delete *i;
	}
	return vector<COcrBasicTextRegion *>::erase(i);
}

COcrTextCharacter::COcrTextCharacter(WCHAR c, CRect rect, COcrBasicTextRegion *pParent, 
									 UCHAR confidence, UINT nFeatures) 
									 :  COcrBasicTextRegion(rect, pParent),
									 m_char(c), 
									 m_confidence(confidence) {
	m_flgFeatures = nFeatures;
}

void COcrBasicTextRegion::Copy(const COcrBasicTextRegion *pBtr){
	clear();
	CopyRect(*pBtr);
	m_flgFeatures = pBtr->m_flgFeatures;
	for(const_iterator i = pBtr->begin(); i != pBtr->end(); ++i){
		if(*i){
			COcrBasicTextRegion *pNtr = (*i)->New(this);
			push_back(pNtr);
		}
	}
}

BOOL COcrTextCharacter::GetText(wstring & sText)const{
	sText.erase();
	if(Dictionary::Instance()->ValidCharacter(m_char)){
		sText = wstring(1, m_char); 
	}
	else{
		sText = L" "; 
	}
	return TRUE;
}

BOOL COcrTextCharacter::GetTaggedText(wstring & sText)const{ 
	if((m_flgFeatures & LOW_CONFIDENCE) != 0){
		sText.erase();
		return FALSE;
	}
	if(!Dictionary::Instance()->ValidCharacter(m_char)){
		sText = L" ";
		return TRUE;
	}
	switch(m_char)
	{
	case '<':
		sText = L"&lt;";
		break;
	case '>':
		sText = L"&gt;";
		break;
	case '&':
		sText = L"&amp;";
		break;
	default:
		sText = wstring(1, m_char); 
		break;
	}
	return TRUE;
}

void COcrTextCharacter::GetFilteredText(wstring & sText, Dictionary *pDict)const{ 
	if((m_flgFeatures & (LOW_CONFIDENCE | DO_NOT_FILTER)) == LOW_CONFIDENCE ){
		sText.erase();
		return;
	}
	if(Dictionary::Instance()->ValidCharacter(m_char)){
		sText = wstring(1, m_char);
		return;
	}
	sText = L" ";
}

WCHAR COcrTextCharacter::GetChar()const{return m_char;} 
UCHAR COcrTextCharacter::GetConfidence()const{return m_confidence;} 
COcrBasicTextRegion* COcrTextCharacter::New(COcrBasicTextRegion *pParent)const{
	
	return new COcrTextCharacter(m_char, *this, pParent, m_confidence);
}

void COcrTextCharacter::Linearize(vector<COcrBasicTextRegion *> & vLine){ 
	vLine.push_back(this);
}

UINT COcrTextCharacter::size()const{return 1;}
int COcrTextCharacter::GetHeight()const{return Height();}
UINT COcrTextCharacter::GetFeatures()const{return m_flgFeatures;}

COcrTextWord::COcrTextWord(CRect rect, COcrBasicTextRegion *pParent) : COcrBasicTextRegion(rect, pParent) {}
void COcrTextWord::AddTextCharacter(WCHAR c, CRect rect, UCHAR confidence, UINT nFeatures){
	UnionRect(*this, rect);
	push_back(new COcrTextCharacter(c, rect, this, confidence, nFeatures));
}
_New_(COcrTextWord)
_Split_(COcrTextWord)

void COcrTextWord::GetFilteredText(wstring & sText, Dictionary *pDict) const {
	COcrBasicTextRegion::GetFilteredText(sText);
	if(pDict) {
		pDict->SpellCheck(sText, FALSE);
	}
}

COcrTextLine::COcrTextLine(CRect rect, COcrBasicTextRegion *pParent) : 
	COcrBasicTextRegion(rect, pParent) {
	
}

void COcrTextLine::clear() {
	COcrBasicTextRegion::clear();
}

void COcrTextLine::Copy(const COcrBasicTextRegion *pOtl) {
	COcrBasicTextRegion::Copy(pOtl);
}


void COcrTextLine::AddTextWord(CRect rect) {
	UnionRect(*this, rect);
	push_back(new COcrTextWord(rect, this));
}

void COcrTextLine::AddTextCharacterToTheLastTextWord(WCHAR c, CRect rect, UCHAR confidence, UINT nFeatures){
	if(!empty()){
		UnionRect(*this, rect);
		((COcrTextWord*)(*rbegin()))->AddTextCharacter(c, rect, confidence, nFeatures);
	}
}

BOOL COcrTextLine::GetText(wstring & sText) const{
	sText.erase();
	if(empty()) return FALSE;
	wstring sBtrText;
	for(const_iterator i = begin(); i != end(); ++i){
		if((*i)->GetText(sBtrText)){
			sText += sBtrText;
		}
		sText += L" ";
	}
	sText += L"\n";
	return TRUE;
}

BOOL COcrTextLine::GetTaggedText(wstring & sText) const {
	sText.erase();
	if(empty() || GetTextFeature(LOW_CONFIDENCE)) return FALSE;
	wstring sBtrText;
	bool forceSingle = GetTextFeature(FORCE_SINGLE);
	int height3 = 3 * GetHeight();
	CPoint topLeft;
	CPoint bottomRight;
	for(const_iterator i = begin(); i != end(); ++i) {
		if((*i)->GetTaggedText(sBtrText)) {
			sText += sBtrText;
			bool insertPause = false;
#ifdef	INSERT_PAUSE
			if(forceSingle) {
				if(i+1 != end()) {
					bottomRight = (*i)->BottomRight();
					topLeft = (*(i+1))->TopLeft();
					insertPause = (topLeft.x - bottomRight.x >= height3);
				}
				else {
					sText += L"\n";
					insertPause = true;
				}
			}
#endif
			sText += insertPause ? L"<pause/>" : L" ";
		}
	}
	//if(forceSingle){
	//	if(GetTextFeature(PARTIALY_NOISE)){
	//		char *pLine = GetTheGoodPartOfTheLine(sText.c_str(), 4);
	//		if(pLine){
	//			sText.assign(pLine);
	//			delete [] pLine;
	//		}
	//	}
	//	//char *pLine = sText.c_str();//RemoveBackSlashAtTheEndOfTheLine(sText.c_str());
	//	//if(pLine){
	//	//	sText.assign(pLine);
	//	//	//delete [] pLine;
	//	//}
	//}
	//if(!HasEnoughLettersOrNumbers(sText.c_str())){
	//	sText.erase();
	//	return FALSE;
	//}
	sText += L"\n";
	return TRUE;
}

void COcrTextLine::GetFilteredText(wstring & sText, Dictionary* pDict) const {
	sText.erase();
	if(empty() || (m_flgFeatures & (LOW_CONFIDENCE | DO_NOT_FILTER)) == LOW_CONFIDENCE) {
		return;
	}
	wstring sBtrText;
	bool forceSingle = GetTextFeature(FORCE_SINGLE);
	for(const_iterator i = begin(); i != end(); ++i) {
		(*i)->GetFilteredText(sBtrText, pDict);
		if(sBtrText.empty()) continue;
		sText += sBtrText;
		sText += L" ";
	}
	sText += L"\r\n";
}

void COcrTextLine::GetFilteredText(wstring & sText, Dictionary* pDict, vector<int> *pWords) const {
	sText.erase();
	if(empty() || (m_flgFeatures & (LOW_CONFIDENCE | DO_NOT_FILTER)) == LOW_CONFIDENCE) {
		return;
	}
	wstring sBtrText;
	bool forceSingle = GetTextFeature(FORCE_SINGLE);
	for(const_iterator i = begin(); i != end(); ++i) {
		(*i)->GetFilteredText(sBtrText);
		if(pDict) {
			pDict->SpellCheck(sBtrText, FALSE);
		}
		if(sBtrText.empty()) continue;
		if(pWords)pWords->push_back(sText.length());
		sText += sBtrText;
		sText += L" ";
	}
	sText += L"\r\n";
}
BOOL COcrTextLine::MarkLowConfidenceParts(int nConfThreshold, int nWindow){
	deque<COcrBasicTextRegion*> chars;
	nConfThreshold *= nWindow;
	//int nSize = size();
	int nSum = 0, nFirst = -1, nLast = -1, nCharCount = 0;
	for(iterator i = begin(); i != end(); ++i){
		for(UINT j = 0; j < (*i)->size(); ++j){
			nSum += (*i)->at(j)->GetConfidence();
			chars.push_back((*i)->at(j));
			++nCharCount;
			if(int(chars.size()) > nWindow){
				nSum -= int(chars.front()->GetConfidence());
				chars.pop_front();
			}
			if((int)chars.size() == nWindow){
				if(nSum < nConfThreshold){
					if(nFirst == -1){
						nFirst = nCharCount - nWindow;
					}
					nLast = nCharCount;
				}
			}
		}
	}
	if(nFirst < nLast){
		nCharCount = 0;
		for(iterator i = begin(); i != end(); ++i){
			for(UINT j = 0; j < (*i)->size(); ++j, ++nCharCount){
				if(nCharCount < nFirst || nCharCount > nLast){
					(*i)->at(j)->SetTextFeature(LOW_CONFIDENCE);
				}
			}
		}
		return TRUE;
	}
	return FALSE;
}

_New_(COcrTextLine)
_Split_(COcrTextLine)

COcrTextRegion::COcrTextRegion(CRect rect, COcrBasicTextRegion *pParent) : 
	COcrBasicTextRegion(rect, pParent), m_history(L""), m_nAlreadyOcredLines(0) {}
void COcrTextRegion::AddTextLine(CRect rect){
	UnionRect(*this, rect);
	push_back(new COcrTextLine(rect, this));
}
void COcrTextRegion::AddTextWordToTheLastTextLine(CRect rect){
	if(!empty()){
		UnionRect(*this, rect);
		((COcrTextLine*)(*rbegin()))->AddTextWord(rect);
	}
}
void COcrTextRegion::AddTextCharacterToTheLastTextWord(WCHAR c, CRect rect, UCHAR confidence, UINT nFeatures){
	if(!empty()){
		UnionRect(*this, rect);
		((COcrTextLine*)(*rbegin()))->AddTextCharacterToTheLastTextWord(c, rect, confidence, nFeatures);
	}
}
void COcrTextRegion::SetHistory(wstring text) {
	m_history = text;
}
wstring COcrTextRegion::GetHistory() {
	return m_history;
}
int COcrTextRegion::GetLineSpacing(unsigned int n){
	if((n <= 0) || (n >= size()))
		return 0;
	COcrBasicTextRegion* pPrev = at(n-1);
	COcrBasicTextRegion* p = at(n);
	int spacing = (p->GetMeanY() - pPrev->GetMeanY()) - (p->GetHeight() + pPrev->GetHeight())/2;
	return spacing;
}

bool COcrTextRegion::CheckText(COcrBasicTextRegion *pTr, Dictionary* pDict)const{
	wstring sText;
	pTr->GetText(sText);
	//if (!pDict->SuspiciousChars(sText.c_str(), 4)) return true;
	int nWord, nNoWord;
	pDict->CountFoundWords((WCHAR*)sText.c_str(), nWord, nNoWord, 3);
	if(nWord >= 1){
		pTr->SetTextFeature(PARTIALY_NOISE);
		return true;
	}
	return false;
}

void COcrTextRegion::SetLineConfidences(Dictionary* pDict){
	int nLow = 0, nHigh = 0;
	iterator i;
	for(i = begin(); i != end(); ++i){
		if(*i){
			int nConfidence  = (*i)->GetConfidence();
			if(nConfidence < 110 ||
				((nConfidence < 200 || ((COcrTextLine*)*i)->MarkLowConfidenceParts(200, 5)) && (!pDict || CheckText(*i, pDict)))) {
				(*i)->SetTextFeature(LOW_CONFIDENCE, false);
				++nHigh;
			}
			/*else if(nConfidence < 128){
			wstring sText;
			(*i)->GetText(sText);
			int nQual;
			sText = pDict->FilterText(sText.c_str(), 256, nQual);
			if(sText.empty()){
			(*i)->SetTextFeature(LOW_CONFIDENCE);
			++nLow;
			}
			else{
			(*i)->SetTextFeature(LOW_CONFIDENCE, false);
			++nHigh;
			}
			}*/
			else{
				(*i)->SetTextFeature(LOW_CONFIDENCE);
				++nLow;
			}
		}
	}
	if(nHigh < 1){
		SetTextFeature(LOW_CONFIDENCE);
		return;
	}

	SetTextFeature(LOW_CONFIDENCE, false);
	if(size() < 3){
		return;
	}
	/*for(i = begin()+1; i != end()-1; ++i){
	if(*i && (*i)->GetTextFeatures() & LOW_CONFIDENCE){
	if(*(i-1) && ((*(i-1))->GetTextFeatures() & LOW_CONFIDENCE) == 0 
	&& *(i+1) && ((*(i+1))->GetTextFeatures() & LOW_CONFIDENCE) == 0){
	(*i)->SetTextFeature(LOW_CONFIDENCE, false);
	}
	}
	}

	if(*begin() && ((*begin())->GetTextFeatures() & LOW_CONFIDENCE) 
	&& *(begin()+1) && ((*(begin() + 1))->GetTextFeatures() & LOW_CONFIDENCE) == 0){
	(*begin())->SetTextFeature(LOW_CONFIDENCE, false);
	}
	if(*rbegin() && ((*rbegin())->GetTextFeatures() & LOW_CONFIDENCE) 
	&& *(rbegin()+1) && ((*(rbegin() + 1))->GetTextFeatures() & LOW_CONFIDENCE) == 0){
	(*rbegin())->SetTextFeature(LOW_CONFIDENCE, false);
	}*/
}

BOOL COcrTextRegion::GetText(wstring & sText) const{
	sText.erase();
	if(GetTextFeatures() & LOW_CONFIDENCE){
		return false;
	}
	wstring stmp;
	for(const_iterator i = begin(); i != end(); ++i){
		if(*i && ((*i)->GetTextFeatures() & LOW_CONFIDENCE) == 0){
			(*i)->GetText(stmp);
			sText += stmp;
		}
	}
	return true;
}

_New_(COcrTextRegion)
_Split_(COcrTextRegion)

void COcrTextRegion::AddOverlapingRegion(COcrTextRegion* pRegion){
	m_xOverlapingRegions.insert(pRegion);
}

bool COcrTextRegion::CheckIfOverlaped(int startTop, int startBottom, const COcrTextRegion *pEndRegion, set<const COcrTextRegion*> & alreadyChecked)const{
	if((top - startTop) * (top - pEndRegion->top) > 0 || (bottom - startBottom) * (bottom - pEndRegion->bottom) > 0 
		||!alreadyChecked.insert(this).second){
			return false;
		}
		if(m_xOverlapingRegions.find(pEndRegion) == m_xOverlapingRegions.end()){
			for(set<const COcrTextRegion*>::const_iterator i = m_xOverlapingRegions.begin(); i != m_xOverlapingRegions.end(); ++i){
				if((*i)->CheckIfOverlaped(startTop, startBottom, pEndRegion, alreadyChecked)){
					return true;
				}
			}
		}
		else{
			return true;
		}
		return false;
}

bool COcrTextRegion::IsVerticalySeparated(const COcrTextRegion *pRegion)const{
	set<const COcrTextRegion*> alreadyChecked;
	return !CheckIfOverlaped(top, bottom, pRegion, alreadyChecked);
}

bool COcrTextRegion::operator < (const COcrTextRegion & tr)const{
	if(IsVerticalySeparated(&tr)){
		return left < tr.left;
	}
	return top < tr.top;
}

void COcrTextRegion::RecalBoundBox(){
	SetRectEmpty();
	for(iterator i = begin(); i != end(); ++i){
		if(*i)*this |= **i;
	}
}

int COcrTextRegion::GetNextNotOcredLineIndx()const {
	return m_nAlreadyOcredLines;
}

void COcrTextRegion::SetNextNotOcredLineIndx(int nNextNotOcredLineIndx) {
	m_nAlreadyOcredLines = nNextNotOcredLineIndx;
}

//static int XOverlap(const COcrTextRegion &r1, const COcrTextRegion &r2){
//	return min(r1.right, r2.right) - max(r1.left, r2.left);
//}

//static int YOverlap(const COcrTextRegion &r1, const COcrTextRegion &r2){
//	return min(r1.bottom, r2.bottom) - max(r1.top, r2.top);
//}

inline CRect RotateRect180(CRect rect, int nWidth, int nHeight){
	CRect rotRect;
	rotRect.left = nWidth - 1 - rect.right;
	rotRect.right = nWidth - 1 - rect.left;
	rotRect.top = nHeight - 1 - rect.bottom;
	rotRect.bottom = nHeight - 1 - rect.top;
	return rotRect;
}

COcrTextPage::COcrTextPage(CRect rect, COcrBasicTextRegion *pParent) : COcrBasicTextRegion(rect, pParent){}

_New_(COcrTextPage)
_Split_(COcrTextPage)
void COcrTextPage::AddRegion(CRect rect){
	UnionRect(*this, rect);
	push_back(new COcrTextRegion(rect, this));
}
void COcrTextPage::AddLineToLastRegion(CRect rect){
	UnionRect(*this, rect);
	((COcrTextRegion*)(*rbegin()))->AddTextLine(rect);
}
void COcrTextPage::AddWordToLastLine(CRect rect){
	UnionRect(*this, rect);
	((COcrTextRegion*)(*rbegin()))->AddTextWordToTheLastTextLine(rect);
}
void COcrTextPage::AddCharacterToTheLastWord(WCHAR c, CRect rect, UCHAR confidence, UINT nFeatures){
	UnionRect(*this, rect);
	((COcrTextRegion*)(*rbegin()))->AddTextCharacterToTheLastTextWord(c, rect, confidence, nFeatures);
}

BOOL COcrTextPage::GetText(wstring & sText) const{
	if(!COcrBasicTextRegion::GetText(sText)){
		return FALSE;
	}
	sText += L'\0';
	return TRUE;
}

BOOL COcrTextPage::GetText(wstring & sText){
	m_vLine.clear();
	if(!COcrBasicTextRegion::GetText(sText)){
		return FALSE;
	}
	sText += L'\0';
	Linearize(m_vLine);
	return TRUE;
}
void COcrTextPage::Add(const COcrTextPage & textPage){
	UnionRect(*this, textPage);
	for(const_iterator i = textPage.begin(); i != textPage.end(); ++i){
		if(*i && (*i)->size() > 0)push_back((*i)->New(this));
	}
}

BOOL COcrTextPage::GetText(UINT nFirst, UINT nLast, wstring & sText){
	if(m_vLine.empty()) GetText(sText);
	if(nLast > m_vLine.size() || nFirst >= nLast) 
		return FALSE;
	sText.erase();
	for(vector<COcrBasicTextRegion *>::iterator i = m_vLine.begin() + nFirst; i != m_vLine.begin() + nLast; ++i){
		WCHAR c = ((COcrTextCharacter*)(*i))->GetChar();
		if(Dictionary::Instance()->ValidCharacter(c)){
			sText += c; 
		}
		else{
			sText += ' '; 
		}
	}
	return TRUE;
}

COcrBasicTextRegion *COcrTextPage::GetCharacterObject(int nPos){
	wstring sText;
	if(m_vLine.empty()) GetText(sText);
	if(nPos >= int(m_vLine.size())) 
		return NULL;
	return m_vLine[nPos];
}

void COcrTextPage::SplitRegions(){
	SplitRegionsByHeight2(3);
	SplitRegionsBySpacing(2);
	SplitRegionsByWordCount(7, 5);
}

void COcrTextPage::SplitRegionsByHeight1(){
	for(unsigned int iCount = 0; iCount < size(); ++iCount) {
		const_iterator i = begin() + iCount;
		if((*i) && (*i)->size() > 3) {
			int lastH = -1;
			int jCount = 0;
			int jBreak = -1;
			int maxDiff = 0;
			int maxDiff2 = 0;
			for(const_iterator j = (*i)->begin(); j != (*i)->end(); ++j, ++jCount) {
				int height = (*j)->GetHeight();
				if(jCount > 0) {
					int diff = abs(height - lastH);
					if(diff > maxDiff) {
						maxDiff2 = maxDiff;
						maxDiff = diff;
						jBreak = jCount;
					}
					else if(diff > maxDiff2)
						maxDiff2 = diff;
				}
			}
			if((maxDiff2 > 0) && (maxDiff > 2*maxDiff2)) {
				SplitRegion(*i, jBreak, 0, L"splitByHeight1");
			}
		}
	}
}

void COcrTextPage::SplitRegionsByHeight2(int multiplier){
	CPoint topLeft;
	CPoint bottomRight;
	for(unsigned int iCount = 0; iCount < size(); ++iCount) {
		const_iterator i = begin() + iCount;

		if((*i) && (*i)->size() > 1) {
			int jCount = 0;
			vector<bool> jBig;
			vector<int> jBreak;
			int iHeight = (*i)->GetHeight();
			unsigned int iSize = (*i)->size();
			bool isBigPrev = false;
			for(const_iterator j = (*i)->begin(); j != (*i)->end(); ++j, ++jCount) {
				int height = (*j)->GetHeight();
				int refHeight = (iSize > 3) ? iHeight : (((*i)->GetHeight() * iSize) - height) / (iSize - 1);
				int diff = abs(height - refHeight);
				bool isBig = (multiplier * diff >= refHeight);
				if(jCount > 0) {
					if(isBig != isBigPrev) {
						jBreak.push_back(jCount);
						jBig.push_back(isBigPrev);
						jCount = 0;
					}
				}
				isBigPrev = isBig;
			}
			for(unsigned int k = 0; k < jBreak.size(); ++k) {
				UINT textFeature = jBig.at(k) && (jBreak.at(k) <= 3) ? HEADING_FEATURE : 0;
				SplitRegion(at(iCount+k), jBreak.at(k), textFeature, L"splitByHeight2");
			}
		}
	}
}

void COcrTextPage::SplitRegionsBySpacing(int multiplier){
	CPoint topLeft;
	CPoint bottomRight;
	unsigned int jCount;
	for(unsigned int iCount = 0; iCount < size(); ++iCount) {
		const_iterator i = begin() + iCount;
		if((*i) && (*i)->size() > 2) {
			const_iterator j = (*i)->begin();
			unsigned int iSize = (*i)->size();
			int meanHeight = (*j)->Height();
			int meanSpacing = 0;
			for(jCount = 1; jCount < iSize; ++jCount) {
				++j;
				meanHeight += (*j)->Height();
				meanSpacing += ((COcrTextRegion*)(*i))->GetLineSpacing(jCount);
			}
			meanHeight = meanHeight / iSize;
			meanSpacing = meanSpacing / (iSize - 1);
			int refSpacing = max(meanHeight, meanSpacing);
			vector<int> jBreak;
			jCount = 0;
			unsigned int jLine = 0;
			for(j = (*i)->begin(); j != (*i)->end(); ++j, ++jLine) {
				if(jCount > 0) {
					int diff = ((COcrTextRegion*)(*i))->GetLineSpacing(jLine) - meanSpacing;
					if(multiplier * diff >= refSpacing) {
						jBreak.push_back(jCount);
						jCount = 0;
					}
				}
				++jCount;
			}
			for(unsigned int k = 0; k < jBreak.size(); ++k) {
				SplitRegion(at(iCount+k), jBreak.at(k), 0, L"splitBySpacing");
			}
		}
	}
}

void COcrTextPage::SplitRegionsByWordCount(UINT maxWordsPerLineTimes2, UINT maxLines){
	for(unsigned int iCount = 0; iCount < size(); ++iCount) {
		const_iterator i = begin() + iCount;
		if((*i) && ((*i)->size() > 1)) {
			unsigned int nLines = 0;
			unsigned int nWords = 0;
			for(const_iterator j = (*i)->begin(); j != (*i)->end(); ++j) {
				if(((*j)->size() == 0) || ((*j)->GetTextFeatures() & LOW_CONFIDENCE))
					continue;
				nWords += (*j)->size();
				if(++nLines > maxLines)
					break;
			}
			if((nLines < 2) || (nLines > maxLines) || (2 * nWords > maxWordsPerLineTimes2 * nLines))
				continue;

			wstring text;
			unsigned int nSplit = 0;
			unsigned int iSize = (*i)->size();
			for(unsigned int k = 0; k < iSize-1; ++k) {
				COcrBasicTextRegion* region = at(iCount + nSplit);
				if(region->at(1)->size() == 0)
					continue;
				region->at(1)->GetText(text); 
				if((text.at(0) < 'a') || (text.at(0) > 'z')) {
					SplitRegion(region, 1, 0, L"splitByWordCount");
					nSplit++;
				}
			}
		}
	}
}

COcrTextPage::iterator COcrTextPage::SplitRegion(COcrBasicTextRegion* pBtr, UINT nSplit, UINT textFeature, wstring historyText){
	COcrBasicTextRegion* pBtrNew = pBtr->Split(nSplit);
	iterator it;
	if(pBtrNew) {
		pBtrNew->SetTextFeature(textFeature);
		it = insert(find(begin(), end(), pBtr), pBtrNew);
		((COcrTextRegion*)pBtrNew)->SetHistory(((COcrTextRegion*)pBtr)->GetHistory());
		((COcrTextRegion*)pBtr)->SetHistory(historyText);
		pBtr->SetTextFeature(EMBEDDED_FEATURE, false);
	}
	return it + 2;
}

void COcrTextPage::GetFilteredText(wstring& sText, UINT nBegin, const_iterator *piend, Dictionary* pDict){
	wstring tmpText;
	sText.erase();

	const_iterator iend = end();
	if(piend) iend = *piend;

	if(nBegin >= size())
		return;
	for(const_iterator i = begin() + nBegin; i != iend; ++i){
		if(*i && ((*i)->GetTextFeatures() & (COMPLETELY_OCRED | PARTIALLY_OCRED))) {
			for(const_iterator j = (*i)->begin(); j != (*i)->end(); ++j){
				(*j)->SetTextFeature(FORCE_SINGLE, GetTextFeature(FORCE_SINGLE));
				(*j)->GetFilteredText(tmpText, pDict);
				sText.append(tmpText);
			}
		}
	}
}

void COcrTextPage::GetFilteredText(wstring& sText, UINT nBegin, const_iterator *piend, Dictionary* pDict,
								   vector<int> *pWords){
	wstring tmpText;
	sText.erase();

	const_iterator iend = end();
	if(piend) iend = *piend;

	if(nBegin >= size())
		return;
	for(const_iterator i = begin() + nBegin; i != iend; ++i){
		if(*i && ((*i)->GetTextFeatures() & (COMPLETELY_OCRED | PARTIALLY_OCRED))) {
			for(const_iterator j = (*i)->begin(); j != (*i)->end(); ++j){
				(*j)->SetTextFeature(FORCE_SINGLE, GetTextFeature(FORCE_SINGLE));
				((COcrTextLine*)*j)->GetFilteredText(tmpText, pDict, pWords);
				sText.append(tmpText);
			}
		}
	}
}

void COcrTextPage::SetLineConfidences(Dictionary* pDict){
	for(iterator i = begin(); i != end(); ++i){
		if(*i){
			((COcrTextRegion*)(*i))->SetLineConfidences(pDict);
		}
	}
}

void COcrTextPage::AddFeatureToLastRegion(UINT nFeature){
	if(size() < 1) return;
	((COcrTextRegion*)(*rbegin()))->SetTextFeature(nFeature);
}

void COcrTextPage::SetBoundRectOfTheLastRegion(CRect rect){
	if(size() < 1) return;
	(*rbegin())->CopyRect(rect);
}

void COcrTextPage::Rotate180(int nImgWidth, int nImgHeight){
	if(empty()) return;
	int i, j;
	for(i = 0; i < int(size()); ++i){
		if(at(i)->GetTextFeatures() & EMBEDDED_FEATURE){
			break;
		}
	}

	int nLast = i;

	for(i = 0, j = nLast - 1; i < j; ++i, --j){
		COcrBasicTextRegion *pTmp = at(i);
		at(i) = at(j);
		at(j) = pTmp;
		at(i)->CopyRect(RotateRect180(*at(i), nImgWidth, nImgHeight));
		at(j)->CopyRect(RotateRect180(*at(j), nImgWidth, nImgHeight));
	}
	for(i = nLast, j = int(size()) - 1; i < j; ++i, --j){
		COcrBasicTextRegion *pTmp = at(i);
		at(i) = at(j);
		at(j) = pTmp;
		at(i)->CopyRect(RotateRect180(*at(i), nImgWidth, nImgHeight));
		at(j)->CopyRect(RotateRect180(*at(j), nImgWidth, nImgHeight));
	}
}
	
void COcrTextPage::Reverse(){
	if(size() < 2) return;
	for(int i = 0, j = size() - 1; i < j; ++i, --j){
		COcrBasicTextRegion *pTmp = at(i);
		at(i) = at(j);
		at(j) = pTmp;
	}
}

void COcrTextPage::RecalBoundBoxes(){
	for(iterator i = begin(); i != end(); ++i){
		if(*i)((COcrTextRegion*)(*i))->RecalBoundBox();
	}
}

COcrTextPage::iterator COcrTextPage::SubstSplitedRegion(iterator iregion, COcrTextPage &newRegions){
	int nSize = newRegions.size();
	if(nSize < 1){
		return iregion + 1;
	}
	wstring sText;
	reverse_iterator i;
	for(i = newRegions.rbegin(); i != newRegions.rend(); ++i){
		if(*i){
			(*i)->GetText(sText);
		}
		if(!sText.empty()){
			break;
		}
	}
	if(i == newRegions.rend()){
		return iregion + 1;
	}
	UINT nFeatures = (*iregion)->GetTextFeatures();
	if(*iregion){
		delete *iregion;
	}
	*iregion =  *i;
	(*iregion)->SetTextFeature(nFeatures);
	*i = NULL;
	nSize = 1;
	for(++i; i != newRegions.rend(); ++i){
		sText.erase();
		if(*i){
			(*i)->GetText(sText);
		}
		if(!sText.empty()){
			iregion  = insert(iregion, *i);
			(*i)->SetTextFeature(nFeatures);
			(*i) = NULL;
			++nSize;
		}
	}
	newRegions.clear();
	return iregion + nSize;
}

void COcrTextPage::AddTextFeatureToRegions(UINT nFeature){
	for(iterator i = begin(); i != end(); ++i){
		if(*i){
			(*i)->SetTextFeature(nFeature);
		}
	}
}

void COcrTextPage::MergeNotEmpty(COcrBasicTextRegion *pBtr){
	wstring sText;
	pBtr->GetTaggedText(sText);
	if(sText.empty()) return;
	UnionRect(*this, *pBtr);
	for(iterator i = pBtr->begin(); i != pBtr->end(); ++i){
		if(*i){
			(*i)->GetTaggedText(sText);
			if(sText.empty())continue;
			push_back(*i);
			(*i)->SetParent(this);
			*i = NULL;
		}
	}
}
