#include "ccommon.h"
#include "genconst.h"
#include "ccustio.h"
#include "cbpltree.h"
#include "bplpriv.h"
#include "btrread.h"
#include "btrbase.h"
#include "checks.h"
#include        <memory.h>


 void Move(byte ,int ,int ,int ,char *,char *);

/*
@(SHF) Funzioni di base per il B+Tree
@($) ZPage  B+TREE

@(ID)
Azzera una pagina del B+TREE.
@(FD)
*/

	void                            ZPage(ActPage)
	Page                            *ActPage; /* pagina attuale */

		{ 

			ActPage->PA.PageHeader.TPage = EmptyPage;
			ActPage->PA.PageHeader.PathNumber = 0;
			ActPage->PA.PageHeader.Flags = 0;
			ActPage->PA.PageHeader.Father = NoLink;
			ActPage->PA.PageHeader.Prev = NoLink;
			ActPage->PA.PageHeader.Next = NoLink;
			ActPage->PA.PageHeader.NKey = 0;
			memset(ActPage->PA.AreaForKey, ' ', AreaLen);
		}

/*
@($) GetHeader  B+TREE

@(ID)
Legge l'header di un indice.
Restituisce il codice di errore.
@(FD)

@(ISV)
junk = variabile spazzatura per la chiamata della funzione "sleep".

w    = descrittore della finestra.

@(FSV)

*/

	int                                     GetHeader(FilCheck, HPage, LockMode, Err)
	FilCtrl                 *FilCheck; /* identificatore indice             */
	Page                            *HPage;    /* pagina dell' indice  */
	int                                     LockMode;  /* lock sull'header                  */
	int                                     *Err;      /* codice di errore                  */

	{
	  do
	  {
	    CRead(&FilCheck->Fil, (char *) HPage, 1L, LockMode);
	    if (TESTLOCK(FilCheck->Fil.IOR))
	      message_box("Sono in attesa dell' indice");
	  } while TESTLOCK(FilCheck->Fil.IOR);
	  return((*Err = FilCheck->Fil.IOR));
	}

/*
@($) PutHeader  B+TREE

@(ID)
Scrive l'header di un indice.
Restituisce il codice di errore.
@(FD)
*/

	int                                     PutHeader(FilCheck, HPage, LockMode, Err)
	FilCtrl                 *FilCheck; /* identificatore indice    */
	Page                            *HPage;    /* pagina in cui scrivere l'header */
	int                                     LockMode;  /* lock sull'header                */
	int                                     *Err;      /* errore                          */

			{
				CWrite(&FilCheck->Fil, (char *) HPage, 1L, LockMode);
				return((*Err = FilCheck->Fil.IOR));
			}

/*
@($) GetAPage  B+TREE

@(ID)
Legge una pagina generica dell'indice.
Restituisce il codice di errore.
@(FD)
*/

	int                                     GetAPage(FilCheck, ActPage, Pg, Err)
	FilCtrl                 *FilCheck; /* identificatore indice */
	Page                            *ActPage;  /* pagina letta          */
	RecNoType               Pg;        /* numero di pagina      */
	int                                     *Err;      /* codice di errore      */

		{
			CRead(&FilCheck->Fil, (char *) ActPage, Pg + FilCheck->FHead.CABlock + 1,
						NoLock);
			return((*Err = FilCheck->Fil.IOR)); 
		}

/*
@($) PutAPage  B+TREE

@(ID)
Scrive una pagina generica dell'indice.
Restituisce il codice di errore.
@(FD)
*/

	int                                     PutAPage(FilCheck, ActPage, Pg, Err)
	FilCtrl                 *FilCheck; /* identificatore indice */
	Page                            *ActPage;  /* pagina attiva         */
	RecNoType               Pg;        /* numero della pagina   */
	int                                     *Err;      /* codice errore         */

		{
			CWrite(&FilCheck->Fil, (char *) ActPage, Pg + FilCheck->FHead.CABlock + 1,
							NoLock);
			return((*Err = FilCheck->Fil.IOR)); 
		}

