//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include "TableViewUnit.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
//---------------------------------------------------------------------------
#define CelOFF 	   1
#define HgtERROR   0
//---------------------------------------------------------------------------
__fastcall TTableView::TTableView(TComponent* Owner) : TScrollView(Owner)
{
	OnChangeSBPos = DoChgPos;
	OnChangeSize  = DoChgSize;
	OnHgtTitle    = DoHgtTitle;
	OnHgtBottom   = DoHgtBottom;

	FIndFocus    = NOSET;
	FIndSel      = NOSET;
	FCount       = 0;
	FHgtSmall    = NOSET;
	m_Title      = NULL;
	m_Bottom     = NULL;

	OnMouseWheel = DoMouseWheel;
}
//---------------------------------------------------------------------------
__fastcall TTableView::~TTableView()
{
	DoTableInit();
}
//---------------------------------------------------------------------------
bool __fastcall TTableView::ReloadData(int iCellIndex, SBS iSBS, int iTopline)
{
	bool bRet = true;

	DoTableInit();

	if( FOnTitle ) {  //set title

		m_Title = (TPanelDB *)FOnTitle(this);
		if( m_Title ) m_Title->Parent = this;
	}

	if( FOnBottom ) {  //set Bottom

		m_Bottom = (TPanelDB *)FOnBottom(this);
		if( m_Bottom ) m_Bottom->Parent = this;
	}

	if( FOnCellHgt && FOnMDNum && FOnMDCell )
	{
		m_DBNum = FOnMDNum(this); //set data num
		if( m_DBNum<=0 ) bRet = false;

		if( bRet ) {

			for( int index=0; index<m_DBNum; index++ ){ //set data show height

				int iCurHgt = FOnCellHgt(this, index);
				if( iCurHgt<=HgtERROR ) {
					bRet = false;    //cell nHeight error
					m_VCHGT.clear();
					break;
				}
				m_VCHGT.push_back(iCurHgt);
			}

			if( bRet ) {

				DoLstInitial();
				SetSB(m_PosTotal, m_PosPage, m_PosBegin);

				TStateBtn *pCellTmp = NULL;
				for(int index=0; index<m_DBNeed; index++){ //set data cell content

					int iAdCell = FOnMDCell(this, index);
					pCellTmp    = (TStateBtn *)iAdCell;
					if( pCellTmp ) m_LstBtn.push_back(pCellTmp);
				}

				if( OnChange ) OnChange(this);

				if( NOSET!=iCellIndex )  { SetCellInd(iCellIndex); }  //set begin show pos
				else if( SBS_NONE!=iSBS) { SetSBPos(iSBS, iTopline); }
				else {
					DoLstChg();
				}
			}
		}

	}else bRet = false;  //cell num error

	if( !bRet ) {
		FIndSel = NOSET;
		SetSB(0, 0, 0);
		Paint();
	}

	return bRet;
}
//---------------------------------------------------------------------------
bool __fastcall TTableView::DoLstInitial()
{
	if( 0==m_VCHGT.size() ) return false;

	int nHeight = getContentHeight();

	// get smallest nHeight
	int iSmlHgt = m_VCHGT[0];
	for( int i=1; i<m_VCHGT.size(); i++ ) {
		if( iSmlHgt>m_VCHGT[i] ) iSmlHgt = m_VCHGT[i];
	}

	int iSmlHgtTmp = iSmlHgt;
	if(iSmlHgt>nHeight)
		iSmlHgtTmp = iSmlHgt = (iSmlHgt-nHeight)%nHeight;
	if( (HgtSmall!=NOSET) && (HgtSmall<iSmlHgtTmp) )
		iSmlHgtTmp = HgtSmall;

	//get each cell pos num range
	int  iCellShowHgt  = 0;
	int  iCellShowNum  = 0;
	int  iPosNumCur    = 0;
	int  iPosNumTotal  = 0;

	bool bSBOK = false;
	for( int i=0; i<m_VCHGT.size(); i++ ) {

		int iPlus     = ( (m_VCHGT[i]%iSmlHgtTmp)==0 ) ? 0:1;
		iPosNumCur    = m_VCHGT[i]/iSmlHgtTmp + iPlus;
		iPosNumTotal += iPosNumCur;

		m_VCPOS.push_back(iPosNumTotal);

		if(!bSBOK){

			iCellShowNum++;
			iCellShowHgt += m_VCHGT[i];

			if( iCellShowHgt>=nHeight ) {

				iCellShowNum = nHeight/iSmlHgt;
				m_PosPage = nHeight/iSmlHgtTmp;

				bSBOK = true;
			}
		}
	}
	//get sb info
	m_PosTotal = iPosNumTotal;

	//no sb will show
	if(iCellShowHgt<nHeight) m_PosPage = m_PosTotal;

	//smallest cell nHeight
	m_HgtSmall = iSmlHgtTmp;

	//cell visible num + off 2
	m_DBNeed = iCellShowNum + CelOFF*2;

	return true;
}
//---------------------------------------------------------------------------
bool __fastcall TTableView::DoTableInit()
{
	m_bTop = m_bBom = false;

	m_PosTotal = m_PosPage = m_PosBegin = 0;

	m_DBNum    = m_DBNeed = 0;
	m_IndTop   = m_IndBeg = 0;
	m_UIOffMerge = CelOFF;
	m_UIOff      = 0;

	m_HgtSmall = NOSET;
	m_HgtTop   = m_HgtOver = 0;


	m_VCHGT.clear();
	m_VCPOS.clear();
	m_VCCHG.clear();

	BtnClear();

	SAFE_DELETE(m_Title);
	SAFE_DELETE(m_Bottom);

	return true;
}
//---------------------------------------------------------------------------
void __fastcall TTableView::DoLstChg(CELLS eCELLS, int Offset)
{
	DoLstChgPos(eCELLS, Offset);
	DoLstChgCel(eCELLS);
}
//---------------------------------------------------------------------------
void __fastcall TTableView::DoLstChgPos(CELLS eCELLS, int Offset)
{

	bool bPage       = false;
	bool bDown       = true;
	bool bMerge      = true;      //get cell change list
	int  iMergePos   = 0;
	int  iCellChgNum = 0;
	int  iCellTop    = 0;         //get cell top value
	int  iIndTop     = m_IndTop;  //get cell index: top and begin

	switch(eCELLS)
	{
		case CELLS_Default:
		{
			m_IndTop = m_IndBeg = 0;
			m_HgtTop = 0;
			m_UIOff  = 0;
			return;
		} break;
		case CELLS_Page:
		{
			bPage    = true;
			bDown    = false;
			bMerge 	 = false;

			iIndTop  = m_IndTop = m_IndBeg;
			m_HgtTop = 0;
			m_UIOff  = 0;
		} break;
		case CELLS_Up:
		{
			bDown    = false;
			//--------------when just show > needs cell
			//merge
			if(m_UIOff-Offset<0)
			{
				bMerge 			   = true;
				if(m_UIOff!=0){
					iCellChgNum   = Offset - m_UIOff;
				}else{
					iCellChgNum   = Offset;
				}
				m_IndTop   -= iCellChgNum;
				m_UIOff  = 0;

			//--------------when just show between needs cell
			}else{

				bMerge 		 = false;
				iCellChgNum  = 0;
				m_UIOff     -= Offset;
			}
		} break;
		case CELLS_Down:
		{
			//--------------when just show > needs cell
			//merge
			if(m_UIOff+Offset>m_UIOffMerge ||
			   m_UIOff==1)
			{
				bMerge 			   = true;
				if(m_UIOff<m_UIOffMerge){
					iCellChgNum   =
					Offset - (m_UIOffMerge - m_UIOff);
				}else{
					iCellChgNum   = Offset;
				}
				m_IndTop   += iCellChgNum;
				m_UIOff  = m_UIOffMerge;

			//--------------when just show between needs cell
			}else{

				bMerge 			   = false;
				iCellChgNum     = 0;
				m_UIOff += Offset;
			}

			bDown    		   	   = true;
		} break;
		default: break;
	}

	int iCelInd = 0;
	BTNLST::iterator lst_FCell = m_LstBtn.begin();
	for( VCINT::iterator vc_FHgt = (m_VCHGT).begin() + iIndTop;
		 vc_FHgt   != m_VCHGT.end(),
		 lst_FCell != m_LstBtn.end();
		 vc_FHgt++,
		 lst_FCell++ )
	{
		if(bPage) { //move too fast, change all the cells

			if( iCelInd<m_DBNeed )
				m_VCCHG.push_back( iIndTop+iCelInd );
			iCelInd++;
			continue;
		}

		if(bMerge) //judge the mergePos, ready for merge the new celllist
		{
			if( (bDown&&(iIndTop+iCelInd==m_IndTop)) ||
				(!bDown&&(iIndTop+iCelInd==iIndTop+m_DBNeed-iCellChgNum)) )
				iMergePos = iCelInd;
		}

		//  m_VCCHG >> save the change cells's source index
		if(iCelInd<iCellChgNum) {

			if(bDown){ // down last cells change
				m_VCCHG.push_back(iIndTop+m_DBNeed+iCelInd);
			}else{     //  up  first cells change
				m_VCCHG.push_back(m_IndTop+iCelInd);
			}
		}

		//  [m_IndTop, m_IndBeg) >> iCellTop
		if( m_IndBeg-m_UIOffMerge<=iIndTop+iCelInd &&
		    iIndTop+iCelInd<m_IndBeg )
		{
			iCellTop += *vc_FHgt;
		}

		iCelInd++;
	}

	if(bPage) { return; }

	m_HgtTop = -iCellTop;

	if( bMerge ) {

		if( iMergePos ) {

			BTNLST::iterator lst_FCell;
			lst_FCell = m_LstBtn.begin();
			advance(lst_FCell, iMergePos);
			m_LstBtn.splice(m_LstBtn.begin(), m_LstBtn, lst_FCell, m_LstBtn.end());
		}
		else {
			int index = m_IndTop;
			m_VCCHG.clear();
			for( BTNLST::iterator lst_FCell  = (m_LstBtn).begin();
				 lst_FCell != (m_LstBtn).end();
				 lst_FCell++ )
			{
				m_VCCHG.push_back(index);
				index++;
			}
		}
	}
}
//---------------------------------------------------------------------------
void __fastcall TTableView::DoLstChgCel(CELLS eCELLS, bool bPaint)
{
	if(!FOnMDCell ) return;

	for( VCINT::iterator vc_FChg  = (m_VCCHG).begin();
		 vc_FChg != (m_VCCHG).end();
		 vc_FChg++ )
	{
		FOnMDCell(this, *vc_FChg);
	}

	if( bPaint ) Paint();

	m_VCCHG.clear();

	if( OnChange )
		OnChange(this);
}
//---------------------------------------------------------------------------
bool __fastcall TTableView::IsCellVisible(int iCellIndex)
{
	bool bVisible = true;

	if( iCellIndex<m_IndBeg ) bVisible = false;

	if( iCellIndex>=GetCellBom() ) bVisible = false;

	return bVisible;
}
//---------------------------------------------------------------------------
int __fastcall TTableView::getContentHeight()
{
	int nHeight = Height;
	if( m_Title ) nHeight -= m_Title->Height;
	if( m_Bottom ) nHeight -= m_Bottom->Height;
	return nHeight;
}
//---------------------------------------------------------------------------
TStateBtn* __fastcall TTableView::GetCellAds(int iCellIndex)
{
	TStateBtn *rowCellTmp = NULL;

	int index = 0;
	BTNLST::iterator lst_FCell;
	for( lst_FCell  = (m_LstBtn).begin();
		 lst_FCell != (m_LstBtn).end();
		 lst_FCell++)
	{
		if(m_IndTop+index == iCellIndex){
			rowCellTmp = *lst_FCell;
			break;
		}
		index++;
	}

	return rowCellTmp;
}
//---------------------------------------------------------------------------
int __fastcall TTableView::GetCellHgt(int iCellIndex)
{
	if( 0<=iCellIndex && iCellIndex<=m_DBNum-1 )
		return m_VCHGT[iCellIndex];

	return 0;
}
//---------------------------------------------------------------------------
int  __fastcall TTableView::GetCellBeg()
{
	return m_IndBeg;
}
//---------------------------------------------------------------------------
int __fastcall TTableView::GetCellBom()
{
	int nHeight = m_RtContent.Height();

	int indBeg = m_IndBeg;
	int iCurTop = m_HgtOver;
	int index = 0;
	for( ; index<m_DBNum; index++ ) {
		iCurTop += m_VCHGT[index];
		if( iCurTop>=nHeight ) break;
	}

	return indBeg+index+1;
}
//---------------------------------------------------------------------------
int __fastcall TTableView::GetCellNum()
{
	return m_DBNum;
}
//---------------------------------------------------------------------------
void __fastcall TTableView::SetCellInd(int iCellIndex)
{
	if( iCellIndex<0 || iCellIndex>m_DBNum-1) return;

	int iTopLine = 0;
	if( iCellIndex!=0 ) iTopLine = m_VCPOS[iCellIndex-1];

	SetSBPos(SBS_POS, iTopLine);
}
//---------------------------------------------------------------------------
void __fastcall TTableView::SetSBPos(SBS iSBS, int iTopline)
{
	short ScrollCode = iSBS;

	if(iSBS==SBS_TOP){

		iTopline   = 0;

	}else if(iSBS==SBS_BOTTOM){

		iTopline = m_PosTotal-m_PosPage;
	}

	DoChgPos(this, ScrollCode, iTopline);
	SetSB(m_PosTotal, m_PosPage, m_PosBegin);
}
//---------------------------------------------------------------------------
void __fastcall TTableView::DoChgPos(TObject *Sender, short ScrollCode, int iTopLine)
{
	if( iTopLine<0 ) {
      	iTopLine = 0;
	}
	else if(iTopLine>m_PosTotal-m_PosPage ) {
		iTopLine = m_PosTotal-m_PosPage;
	}

	if(  m_bTop && (ScrollCode==SB_LINEUP||ScrollCode==SB_PAGEUP ) ){
		if(FOnMDTop) FOnMDTop(this);
		return;
	}
	else if( m_bBom && (ScrollCode==SB_LINEDOWN||ScrollCode==SB_PAGEDOWN ) ){
		if(FOnMDBottom) FOnMDBottom(this);
		return;
	}
	else if( iTopLine<0 || iTopLine>m_PosTotal ) return;
	m_bTop    = (0==iTopLine)?true:false;
	m_bBom = (m_PosTotal-m_PosPage==iTopLine)?true:false;

	//get CELLS
	CELLS eCELLS;
	if(abs(m_PosBegin-iTopLine)>=m_PosPage+2) eCELLS = CELLS_Page;
	else if(m_PosBegin<iTopLine) 			  eCELLS = CELLS_Down;
	else if(m_PosBegin>iTopLine) 			  eCELLS = CELLS_Up;

	//get new m_IndBeg
	int iIndChange, iPosCur, iPosPre;
	iIndChange = iPosCur = iPosPre  = 0;

	int front, end, mid;
	front  = 0;
	end    = m_VCPOS.size()-1;
	mid    = (front+end)/2;
	VCINT::iterator vc_FPos = (m_VCPOS).begin();
	iPosCur = *(vc_FPos+mid);
	if(mid==0) iPosPre = 0;
	else       iPosPre = *(vc_FPos+mid-1);
	while( front<=end ) {

		if(iPosPre<=iTopLine && iTopLine<iPosCur) {
			iIndChange = mid;
        	break;
		}

		if(iPosCur<=iTopLine)      front = mid+1;
		else if(iPosCur>iTopLine)  end   = mid;

		mid     = (front+end)/2;
		iPosCur = *(vc_FPos+mid);
		if(mid==0) iPosPre = 0;
		else       iPosPre = *(vc_FPos+mid-1);
	}

	//get new m_IndBeg to it's pre index >> m_HgtOver
	int iPosBeginNum = 0;
	if( iIndChange != 0 ) iPosBeginNum = *(m_VCPOS.begin()+iIndChange-1);
	int iABS  = abs(iPosBeginNum-iTopLine);
	m_HgtOver = -iABS*m_HgtSmall;

	if( iIndChange!=m_IndBeg ) { //change m_IndBeg

		int indBeginTmp = m_IndBeg;
		m_IndBeg      = iIndChange;

		DoLstChg( eCELLS, abs(indBeginTmp-iIndChange) );

	}else {  //change m_IndBeg over hegiht

		Paint();
	}

	m_PosBegin = iTopLine;
}
//---------------------------------------------------------------------------
int  __fastcall TTableView::DoHgtTitle(TObject *Sender)
{
	if( m_Title) return m_Title->Height;
	return 0;
}
//---------------------------------------------------------------------------
int  __fastcall TTableView::DoHgtBottom(TObject *Sender)
{
	if( m_Bottom) return m_Bottom->Height;
	return 0;
}
//---------------------------------------------------------------------------
void __fastcall TTableView::DoPaintLst(TCanvas *pCanvas)
{
	int iTop, iLeft, iWidth, iHeight;
	iTop   = m_HgtTop + m_HgtOver + m_RtContent.Top;
	iLeft  = m_RtContent.Left;
	iWidth = m_RtContent.Width();

	int iIndex = 0;
	for(BTNLST::iterator lstBtn=m_LstBtn.begin(); lstBtn!=m_LstBtn.end(); lstBtn++) {

		if(m_IndTop+iIndex>m_VCHGT.size()-1) { break; }

		iHeight = m_VCHGT[m_IndTop+iIndex];

		(*lstBtn)->SetBounds( iLeft, iTop, iWidth, iHeight );
		(*lstBtn)->Paint(pCanvas);

		iTop += iHeight;
		iIndex++;
	}
}
//---------------------------------------------------------------------------
