/*--------------------------------------------------------------------
 * FICHERO:  IndInser.c
 * OBJETIVO: Definir la funcin Ind_Inserta()
 * AUTOR:    Pedro Reina
 * FECHA:    L.7.8.1995
 *------------------------------------------------------------------*/

/*--------------------------------------------------------------------
 * Funciones privadas
 *
 *   Ind_InsertaItem()
 *   Ind_DividePagina()
 *------------------------------------------------------------------*/

/*--------------------------------------------------------------------
 * Ficheros de cabecera
 *------------------------------------------------------------------*/

#include "Indice.h"

/*--------------------------------------------------------------------
 * Declaracin de funciones
 *------------------------------------------------------------------*/

static logico Ind_InsertaItem();
static logico Ind_DividePagina();

/*--------------------------------------------------------------------
 * Definicin de funciones
 *------------------------------------------------------------------*/

/*--------------------------------------------------------------Olimpo
 * FUNCION:  Ind_Inserta()
 * OBJETIVO: Insertar una nueva clave en un ndice
 * ENTRADAS: El ndice, el nmero de registro de base de datos,
 *           la clave  y la funcin que se usar para comparar
 *           claves (debe recibir dos cadenas y devolver un
 *           contador).
 * SALIDAS:  Lgica, que indica si se ha podido insertar
 * EJEMPLO:  Ind_Inserta ( Agenda, NumeroRegistro, Clave,
                           FuncionComparacion )
 * NOTA:     Los registros se numeran a partir de 0
 * ALGORITMO:
 *      Buscar la clave dada
 *      Situarse en la siguiente clave
 *      Si la pgina est llena
 *        Dividir la pgina
 *        Volver a llamar a Ind_Busca()
 *      Si la pgina no est llena, insertar la clave dada
 *------------------------------------------------------------------*/
logico Ind_Inserta (Indice, Registro, Clave, Compara)
indice   Indice;
entero   Registro;
cadena   Clave;
contador Compara();
  {
  logico   Respuesta;
  caracter ClaveReferencia[IND_MAXCLAVE+1];
  entero   RegistroReferencia;

  if ( Ind_ModoApertura(Indice) == IND_LECTURA )
    { Respuesta = NO; }

  else
    {
    Ind_AlmacenaClave (Indice, ClaveReferencia, Clave);

    if ( Ind_Busca (Indice, &RegistroReferencia, ClaveReferencia, Compara) )
      {
      if ( !Ind_Siguiente (Indice, &RegistroReferencia, ClaveReferencia) )
        {
        Ind_Ultimo (Indice, &RegistroReferencia, ClaveReferencia);
        Ind_IncrementaItem (Indice);
        }
      }
    else if ( !Ind_Primero(Indice, &RegistroReferencia, ClaveReferencia) )
      { Ind_PonItem(Indice,0); }

    Ind_AlmacenaClave (Indice, ClaveReferencia, Clave);
    if ( Ind_MaxItem(Indice) >= Ind_ClavesPagina(Indice) )
      {
      if ( Ind_DividePagina (Indice) )
        { Respuesta = Ind_Inserta (Indice, Registro,
                      ClaveReferencia, Compara); }
      else  { Respuesta = NO; }
      }
    else
      { Respuesta = Ind_InsertaItem (Indice, Registro, ClaveReferencia); }
    }

  return ( Respuesta );
  }

/*--------------------------------------------------------------------
 * FUNCION:  Ind_InsertaItem()
 * OBJETIVO: Insertar un nuevo item en la pgina en memoria
 * ENTRADAS: El ndice, el nmero de registro de base de datos,
 *           y la clave.
 * SALIDAS:  Lgica, que indica que todo ha ido bien
 * EJEMPLO:  Ind_InsertaItem ( Agenda, NumeroRegistro, Clave )
 * NOTAS:    1. Los registros se numeran a partir de 0
 *           2. El item se anota en la posicin actual
 *           3. La clave ya viene preparada
 * ALGORITMO:
 *      Almacenar el desplazamiento al ltimo item disponible
 *      Desplazar los ltimos desplazamientos
 *      Anotar el desplazamiento al nuevo item
 *      Anotar el registro, la pgina y la clave
 *      Incrementar el total de items en la pgina
 *      Grabar la pgina al fichero
 *------------------------------------------------------------------*/
