#include <stdio.h>
#include <stdlib.h>
#include "btree.h"

/*
 * Fußnotensortierprogramm optimiert auf die Aufgabe aus dem Linux-Magazin
 * Ausgabe 10/08 Seite 30ff
 *
 * Beschränkungen: max. 2^32 Fußnoten, größte max. Nummerierung: 2^32.
 *                 Je nach Vorsortierung max. 2^32 bytes Fußnoten oder mehr, 
 *                 bzw. beschränkt durch RAM.
 *
 * Funktionsweise:
 * Gelesen wird von stdin, ausgegeben auf stdout. Es gibt keine Hilfe oder
 * sonstige sinnvolle Ausgaben.
 *
 * Ich bin kein Programmierer und daher... öhm, sieht der Code
 * etwas Grauenhaft aus?? k.A.
 * Nach fast einem Tag Arbeit (ich wollte halt nur mal ein wenig C lernen, da
 * war mir das ganze hier Recht) hab ich jetzt auch keine Lust mehr, nochmal
 * drüberzugucken und noch Teile in Funktionen auszulagern etc.
 *
 * Meinen Teil hab ich mal (denke ich) auf geringen Speicherverbrauch
 * optimiert. Daher Langsamer durch häufiges freigeben
 * auch kleiner nicht benötigter Speicherbereiche(?). Speicherverbrauch ohne
 * b-Baum(*altzuneu) im ungünstigsten Fall etwas mehr als der untere Teil der
 * Eingabedatei, oder?
 * Die Verwendete btree Implementierung ist nicht von mir und daher u.U. 
 * deutlich Speicherintensiver. k.A., hab keine Lust mehr noch selber was zu
 * implementieren ;-)
 *
 * Lizenz dieser Datei: Public Domain
 * Lizenz btree.c und btree.h: GPL v2.
 *
 * BUGs: Ich nutze eine btree-implementierung, die ich auf der LKML gefunden
 *       habe. Diese steht unter der GPL v2. Ich musste eine Zeile hinzufügen,
 *       da diese sonst nachvollziehbar Segfaults bei btree_search() lieferte.
 *
 *       Wenn im Text irgendwo ein [XXX] auftaucht, wo XXX eine beliebige Zahl
 *       ist, aber [XXX] keinen Verweis auf eine Fußnote darstellt, wird dafür
 *       ein Verweis angelegt (die Zahl XXX also geändert), und in der
 *       Fußnotenliste dafür die originale Zahl wieder angegeben.
 */

//globale Variablen
int alloziierterspeicher = 0;
int *neuzualt; //flacher speicher für mapping neu->alt
int laufendenummer = 0;
btree *altzuneu; //btree für mapping alt->neu
char **fussnoten;

//funktionen für die btree-lib
unsigned int value(void * key)
{
  return *(int *)key;
}
unsigned int keysize(void * key)
{
  return sizeof(int);
}
unsigned int datasize(void * data)
{
  return sizeof(int);
}

/*
 * fehler ausgeben und programm beenden
 */
fehler(char * fehlermeldung)
{
  printf(fehlermeldung);
  exit(1);
}

/*
 * Es wurde eine Fußnote im Text gefunden. Diese sollte eine neue Nummer 
 * bekommen und in die Korrespondenztabelle der alten eingefügt werden.
 */
int fn_einfuegen(char* altenummer_s)
{
  int altenummer,i,neuenummer;
  bt_key_val * kv = NULL;

  //falls Speicher knapp wird, neuen alloziieren.
  if (alloziierterspeicher<(laufendenummer+1))
  {
    if (alloziierterspeicher==0)
    {
      neuzualt=(int *)malloc(64*sizeof(int));
      if (neuzualt==NULL) fehler("malloc: zu wenig speicher");
      alloziierterspeicher=64;
    }
    else
    {
      alloziierterspeicher=alloziierterspeicher+(alloziierterspeicher);
      neuzualt=(int *)
                   realloc(neuzualt,alloziierterspeicher*sizeof(int));
      if (neuzualt==NULL) fehler("realloc: zu wenig speicher");
    }
  }

  altenummer = atoi(altenummer_s);

  //alte nummer schon aufgetreten?
  if (laufendenummer>0)
  {
    kv = btree_search(altzuneu, &altenummer);
  }
  if (kv!=NULL)
  { //schon da -> vorhandene neue ausgeben
    return *(int *) kv->val;
  }
  else 
  { //noch nicht da -> anfügen
    neuenummer = ++laufendenummer;
    kv = (bt_key_val*)malloc(sizeof(*kv));
    kv->key = malloc(sizeof(int));
    *(int *)kv->key = altenummer;
    kv->val = malloc(sizeof(int));
    *(int *)kv->val = neuenummer;
    btree_insert_key(altzuneu,kv);
 
    neuzualt[neuenummer]=altenummer;

    return neuenummer; //TODO: neuenummer->laufendenummer
  }
  //return 100; unreachable
}

/*
 * Eine Klammer wurde im Text erkannt. Nun wird geprüft, ob dies auch eine
 * Fußnote ist und diese ggf. aufgenommen.
 */