/*
@($) GetAKey  B+TREE

@(ID)
Estrae una chiave da una pagina.
@(FD)

@(ISV)
BgnItem = numero di byte precedenti alla chiave dall'inizio della pagina.
@(FSV)
*/

	void                            GetAKey(ActPage, KeyLen, Pstion, KKey, Index)
	Page                            ActPage; /* pagina attiva                    */
	int                                     KeyLen;  /* lunghezza della chiave           */
	int                                     Pstion;  /* numero chiave nella pagina       */
	TKey                            KKey;    /* Valore chiave (in output)        */
	RecNoType               *Index;  /* indice associato a questa chiave */

		{
			int                                BgnItem;

			BgnItem = (Pstion - 1) * (KeyLen + IndLen);
			strcpy(KKey, (char *)(ActPage.PA.AreaForKey + BgnItem));
			*Index = *((RecNoType *)(ActPage.PA.AreaForKey + BgnItem + KeyLen));
		}

/*
@($) PutAKey  B+TREE

@(ID)
Mette una chiave in una pagina.
@(FD)

@(ISV)
BgnItem = numero di byte precedenti alla chiave dall'inizio della pagina.
@(FSV)
*/

	void                            PutAKey(KKey, Index, Pstion, KeyLen, ActPage)
	TKey                            KKey;    /* valore della chiave             */
	RecNoType               Index;   /* indice                          */
	int                                     Pstion;  /* posizione nella quale scrivere la chiave */
	int                                     KeyLen;  /* lunghezza della chiave          */
	Page                            *ActPage;/* pagina attiva                   */

		{
			register int                                    BgnItem;

			BgnItem = (Pstion - 1) * (KeyLen + IndLen);
			strcpy((char *)(ActPage->PA.AreaForKey + BgnItem), KKey);
			*((RecNoType *)(ActPage->PA.AreaForKey + BgnItem + KeyLen)) = Index;
		}

/*
@($) Repos  B+TREE

@(ID)
Riposizione all'ultima chiave letta dall'ultima operazione.
@(FD)

@(ISV)
WKey, WKey1 = variabili di lavoro contenenti chiavi.

WPos        = variabile di lavoro contenente il numero di chiave nella pagina.

WPag        = variabile di lavoro contenente il numero di pagina attuale.

WInd,WInd1  = variabili di lavoro contenenti indici associati alle chavi Wkey.
@(FSV)

@(IN)
Serve solo in multiutenza (XENIX).
@(FN)
*/

	void                            Repos(FilCheck, BError)
	FilCtrl                 *FilCheck;  /* identificatore indice     */
	int                                     *BError;    /* codice errore (in output) */

	{
		TKey                    WKey, WKey1;
		int                             WPos = FilCheck->Pos, WError;
		RecNoType       WPag = FilCheck->CurPag, WInd, WInd1;

		if (GetAPage(FilCheck, &PathPage, WPag, BError) != NoErr) return;
		if (PathPage.PA.PageHeader.NKey < WPos) WPos = PathPage.PA.PageHeader.NKey ;
		GetAKey(PathPage, FilCheck->Base[FilCheck->PN].KeyLen, WPos, WKey, &WInd);
		if ((strcmp(WKey, FilCheck->Key) == 0) && (WInd == FilCheck->Ind)) return ;
		strcpy(WKey1, FilCheck->Key) ;
		WInd = FilCheck->Ind ;
		BTrRead(FilCheck, FilCheck->Key, WKey, &WInd1, BError) ;
		if (*BError == BTrEmptyTree) return ;
		if (FilCheck->Base[FilCheck->PN].DupKeys == TRUE)
		{
			while ((strcmp(WKey, WKey1) == 0) && (WInd1 < WInd))
			{
				if (WPos < PathPage.PA.PageHeader.NKey) WPos++;
				else
					if (PathPage.PA.PageHeader.Next == NoLink) break;
					else
					{
						WPag = PathPage.PA.PageHeader.Next;
						if (GetAPage(FilCheck, &PathPage, WPag, BError) != NoErr) return;
						WPos = 1;
					}
				GetAKey(PathPage, FilCheck->Base[FilCheck->PN].KeyLen, WPos, WKey, &WInd1);
			}
		}
		if (GetAPage(FilCheck, &PathPage, WPag, &WError) != NoErr) return;
		if ((strcmp(WKey, WKey1) == 0) && (WInd == WInd1))
		{
			FilCheck->CurPag = WPag;
			FilCheck->Pos = WPos;
		}
		else 
		{
			if (*BError != BTrEOF)
			{
				FilCheck->CurPag = WPag;
				FilCheck->Pos = WPos;
				strcpy(FilCheck->Key, WKey) ;
				FilCheck->Ind = WInd1;
				*BError = BTrKeyNotFound;
			}
		}
	}