static logico Ind_InsertaItem (Indice, Registro, Clave)
indice Indice;
entero Registro;
cadena Clave;
  {
  contador i, Desplazamiento;
  entero   Siguiente;

  Siguiente = Ind_PaginaApuntada (Indice,Ind_MaxItem(Indice));
  Desplazamiento = Ind_Desplazamiento (Indice, Ind_ClavesPagina(Indice));

  for ( i = Ind_ClavesPagina(Indice) ; i>Ind_Item(Indice) ; i-- )
    { Ind_PonDesplazamiento (Indice, i, Ind_Desplazamiento(Indice, i-1)); }

  Ind_PonDesplazamiento (Indice, Ind_Item(Indice), Desplazamiento);

  Ind_PonRegistro (Indice, Ind_Item(Indice), Registro);
  Ind_PonClave (Indice, Ind_Item(Indice), Clave);
  Ind_PonPaginaApuntada (Indice, Ind_Item(Indice),
                         Ind_PaginaApuntada (Indice, Ind_Item(Indice)+1));
  Ind_PonPaginaApuntada (Indice, Ind_Item(Indice)+1, (entero)0);

  Ind_PonMaxItem (Indice, Ind_MaxItem(Indice)+1);
  Ind_PonPaginaApuntada (Indice,Ind_MaxItem(Indice),Siguiente);

  return ( Ind_EscribePagina (Indice, Ind_Posicion(Indice)) );
  }

/*--------------------------------------------------------------------
 * FUNCION:  Ind_DividePagina()
 * OBJETIVO: La pgina en memoria est completa; la dividimos en tres
 *           pginas dejando slo el item central en la pgina en memoria
 * ENTRADAS: El ndice
 * SALIDAS:  Lgica, que indica que todo ha ido bien
 * EJEMPLO:  Ind_DividePagina ( Agenda )
 * ALGORITMO:
 *      Pedir memoria para una pgina auxiliar
 *      Calcular el item Medio, por el que vamos a dividir la pgina:
 *        los items menores irn a la pgina izquierda y los mayores
 *        a la pgina derecha
 *      Copiar la pgina en memoria a la pgina auxiliar
 *      Crear una nueva pgina, la izquierda
 *      Copiar la pgina auxiliar en la izquierda, dejar slo los
 *        items menores que Medio y grabarla en el fichero
 *      Crear una nueva pgina, la derecha
 *      Copiar la pgina auxiliar en la derecha, dejar slo los
 *        items mayores que Medio y grabarla en el fichero
 *      Modificar la pgina auxiliar para dejar slo el item Medio
 *      Grabarla en el lugar de la pgina original
 *------------------------------------------------------------------*/
static logico Ind_DividePagina (Indice)
indice Indice;
  {
  logico   TodoBien = NO;
  memoria  PagAux;
  entero   PagActual, PagIzq, PagDer, PagAnt, PagSig;
  contador TotalItem, Medio, ItemsDerecha, Desplazamiento, i;

  if ( PagAux = Mem_Crea (IND_PAGINA) )
    {
    TotalItem = Ind_MaxItem (Indice);
    Medio = TotalItem / 2;
    ItemsDerecha = TotalItem - Medio - 1;
    PagAnt = Ind_PaginaApuntada (Indice, 0);
    PagSig = Ind_PaginaApuntada (Indice, TotalItem);
    PagActual = Ind_Posicion (Indice);
    Mem_Copia (PagAux, Ind_Pagina(Indice), IND_PAGINA);

    if ( PagIzq = Ind_NuevaPagina (Indice) )
      {
      Mem_Copia (Ind_Pagina(Indice), PagAux, IND_PAGINA);
      Ind_PonMaxItem (Indice, Medio);
      Ind_PonPaginaApuntada (Indice, 0, PagAnt);
      Ind_PonPaginaApuntada (Indice, Medio, (entero)0);
      if ( Ind_EscribePagina (Indice, PagIzq) &&       
           (PagDer = Ind_NuevaPagina (Indice)) )
        {
        Mem_Copia (Ind_Pagina(Indice), PagAux, IND_PAGINA);
        for ( i=0 ; i<ItemsDerecha ; i++ )
          {
          Desplazamiento = Ind_Desplazamiento (Indice, i);
          Ind_PonDesplazamiento (Indice, i,
                                 Ind_Desplazamiento (Indice,Medio+1+i));
          Ind_PonDesplazamiento (Indice, Medio+1+i, Desplazamiento);
          }
        Ind_PonMaxItem (Indice, ItemsDerecha);
        Ind_PonPaginaApuntada (Indice, 0, (entero)0);
        Ind_PonPaginaApuntada (Indice, ItemsDerecha, PagSig);
        if ( Ind_EscribePagina (Indice, PagDer) )
          {
          Mem_Copia (Ind_Pagina(Indice), PagAux, IND_PAGINA);
          Desplazamiento = Ind_Desplazamiento (Indice, 0);
          Ind_PonDesplazamiento (Indice, 0,
                                 Ind_Desplazamiento (Indice,Medio));
          Ind_PonDesplazamiento (Indice, Medio, Desplazamiento);
          Ind_PonMaxItem (Indice, 1);
          Ind_PonPaginaApuntada (Indice, 0, PagIzq);
          Ind_PonPaginaApuntada (Indice, 1, PagDer);
          TodoBien = Ind_EscribePagina (Indice, PagActual);
          }
        }
      }
    Mem_Destruye (PagAux);
    }

  return ( TodoBien );
  }