void klammer_im_text()
{
  char fussnotennummer[10];
  int i = 0, neuenummer; char c;
  c=getchar();
  while (c>=48 && c<=57 && i<10)
  {
    fussnotennummer[i]=c;
    i++;
    c=getchar();
  }
  fussnotennummer[i]=0;
  if (c==93 && i>0)
  {
    neuenummer = fn_einfuegen(fussnotennummer);
    printf("[%i]",neuenummer);
    return; //neue Nummer eingefügt und ausgegeben.
  }

  // ist keine Fußnote, der hier eingelesene Text wird ausgegeben
  printf("[%s",fussnotennummer);
  putchar(c);
  
}

int fussnoten_folgen()
{
  char trenntext[10] = "footnotes:";
  char c; int i,j;
  for (i=0; i<10; i++)
  {
    c=getchar();
    if (c!=trenntext[i])
    {
      putchar('@');
      for (j=0; j<i; j++)
        putchar(trenntext[j]);
      putchar(c);
      return 0;
    }
  }
  return 1;
}

int main()
{
  char c;
  int i,infussnote=0,altenummer,aktuellefussnotenmaxlaenge=0;
  int aktuellefussnote=0,aktuellefussnotenlaenge=0,maxausgegebenefn=0;
  char fussnotennummer[10];
  bt_key_val * kv; //für btree_search

  //btree lib initialisieren
  altzuneu = btree_create(3);
  altzuneu->value = value;
  altzuneu->key_size = keysize;
  altzuneu->data_size = datasize;

  //ab hier der obere Teil (Textmodus)
  while ((c=getchar())!=EOF)
  {
    if (c==91) klammer_im_text();
    else if (c==64) {if (fussnoten_folgen()) break;}
    else putchar(c);
  }

  //neuzualt-Speicher aufs nötigte schrumpfen
  neuzualt=(int *)realloc(neuzualt,laufendenummer*sizeof(int));
  //Speicher für die Zeiger auf die Fußnoten alloziieren.
  fussnoten=(char **)malloc(laufendenummer*sizeof(char*));
  //TODO:hier wird 1*sizeof(int) verschenkt, da fussnoten[0] nie vorkommen wird

  //ab hier der untere Teil (Fußnotenmodus)
  printf("@footnotes:");

  alloziierterspeicher = 0;

  while ((c=getchar())!=EOF)
  {
    if (c==91) 
    {
      i=0;
      c=getchar();
      while (c>=48 && c<=57 && i<10)
      {
        fussnotennummer[i]=c;
        i++;
        c=getchar();
      }
      fussnotennummer[i]=0;
      if (c==93 && i>0) //fussnotenanfang gefunden
      {
        if (aktuellefussnote) //string beenden
          fussnoten[aktuellefussnote][aktuellefussnotenlaenge++]=0;
        if (aktuellefussnotenmaxlaenge>aktuellefussnotenlaenge
            && aktuellefussnote) //TODO: schrumpfen könnte man auch nach ausg.
        { //platz für alte fußnote zurechtschrumpfen
	  fussnoten[aktuellefussnote]=(char *)realloc(
             fussnoten[aktuellefussnote],aktuellefussnotenlaenge*sizeof(char));
        }
        //prüfen, ob vor dieser fn schon alle anderen ausgegeben wurden
        if (maxausgegebenefn==aktuellefussnote-1)
        { //ja, es können weitere fussnoten ausgegeben werden
          while (fussnoten[aktuellefussnote])
          {
            printf("[%i]%s",aktuellefussnote,fussnoten[aktuellefussnote]);
            free(fussnoten[aktuellefussnote]);
            fussnoten[aktuellefussnote]=NULL;
            maxausgegebenefn++;
            aktuellefussnote++;
          }
        }
        aktuellefussnotenlaenge=0;
        aktuellefussnotenmaxlaenge=0;
        altenummer = atoi(fussnotennummer);
        kv = btree_search(altzuneu, &altenummer);
	if (kv==NULL)
	{
	  printf("[unreferenziert]");
          aktuellefussnote = 0;
	} else //kv!=NULL
	{
          aktuellefussnote = *(int *) kv->val;
	}
      } else
      {
        //TODO: schon gelesene nicht-fußnoten-nummer wieder ausgeben
        printf("TODO");
      }
    }
    else if (aktuellefussnote)
    { //in fussnote: gelesenes zeichen laden
      if (aktuellefussnotenmaxlaenge==0)
      {
	aktuellefussnotenmaxlaenge=256;
        fussnoten[aktuellefussnote]=(char *)
                  malloc(aktuellefussnotenmaxlaenge*sizeof(char));
      } else if (aktuellefussnotenmaxlaenge<aktuellefussnotenlaenge+1)
      {
        aktuellefussnotenmaxlaenge=aktuellefussnotenmaxlaenge+256;
	fussnoten[aktuellefussnote]=(char *) realloc(
          fussnoten[aktuellefussnote],aktuellefussnotenmaxlaenge*sizeof(char));
      }
      fussnoten[aktuellefussnote][aktuellefussnotenlaenge]=c;
      aktuellefussnotenlaenge++;
    }
    else putchar(c); //wenn nicht in einer referenzierten Fußnote
  }
  if (aktuellefussnote) //string beenden
    fussnoten[aktuellefussnote][aktuellefussnotenlaenge++]=0;
  for (i=maxausgegebenefn+1;i<=laufendenummer;i++)
    if (fussnoten[i]==NULL)
      printf("[%i] Alte Zahl: %i\n",i,neuzualt[i]);
    else
      printf("[%i]%s",i,fussnoten[i]);

  return 0;
}