/*
@($) BTrNext  B+TREE

@(ID)
Restituisce la chiave successiva all'ultima letta.
@(FD)

@(ISV)
WKey = variabile di lavoro contenente la chiave.

- Versione DOS e XENIX
@(FSV)
*/

	void                            BTrNext(FilCheck, KKey, IndSr, BError)
	FilCtrl                 *FilCheck; /* identificatore indice   */
	TKey                            KKey;      /* valore della chiave     */
	RecNoType               *IndSr;    /* indice associato        */
	int                                     *BError;   /* codice errore           */

		{
			TKey                    WKey;

			if (FilCheck->CurPag == NoLink)
			{
				MaxKey(FilCheck->Base[FilCheck->PN].KeyLen, FilCheck->Key);
				BTrRead(FilCheck, FilCheck->Key, WKey, IndSr, BError) ;
				*BError = BTrEOF;
			}
			else
			{
#ifdef DOS
				if (adelete)
				{
					adelete = FALSE;
#endif
					Repos(FilCheck, BError) ;
					if (*BError != NoErr)
					{
						if (*BError == BTrKeyNotFound)
						{
							strcpy(KKey,FilCheck->Key);
							*IndSr = FilCheck->Ind ;
							*BError = NoErr ;
						}
						return;
					}
#ifdef DOS
				}
				else
					if (GetAPage(FilCheck, &PathPage, FilCheck->CurPag, BError) != NoErr) return;
#endif
				if (FilCheck->Pos < PathPage.PA.PageHeader.NKey)
				{
					FilCheck->Pos++;
					GetAKey(PathPage, FilCheck->Base[FilCheck->PN].KeyLen, FilCheck->Pos, FilCheck->Key, IndSr);
				}
				else
				{
					if (PathPage.PA.PageHeader.Next == NoLink)
					{
						GetAKey(PathPage, FilCheck->Base[FilCheck->PN].KeyLen, FilCheck->Pos, FilCheck->Key, IndSr);
						/*MaxKey(FilCheck->Base[FilCheck->PN].KeyLen, FilCheck->Key);*/
						*BError = BTrEOF;
					}
					else
					{
						FilCheck->CurPag = PathPage.PA.PageHeader.Next;
						if (GetAPage(FilCheck, &PathPage, FilCheck->CurPag, BError) != NoErr) return;
						FilCheck->Pos = 1;
						GetAKey(PathPage, FilCheck->Base[FilCheck->PN].KeyLen, FilCheck->Pos, FilCheck->Key, IndSr);
					}
				}
				strcpy(KKey,FilCheck->Key);
				FilCheck->Ind = *IndSr ;
			}
		}

