#ifndef NODE_INCLUDED
#define NODE_INCLUDED

typedef SRID ITEM;

#define	citemMost		   		300
#define	citemMax		   		301
#define	citagSonMax				ctagMax
#define	cbFOPNoSon				( sizeof(TAG) + 1 + 1 )		// 6
#define	cbFOPOneSon				( cbFOPNoSon + 1 + 1 )		// 8
/*	most bytes available for page subtree not including FOP son
/**/
#define	cbAvailMost				( cbPage - sizeof(PGHDR) - sizeof(PGTRLR) - cbFOPNoSon - 1 )
								// 4096 - 28 - 4 - 6 - 1 /* for FOP son count */  = 4057
/*	most bytes for node key and data, backlink included.
/**/
#define	cbNodeMost				( cbAvailMost - 1 - 1 - sizeof(TAG) - sizeof(SRID) )
								// 4057 - 1 /* FOP son */ - 1 - 4 - 4 = 4047
/*	bytes for Null key and no data, backlink excluded.
/**/
#define	cbNullKeyData			( cbFOPOneSon - cbFOPNoSon + sizeof(TAG) + 1 + 1 )
								//  8 - 6 + 4 + 1 + 1 = 8
								//	cbNullKeyData is for inserted nodes and
								//	does not have backlink
/*	most bytes for long value chunk with 4 byte key.
/**/
#define	cbChunkMost 			( cbNodeMost - cbNullKeyData - sizeof(LONG) )
								// 4047 - 8 /* node null key data */ - 4 /* key */ = 4035
#define	cbItemNodeMost			( 1 + 1 + JET_cbKeyMost + 0 + 0 + sizeof(SRID) + (citemMax * sizeof(SRID)) )
								// 261 + 300 * 4 = 1461
#define	cbHalfItemNodeMost 		( 1 + 1 + JET_cbKeyMost + 0 + 0 + sizeof(SRID) + ((((citemMax + 1) / 2) + 1 ) * sizeof(SRID)) )
								//  261 + 151 * 4 = 865
#define	cbFirstPagePointer		( 1 + sizeof(PGNO) )

//	node header bits
#define fNDVersion		  		0x80
#define fNDDeleted		  		0x40
#define fNDBackLink		  		0x20
#define fNDReserved				0x10	// Reserved for future use (formerly fNDFDPPtr)
#define fNDSon					0x08
#define fNDVisibleSon	  		0x04
#define fNDFirstItem  	  		0x02
#define fNDLastItem	  	  		0x01

#define	FNDDeleted(b)	 		( (b) & fNDDeleted )
#define	FNDVersion(b)	 		( (b) & fNDVersion )
#define	FNDVerDel(b)			( (b) & (fNDDeleted | fNDVersion) )

#define	FNDBackLink(b)	 		( (b) & fNDBackLink )
#define	FNDNullSon(b)	 		( !( (b) & fNDSon ) )
#define	FNDSon(b)	 			( (b) & fNDSon )

#define FNDReserved(b)			( (b) & fNDReserved )
#define	FNDFDPPtr(b) 			FNDReserved(b)
#define	FNDVisibleSons(b)		( (b) & fNDVisibleSon )
#define	FNDInvisibleSons(b) 	( !( (b) & fNDVisibleSon ) )

#define	FNDFirstItem(b)		   	( (b) & fNDFirstItem )
#define	FNDLastItem(b)		   	( (b) & fNDLastItem )

//	node flag toggle macros

#define	NDSetDeleted(b) 	   	( (b) |= fNDDeleted )
#define	NDResetDeleted(b)	   	( (b) &= ~fNDDeleted )

#define	NDSetVersion(b) 	   	( (b) |= fNDVersion )
#define	NDResetVersion(b)	   	( (b) &= ~fNDVersion )

#define	NDSetSon(b)			   	( (b) |= fNDSon )
#define	NDResetSon(b)		   	( (b) &= ~fNDSon )

#define	NDSetKeyLength(pb, cb) 	( *(pb) = cb )

