#include "ccommon.h"
#include "genconst.h"
#include "ccustio.h"
#include "cbpltree.h"
#include "bplpriv.h"
#include "btrread.h"
#include "btrbase.h"
#include "checks.h"
#ifdef __WATCOMC__
#include <malloc.h>
#else
#include        <memory.h>
#endif


 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) ;
    WPag =  FilCheck->CurPag ;
    WPos =  FilCheck->Pos ;
    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
      {
        Repos(FilCheck, BError) ;
        if (*BError != NoErr)
        {
          if (*BError == BTrKeyNotFound)
          {
            strcpy(KKey,FilCheck->Key);
            *IndSr = FilCheck->Ind ;
            *BError = NoErr ;
          }
          return;
        }
        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
      {
        Repos(FilCheck, BError) ;
        if ((*BError != NoErr) && (*BError != BTrKeyNotFound))
        {
          if (*BError == BTrEOF)
          {
            strcpy(KKey,FilCheck->Key);
            *IndSr = FilCheck->Ind ;
            *BError = NoErr ;
          }
          return;
        }
        *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;
      }
    }