/*
@($) BTrPrev  B+TREE

@(ID)
Restituisce la chiave successiva all'ultima.
@(FD)

@(ISV)
WKey = variabile di lavoro contenente una chiave.

- Versione DOS e XENIX
@(FSV)
*/

	void                            BTrPrev(FilCheck, KKey, IndSr, BError)
	FilCtrl                 *FilCheck; /* identificatore indice  */
	TKey                            KKey;      /* valore della chiave (in output) */
	RecNoType               *IndSr;    /* indice associato       */
	int                                     *BError;   /* codice di errore       */

		{
			TKey                    WKey;

			if (FilCheck->CurPag == NoLink)
			{
				MinKey(FilCheck->Base[FilCheck->PN].KeyLen, FilCheck->Key);
				BTrRead(FilCheck, FilCheck->Key, WKey, IndSr, BError) ;
				*BError = BTrBOF;
			}
			else
			{
#ifdef DOS
				if (adelete)
				{
					adelete = FALSE;
#endif
					Repos(FilCheck, BError) ;
					if ((*BError != NoErr) && (*BError != BTrKeyNotFound))
					{
						if (*BError == BTrEOF)
						{
							strcpy(KKey,FilCheck->Key);
							*IndSr = FilCheck->Ind ;
							*BError = NoErr ;
						}
						return;
					}
#ifdef DOS
				}
				else
					if (GetAPage(FilCheck, &PathPage, FilCheck->CurPag, BError) != NoErr) return;
#endif
				*BError = NoErr;
				if (FilCheck->Pos > 1)
				{
					FilCheck->Pos--;
					GetAKey(PathPage, FilCheck->Base[FilCheck->PN].KeyLen, FilCheck->Pos, FilCheck->Key, IndSr);
				}
				else
				{
					if (PathPage.PA.PageHeader.Prev == NoLink)
					{
						GetAKey(PathPage, FilCheck->Base[FilCheck->PN].KeyLen, FilCheck->Pos, FilCheck->Key, IndSr);
						/*MinKey(FilCheck->Base[FilCheck->PN].KeyLen, FilCheck->Key);*/
						*BError = BTrBOF;
					}
					else
					{
						FilCheck->CurPag = PathPage.PA.PageHeader.Prev;
						if (GetAPage(FilCheck, &PathPage, FilCheck->CurPag, BError) != NoErr) return;
						FilCheck->Pos = PathPage.PA.PageHeader.NKey;
						GetAKey(PathPage, FilCheck->Base[FilCheck->PN].KeyLen, FilCheck->Pos, FilCheck->Key, IndSr);
					}
				}
				strcpy(KKey, FilCheck->Key);
				FilCheck->Ind = *IndSr ;
			}
		}

/*
@($) Move  B+TREE

@(ID)
Sposta a destra od a sinistra un numero di byte "NByt" da una sorgente "Buff1"
ad una destinazione "Buff2".
@(FD)

@(ISV)
w = area di transito.
@(FSV)
*/

	void                            Move(RghOrLft, NByt, BytBeg, BytDest, Buff1, Buff2)
	byte                            RghOrLft; /* destra o sinistra */
	int                                     NByt;     /* numero di byte da spostare */
	int                                     BytBeg;   /* num. byte inizio sorgente  */
	int                                     BytDest;  /* num. byte inizio destinazione */
	AreaKey                 Buff1;    /* sorgente                      */
	AreaKey                 Buff2;    /* destinazione                  */

		{
			AreaKey         w;

			memcpy(w, &Buff1[BytBeg], NByt);
			memcpy(&Buff2[BytDest], w, NByt);
		}

/*
@($) ShiftPage  B+TREE

@(ID)
Esegue uno spostamento di un certo numero di posizioni (coppie chiave-puntatore)
all'interno della pagina.
@(FD)

@(ISV)
Len = lunghezza di una coppia chiave-puntatore.

Beg = numero di byte dall'inizio della pagina alla chiave "StartK" (esclusa).

Dest =  byte al quale si deve postare la coppia chiave-puntatore.
@(FSV)
*/

	void                            ShiftPage(RghOrLft, StartK, NPos, MxKey, KeyLen, Page1)
	byte                            RghOrLft; /* destra o sinistra  */
	int                                     StartK;   /* chiave di partenza */
	int                                     NPos;     /* numero di posizioni da saltare  */
	int                                     MxKey;    /* numero max chiavi in una pagina */
	int                                     KeyLen;   /* lunghezza di una chiave         */
	Page                            *Page1;   /* pagina attiva                   */

	{
		register int                                    Len = KeyLen + IndLen;
		register int                                    Beg = (StartK - 1) * Len;
		register int                                    Dest;

		if (RghOrLft == RightDir)
		{
			Dest = Beg + NPos * Len;
			if (Page1->PA.PageHeader.NKey + NPos <= MxKey)
			{
				Move(RightDir, (Page1->PA.PageHeader.NKey - StartK + 1) * Len, Beg, Dest, Page1->PA.AreaForKey, Page1->PA.AreaForKey);
				Page1->PA.PageHeader.NKey += NPos;
			}
		}
		else
		{
			Dest = Beg - NPos * Len;
			if (StartK - NPos > 0)
			{
				if (StartK <= Page1->PA.PageHeader.NKey)
					Move(LeftDir, (Page1->PA.PageHeader.NKey - StartK + 1) * Len, Beg, Dest, Page1->PA.AreaForKey, Page1->PA.AreaForKey);
				Page1->PA.PageHeader.NKey -= NPos;
				memset(&Page1->PA.AreaForKey[Page1->PA.PageHeader.NKey * Len], ' ', NPos * Len);
			}
		}
	}