#define	NDSetVisibleSons(b)	   	( (b) |= fNDVisibleSon )
#define	NDSetInvisibleSons(b)  	( (b) &= ~fNDVisibleSon )

#define	NDSetBackLink(b)   	   	( (b) |= fNDBackLink )
#define	NDResetBackLink(b) 	   	( (b) &= ~fNDBackLink )

#define	NDSetFirstItem(b)	   	( (b) |= fNDFirstItem )
#define	NDResetFirstItem(b)	   	( (b) &= ~fNDFirstItem )

#define	NDSetLastItem(b)	   	( (b) |= fNDLastItem )
#define	NDResetLastItem(b)	   	( (b) &= ~fNDLastItem )

//	macros
#define StNDKey(pb)					( (pb) + 1 )
#define PbNDKeyCb(pb)		  		( (pb) + 1 )
#define PbNDKey(pb)					( (pb) + 2 )
#define CbNDKey(pb)					( *( (pb) + 1 ) )
#define PbNDSonTable(pb)	  		( (pb) + 1 + 1 + *(pb + 1) )
#define PbNDSon(pb)			  		( (BYTE *)PbNDSonTable(pb) + 1 )
#define CbNDSonTable(pbSonTable)  	( *(BYTE *)(pbSonTable) )
#define CbNDSon(pbNode) \
	( FNDNullSon(*(pbNode)) ? 0 : *PbNDSonTable(pbNode) )
#define PgnoNDOfPbSon(pb) 		 	( *(PGNO UNALIGNED *)PbNDSon(pb) )
#define FNDNonIntrinsicSons(pbNode)	( !FNDNullSon(*pbNode) && ( FNDVisibleSons(*pbNode) || CbNDSon(pbNode) > 1 ) )
#define FNDIntrinsicSons(pbNode)	( !FNDNullSon(*pbNode) && !FNDVisibleSons(*pbNode) && CbNDSon(pbNode) == 1 )
#define PbNDBackLink(pb)											\
	( PbNDSonTable(pb) + ( FNDNullSon( *(pb) ) ? 0 :				\
	( ( ( *PbNDSonTable(pb) == 1 ) && FNDInvisibleSons( *(pb) ) ) ?	\
	sizeof(PGNO) + 1 : *PbNDSonTable(pb) + 1 ) ) )
#define PbNDData(pb) \
	( PbNDBackLink(pb) + ( FNDBackLink( *(pb) ) ? sizeof(SRID) : 0 ) )
#define CbNDData( pbNode, cbNode )	( (cbNode) - (INT)( PbNDData(pbNode) - (pbNode) ) )
#define ItagSonOfPbND(pb,ib)		( PbNDSonTable(pb)[ib] )
#define ItagSonOfPbSonTable(pb,ib) 	( pb[ib+1] )
#define ItagSonOfPbSon(pb,ib)		( pb[ib] )

#define NDGet( pfucb, itagT )		PMGet( &(pfucb)->ssib, itagT )

#ifdef DEBUG

#define AssertNDIntrinsicSon( pbNode, cbNode )							\
		{																\
		Assert( ( PbNDData( (pbNode) )) - (pbNode) <=					\
			(INT)(cbNode) ); 											\
		Assert( ( PgnoNDOfPbSon( pbNode ) & 0xff000000 ) == 0 );		\
		}																				

#define AssertNDGet( pfucb, itagT )										\
		{																\
		AssertPMGet( &(pfucb)->ssib, itagT ); 							\
		}

VOID AssertNDGetKey( FUCB *pfucb, INT itag );

VOID AssertNDGetNode( FUCB *pfucb, INT itag );

#else

#define AssertNDIntrinsicSon( pbNode, cbNode )
#define AssertNDGet( pfucb, itag )
#define AssertNDGetKey( pfucb, itag )
#define AssertNDGetNode( pfucb, itag )

#endif