/*
@($) ShiftOut  B+TREE

@(ID)
Sposta un certo numero di chiavi da una pagina.
@(FD)

@(ISV)
Len = lunghezza coppia chiave-puntatore.
@(FSV)
*/

	void                            ShiftOut(FromK, Numk, ToK, KeyLen, Page1, Page2)
	int                                     FromK; /* prima chiave da spostare               */
	int                                     Numk;  /* numero di chiavi da spostare           */
	int                                     ToK;   /* posizione nella pagina destinazione    */
	int                                     KeyLen;/* lunghezza della chiave                 */
	Page                            *Page1;/* puntatore alla pagina destinazione     */
	Page                            *Page2;/* puntatore alla pagina sorgente         */

	{
		register int                                    Len = KeyLen + IndLen;

		if (Page1->PA.PageHeader.TPage == Page2->PA.PageHeader.TPage)
			Move(LeftDir, Numk * Len, (FromK - 1) * Len, (ToK - 1) * Len, Page2->PA.AreaForKey, Page1->PA.AreaForKey);
	}

/*
@($) FindPos  B+TREE

@(ID)
Cerca una chiave all'interno di una pagina.
Restituisce la chiave piu' simile.
@(FD)

@(ISV)
low = numero della chiave.

j   = incremento in byte di coppia chiave puntatore.
@(FSV)

*/

	int                                     FindPos(ActPage, KKey, KeyLen, KeyOut, IndOut)
	Page                            *ActPage;  /* pagina attiva          */
	TKey                            KKey;      /* valore chiave (input)  */
	int                                     KeyLen;    /* lunghezza della chiave */
	TKey                            KeyOut;    /* valore chiave (output) */
	RecNoType               *IndOut;   /* indice associato       */

	{
		register int                            low = 1, j = 0;

		while (strcmp((char *)(ActPage->PA.AreaForKey + j), KKey) < 0)
      if (low < ActPage->PA.PageHeader.NKey)
			{
				low++;
				j += (KeyLen + IndLen);
			}
			else break;
		strcpy(KeyOut, (char *)(ActPage->PA.AreaForKey + j));
		*IndOut = *((RecNoType *)(ActPage->PA.AreaForKey + j + KeyLen));
		return(low);
	}