#define NDIGetBookmarkFromCSR( pfucb, pcsr, psrid )												\
	{																							\
	if ( FNDBackLink( *((pfucb)->ssib.line.pb) ) )								\
		{																						\
		*(SRID *)(psrid) = *(SRID UNALIGNED *)PbNDBackLink((pfucb)->ssib.line.pb);	\
		Assert( PgnoOfPn(*(SRID *)psrid) != pgnoNull );							\
		}																						\
	else																						\
		{																						\
		*(psrid) = SridOfPgnoItag( (pcsr)->pgno, (pcsr)->itag );		 										\
		}																						\
	}

#define NDIGetBookmark( pfucb, psrid ) 		NDIGetBookmarkFromCSR( pfucb, \
													PcsrCurrent( pfucb ), \
													psrid )

#define NDGetBookmark( pfucb, psrid ) 		NDGetBookmarkFromCSR( pfucb,	\
													PcsrCurrent( pfucb ),	\
													psrid )

/*	item bits and macros.
/**/
#define fNDItemDelete					0x40000000
#define fNDItemVersion					0x80000000

#define FNDItemVersion( item )		( (item) & fNDItemVersion )
#define ITEMSetVersion( item )		( (item) |= fNDItemVersion )
#define ITEMResetVersion( item )		( (item) &= ~(fNDItemVersion) )

#define FNDItemDelete( item ) 		( (item) & fNDItemDelete )
#define ITEMSetDelete( item )			( (item) |= fNDItemDelete )
#define ITEMResetDelete( item )		( (item) &= ~(fNDItemDelete) )

#define BmNDOfItem( item )				((item) & ~( fNDItemVersion | fNDItemDelete ) )

#define	CitemND(pb, cb) \
	( ( cb - ( PbNDData(pb) - pb ) ) / sizeof(ITEM) )

#define	CitemNDData(pb, cb, pbData)	\
	( ( cb - ( (pbData) - pb ) ) / sizeof(ITEM) )

#define FNDSingleItem( pfucb )											\
	( ( (pfucb)->ssib.line.cb -		 									\
	( PbNDData( (pfucb)->ssib.line.pb ) - (pfucb)->ssib.line.pb ) )  	\
	/ sizeof(ITEM) == 1	)

//	LSridCmp
//	========================================================================
//	LONG LSridCmp( SRID *psrid1, SRID *psrid2 )
//
//	Compare the srids.
//
//	PARAMETERS	psrid1		   pointer to a item;
//					psrid2		   pointer to a item;
//
//	RETURNS		< 0, then the first srid is less than the second.
//					= 0, then the first srid is equal to the the second.
//					> 0, then the first srid is greater than the second.
//-
#define LSridCmp( srid1, srid2 )										\
	((LONG) ((SRID) BmNDOfItem( srid1 ) - (SRID) BmNDOfItem( srid2 )))

#ifdef DEBUG
	VOID NDCheckTreeInPage( PAGE *ppage, INT itagFather );
	#define	NDCheckPage( pssib )		NDCheckTreeInPage( (pssib)->pbf->ppage, itagFOP )
#else
	#define NDCheckTreeInPage( ppage, itagFather )
	#define NDCheckPage( pssib )
#endif

#define NDGetKey( pfucb )												\
	{																	\
	AssertNDGet( pfucb, PcsrCurrent(pfucb)->itag );	   					\
	(pfucb)->keyNode.cb = (INT)*((BYTE *)(pfucb)->ssib.line.pb + 1);	\
	(pfucb)->keyNode.pb = ( (BYTE *)(pfucb)->ssib.line.pb + 2 );		\
	}

//	node prototypes
ERR ErrNDNewPage( FUCB *pfucb, PGNO pgno, PGNO pgnoFDP, PGTYP pgtyp, BOOL fVisibleSons );
ERR ErrNDSetNodeHeader( FUCB *pfucb, BYTE bHeader );
VOID NDSeekSon( FUCB *pfucb, CSR *pcsr, KEY const *pkey, INT fFlags );
VOID NDMoveFirstSon( FUCB *pfucb, CSR *pcsr );
VOID NDMoveLastSon( FUCB *pfucb, CSR *pcsr );
ERR ErrNDMoveSon( FUCB *pfucb, CSR *pcsr );
VOID NDGetNode( FUCB *pfucb );
VOID NDGetBookmarkFromCSR( FUCB *pfucb, CSR *pcsr, SRID *psrid );