/*
@($) PerformOp  B+TREE

@(ID)
Effettua operazioni di Underflow e Overflow sull'indice.
@(FD)

@(ISV)
Pst    = variabile di lavoro contenente numeri chiave.

W      = contatore.

OldKey = variabile di lavoro contenente il valore di chiavi.
@(FSV)
*/

	void            PerformOp(RecOp,ActPage,Pg,OvUnFlow,Temp,MxKey,MnKey,KeyLen,BError)
	KOp                                             RecOp; /* struttura contenente le operazioni da effettuare */
	Page                                    *ActPage; /* pagina attiva           */
	RecNoType                       Pg;       /* numero pagina           */
	BOOLEAN                         *OvUnFlow;/* underflow opp. overflow */
	KeyInd                          *Temp;    /* di lavoro, contiene una coppia chiave-puntatore */
	int                                             MxKey;    /* numero max chiave       */
	int                                             MnKey;    /* numero min chiave       */
	int                                             KeyLen;   /* lunghezza della chiave  */
	int                                             *BError;  /* codice errore           */

		{
			int                                     Pst, W, i;
			TKey                            OldKey;

			for (W = 0; W <= 1; W++)
			{
				if (RecOp[W].FlagOp)
				{
					RecOp[W].FlagOp = FALSE;
					if (RecOp[W].Which == Inser)
					{
						Pst = 0;
						while (Pst < ActPage->PA.PageHeader.NKey )
						{
							GetAKey(*ActPage, KeyLen, ++Pst, IntKey, &IntIndex);
							if (IntIndex == RecOp[W].where) break;
						}
						if (ActPage->PA.PageHeader.NKey == MxKey)
						{
							GetAKey(*ActPage, KeyLen, MxKey, IntKey, &IntIndex);
							if (strcmp(IntKey, RecOp[W].KeyAndInd.KKey) < 0)
							{
								strcpy(Temp->KKey, RecOp[W].KeyAndInd.KKey);
								Temp->Ind = RecOp[W].KeyAndInd.Ind;
							}
							else
							{
								strcpy(Temp->KKey, IntKey);
								Temp->Ind = IntIndex;
								ActPage->PA.PageHeader.NKey--;
								ShiftPage(RightDir, Pst, OnePos, MxKey, KeyLen, ActPage);
								PutAKey(RecOp[W].KeyAndInd.KKey, RecOp[W].KeyAndInd.Ind, Pst, KeyLen, ActPage);
							}
							*OvUnFlow = TRUE;
						}
						else
						{
							*OvUnFlow = FALSE;
							if (strcmp(IntKey, RecOp[W].KeyAndInd.KKey) < 0)
							{
								ActPage->PA.PageHeader.NKey++;
								PutAKey(RecOp[W].KeyAndInd.KKey, RecOp[W].KeyAndInd.Ind, ActPage->PA.PageHeader.NKey, KeyLen, ActPage);
								RecOp[W].FlagOp = TRUE;
								RecOp[W].Which = Change;
								RecOp[W].KeyAndInd.Ind = Pg;
							}
							else
							{
								ShiftPage(RightDir, Pst, OnePos, MxKey, KeyLen, ActPage);
								PutAKey(RecOp[W].KeyAndInd.KKey, RecOp[W].KeyAndInd.Ind, Pst, KeyLen, ActPage);
							}
						}
					}
					else
						if (RecOp[W].Which == Change)
						{
							Pst = 0;
							GetAKey(*ActPage, KeyLen, ++Pst, IntKey, &IntIndex);
							while ((IntIndex != RecOp[W].KeyAndInd.Ind) && (Pst < ActPage->PA.PageHeader.NKey))
								GetAKey(*ActPage, KeyLen, ++Pst, IntKey, &IntIndex);
							if (IntIndex == RecOp[W].KeyAndInd.Ind)
								PutAKey(RecOp[W].KeyAndInd.KKey, IntIndex, Pst, KeyLen, ActPage);
							*OvUnFlow = FALSE;
							if ((IntIndex == RecOp[W].KeyAndInd.Ind) && (Pst == ActPage->PA.PageHeader.NKey))
							{
								RecOp[W].FlagOp = TRUE;
								RecOp[W].Which = Change;
								RecOp[W].KeyAndInd.Ind = Pg;
							}
						}
						else
							if (RecOp[W].Which == Delet)
							{
								Pst = 0;
								GetAKey(*ActPage, KeyLen, ++Pst, IntKey, &IntIndex);
								while ((IntIndex != RecOp[W].KeyAndInd.Ind) && (Pst < ActPage->PA.PageHeader.NKey))
									GetAKey(*ActPage, KeyLen, ++Pst, IntKey, &IntIndex);
								if (IntIndex == RecOp[W].KeyAndInd.Ind)
								{
									*OvUnFlow = (ActPage->PA.PageHeader.NKey == MnKey);
									ShiftPage(LeftDir, ++Pst, OnePos, MxKey, KeyLen, ActPage);
								}
							}
					if ((!OvUnFlow) && (Pst >= ActPage->PA.PageHeader.NKey))
					{
						RecOp[W].FlagOp = TRUE;
						RecOp[W].Which = Change;
						GetAKey(*ActPage, KeyLen, ActPage->PA.PageHeader.NKey, IntKey, &IntIndex);
						strcpy(RecOp[W].KeyAndInd.KKey, IntKey);
						RecOp[W].KeyAndInd.Ind = Pg;
					}
				}
			}
		}

/*
@($)  ZrecOp  B+TREE

@(ID)
Azzera la struttura delle operazioni da effettuare.
@(FD)

@(ISV)
i = contatore.
@(FSV)
*/

	void                            ZRecOp(R)
	KOp                                     R; /* struttura operazioni da azzerare */

		{
			int                                     i;

			for (i = 0; i <= 1; i++)
			{
				R[i].FlagOp = FALSE;
				R[i].Which = Nothing;
				R[i].Posit = NoLink;
				R[i].where = -1;
				MinKey(MaxArray, R[i].KeyAndInd.KKey);
				R[i].KeyAndInd.Ind = NoLink;
			}
		}