ERR ErrNDInsertNode( FUCB *pfucb, KEY const *pkey, LINE *plineData, INT fHeader, INT fFlags );
ERR ErrNDDeleteNode( FUCB *pfucb );
ERR ErrNDReplaceWithLink( FUCB *pfucb, SRID sridLink );
ERR ErrNDDeleteInvisibleSon(
		FUCB  	*pfucb,
		RMPAGE	*prmpage,
		BOOL  	fCheckRemoveParentOnly,
		BOOL  	*pfRmParent );
ERR ErrNDFlagDeleteNode( FUCB *pfucb, INT fFlags );
ERR ErrNDReplaceNodeData( FUCB *pfucb, LINE *pline, INT fFlags );
VOID NDResetNodeVersion( FUCB *pfucb );
VOID NDDeferResetNodeVersion( FUCB *pfucb );
VOID NDResetNodeDeleted( FUCB *pfucb );

ERR ErrNDGetItem( FUCB *pfucb );
ERR ErrNDFirstItem( FUCB *pfucb );
ERR ErrNDLastItem( FUCB *pfucb );
ERR ErrNDNextItem( FUCB *pfucb );
ERR ErrNDPrevItem( FUCB *pfucb );
ERR ErrNDSeekItem( FUCB *pfucb, SRID srid );

#define fNDCitemAll			(1<<0)
#define fNDCitemFromIsrid	(1<<1)
#define fNDCitemToIsrid		(1<<2)
INT CitemNDThere( FUCB *pfucb, BYTE fNDCitem, INT isrid );

ERR ErrNDInsertItemList( FUCB *pfucb, KEY *pkey, SRID srid, INT fFlags );
ERR ErrNDInsertItem( FUCB *pfucb, ITEM item, INT fFlags );
ERR ErrNDInsertItems( FUCB *pfucb, ITEM *rgitem, INT citem );
ERR ErrNDFlagInsertItem( FUCB *pfucb );											
ERR ErrNDDeleteItem( FUCB *pfucb );
ERR ErrNDFlagDeleteItem( FUCB *pfucb, BOOL fNoMPLRegister );
VOID NDSetItemDelete( FUCB *pfucb );
VOID NDResetItemVersion( FUCB *pfucb );
VOID NDResetItemDelete( FUCB *pfucb );
ERR ErrNDSplitItemListNode( FUCB *pfucb, INT fFlags );
ERR ErrNDDelta( FUCB *pfucb, LONG lDelta, INT fFlags );
ERR ErrNDDeltaNoCheckLog( FUCB *pfucb, LONG lDelta, INT fFlags );
ERR	ErrNDLockRecord( FUCB *pfucb );

ERR ErrNDInsertWithBackLink( FUCB *pfucb, BYTE bFlags, KEY const *pkey,
	LINE *plineSonTable, SRID sridBackLink, LINE *plineData );
VOID NDGetBackLink( FUCB *pfucb, PGNO *ppgno, INT *pitag );
ERR ErrNDExpungeBackLink( FUCB *pfucb );
ERR ErrNDExpungeLinkCommit( FUCB *pfucb, FUCB *pfucbSrc );
VOID NDGetItagFatherIbSon( INT *pitagFather, INT *pibSon, PAGE *ppage, INT itag );

#define FNDFreePageSpace( pssib, cbT )							\
		( ((INT)(pssib)->pbf->ppage->cbFree) < cbT ?		\
		fFalse :												\
		FVERCheckUncommittedFreedSpace( (pssib)->pbf, cbT ) )
INT CbNDFreePageSpace( BF *pbf );

BOOL FNDMaxKeyInPage( FUCB *pfucb );

#endif		// NODE_INCLUDED