// This sourcefile is created by Peter Jodda / peter@jodda.de
// It is under the Peter Jodda license:
// Everyboy may use it on his own risk. This header must stay in the file!
// If you expand this file, please contact me, that I can integrate the
// enhancemants in the original file.
//

/*
 * text_util - verwalten von texten mit mehreren zeilen.
 *
 */

#include <string.h>
#include <stdio.h>

#include "text_util.h"

/*-----------------------------------------*/
/* einen string in eine passende textzeile kopieren.
 * dafuer wird speicher angefordert. Falls nicht gelungen, dann
 * wird NULL zurueclgegeben.
 */
TextZeileType::TextZeileType(char *zeile)
{
   int laenge;
   nachfolger = NULL;
   laenge = strlen(zeile);
   string = new char[laenge+1];
   if(string != NULL)
      strcpy(string,zeile);

} 

/*-----------------------------------------*/
/* eine textzeile freigeben.
 * der allozierte speicher wird zurueckgegeben.
 */
TextZeileType::~TextZeileType()
{
   if(string != NULL)
   {
      delete string;
   }
   string = NULL;
} // TextZeileType - destruktor

/*-----------------------------------------*/
/* eine ganze liste von textzeilen freigeben
 */
void TextZeileType::TextFreeTextZeilenListe()
{
   TextZeileType *danach,*aktuell;
   aktuell = this;
   while(aktuell != NULL)
   {
      danach=aktuell->nachfolger;
      delete aktuell;
      aktuell = danach;
   } /* while aktuell != NULL */
} /* TextFreeTextZeilenListe */

/*-----------------------------------------*/
/* struktur zum speichern von texten belegen.
 */
void TextType::initFelder()
{
   anzahl_zeilen =0;
   wurzel = NULL;
   hinten = NULL;
} /* TextAllocText */
/*-----------------------------------------*/
/* struktur zum speichern von texten belegen.
 */
TextType::TextType()
{
   initFelder();
} /* TextAllocText */

/*-----------------------------------------*/
/* textstruktur wieder freigeben.
 */
TextType::~TextType()
{
   if(wurzel != NULL)
   {
       wurzel->TextFreeTextZeilenListe();
       wurzel=NULL;
   }
} /* TextFreeText */

/*-----------------------------------------*/
/* anzahl der zeilen der textstruktur holen.
 */
int TextType::TextGetZeilenAnzahl()
{
   return anzahl_zeilen;
} /* TextGetZeilenAnzahl */

/*-----------------------------------------*/
/* eine zeile zu einem text zufuegen.
 * die zeile wird ans des des textes gesetzt.
 */
bool TextType::TextAddTextZeile( char *zeile)
{
   TextZeileType *neue_zeile;
   if(zeile ==NULL)
   {
      neue_zeile = new TextZeileType("");
   }
   else /* zeile vorhanden */
   {
      neue_zeile = new TextZeileType(zeile);
   } /* else zeile vorhanden */

   if(neue_zeile ==NULL)     /* zeile nicht bekommen */
      return false;
   if(hinten ==NULL)
   {
      /* noch nichts da */
      wurzel = hinten = neue_zeile;
      anzahl_zeilen = 1;
   }
   else /* hinten anfuegen */
   {
      hinten->nachfolger = neue_zeile;
      hinten = neue_zeile;
      anzahl_zeilen +=1;
   } /* else hinten anfuegen */
   return true;
} /* TextAddTextZeile */

/*-----------------------------------------*/
/* eine zeile zu dem text zufuegen.
 * die zeile wird auf position gesetzt. alle zeilen
 * danach werden eine zeile nach hinten geschoben
 */
void TextType::TextInsertTextZeile( char *zeile,int position)
{
   TextZeileType *neue_zeile,*aktuell,*davor;
   int nummer;
   if(zeile ==NULL)
   {
      neue_zeile = new TextZeileType("");
   }
   else /* zeile vorhanden */
   {
      neue_zeile = new TextZeileType(zeile);
   } /* else zeile vorhanden */
   if(neue_zeile ==NULL)     /* zeile nicht bekommen */
      return;

   davor =NULL;
   aktuell = wurzel;
   nummer = 0;
   /* bis zur gewuenschten zeile suchen */
   while(nummer < position && aktuell != NULL)
   {
      nummer++;
      davor = aktuell;
      aktuell = aktuell->nachfolger;
   } /* while noch davor */

   /* jetzt die neue zeile einfuegen */
   if(aktuell==NULL)
   {
      hinten = neue_zeile;
      neue_zeile->nachfolger = NULL;
      anzahl_zeilen +=1;
      /* am ende engelangt */
      if(davor ==NULL)
      {
         wurzel = neue_zeile;
         /* davor nicht gesetzt, also ganz vorne */
      }
      else /* nicht ganz vorne */
      {
         davor->nachfolger = neue_zeile;
      }
   }
   else /* irgendwo, nicht am ende */
   {
      neue_zeile->nachfolger = aktuell;
      anzahl_zeilen +=1;
      if(davor == NULL)
      {
         wurzel = neue_zeile;
      }
      else /* in der mitte */
      {
         davor->nachfolger = neue_zeile;
      } /* else in der mitte */
   } /* else nicht am ende */

} /* TextInsertTextZeile */

/*-----------------------------------------*/
/* die zeile mit der nummer zeilen_nummer aus dem text holen.
 * ist die zeile nicht vorhanden, dann wird NULL zurueckgegeben.
 * es wird der zeiger auf die zeile zurueckgeben. die zeile selbst
 * darf damit nicht veraendert werden.
 */
char *TextType::TextGetZeile( int zeilen_nummer)
{
   int nummer;
   TextZeileType *aktuell;

  if(zeilen_nummer >= anzahl_zeilen)
     return NULL;

   aktuell = wurzel;
   nummer = 0;
   while(aktuell!= NULL && nummer < zeilen_nummer)
   {
      aktuell = aktuell->nachfolger;
      nummer ++;
    } /* while nicht das gesuchte */

   if(aktuell==NULL)   /* darf eigentlich nicht sein !!!! */
      return NULL;

   return aktuell->string;
} /* TextGetZeile */

/*-----------------------------------------*/
/* die zeile mit der nummer zeilen_nummer aus dem text holen.
 * ist die zeile nicht vorhanden, dann wird NULL zurueckgegeben.
 * es wird der zeiger auf die zeile zurueckgeben. die zeile selbst
 * darf damit nicht veraendert werden.
 */
bool TextType::TextReplaceZeile( int zeilen_nummer,
		char *neuer_string)
{
   int nummer;
   char *neu_s;
   int laenge;
  
   TextZeileType *aktuell;
   if(zeilen_nummer >= anzahl_zeilen)
      return false;
   aktuell = wurzel;
   nummer = 0;
   while(aktuell!= NULL && nummer < zeilen_nummer)
   {
      aktuell = aktuell->nachfolger;
      nummer ++;
   } /* while nicht das gesuchte */
   if(aktuell==NULL)   /* darf eigentlich nicht sein !!!! */
      return false;

   /* aktuell steht jetzt auf der zu ersetzenden zeile */

   laenge = strlen(neuer_string);
   /* speicher fuer neue zeile benutzen */
   neu_s = new char[ laenge +1 ];
   if(neu_s==NULL)  /* keinen speicher, dann melden */
      return false;
      
   // string jetzt rueber kopieren
   strcpy(neu_s,neuer_string);

   /* alten freigeben und durch neuen ersetzen */
   delete aktuell->string;

   aktuell->string = neu_s;

   return true;    /* ok zurueck */
} /* TextReplaceZeile */

/*-----------------------------------------*/
/* die zeile zeilennummer aus dem text entfernen.
 * alle nachfolgenden zeile ruecken eine zeile nach vorne auf.
 */
void TextType::TextRemoveZeile(int zeilennummer)
{
   int nummer;
   TextZeileType *aktuell,*davor;
   if(zeilennummer >= anzahl_zeilen)
      return;
   davor=NULL;
   nummer =0;
   aktuell = wurzel;
   while(nummer< zeilennummer && aktuell != NULL)
   {
      davor = aktuell;
      nummer += 1;
      aktuell = aktuell->nachfolger;
   }
   if(aktuell != NULL)  /* sollte eigentlich immer sein */
   {
      if(davor ==NULL)
      {
         /* erste zeile soll entfernt werden */
         wurzel = aktuell->nachfolger;
         anzahl_zeilen -= 1;
         if(hinten == aktuell) /* falls auch letzte zeile */
            hinten = NULL;
      }
      else /* nicht erste zeile */
      {
         davor->nachfolger = aktuell->nachfolger;
         anzahl_zeilen -= 1;
         if(hinten ==aktuell)  /* falls letzte zeile */
            hinten = davor;

      } /* else nicht erste zeile */
      /* textzeile jetzt auch noch loeschen */
      delete aktuell;
   } /* if aktuell != NULL */
} /* TextRemoveZeile */

/*-----------------------------------------*/
/* alle zeilen aus dem text entfernen.
 * es bleibt nur die textstruktur selber uebrig.
 */
void TextType::TextRemoveAlleZeilen()
{
   /* alle zeilen loeschen */
   wurzel->TextFreeTextZeilenListe();
   /* zeiger leer zetzen */
   wurzel = hinten = NULL;
   anzahl_zeilen = 0;
} /* TextRemoveAlleZeilen */

/*-----------------------------------------*/
/* einen text vollstaendig duplizieren.
 * der zeiger auf den neuen text wird zurueckgegeben.
 * bei fehler wird null zurueckgegeben.
 */
TextType::TextType(TextType *Original)
{
   int i;
   bool gelungen;

   initFelder();

   if(Original ==NULL)
      return ;

   for(i=0;i<Original->anzahl_zeilen;i++)
   {
      gelungen = TextAddTextZeile(Original->TextGetZeile(i));
      if(!gelungen)
      {
         return ;
      } /* kopie der zeile nicht gelungen */
   } /* for alle zeilen */
} /* TextDuplikat */

/*-----------------------------------------*/
/* den gesamten text fuer debug-zwecke ausgeben.
 */
void TextType::TextPrint()
{
   int i;
   char *zeile;

   for(i=0;i<anzahl_zeilen;i++)
   {
      zeile = TextGetZeile(i);
      fprintf(stderr,"%d:<%s>\n",i,zeile);
   } /* for alle zeilen */
} /* TextPrint */

/*-----------------------------------------*/
/* ein string mit mehreren zeilen, die durch \n getrennt sind wird uebergeben.
 * daraus wird ein texttype-text erzeugt.
 * falls es nicht gelingt, wird NULL zurueckgegeben.
 */
TextType::TextType(char *der_string)
{
   char *im_string,*im_buffer,buffer[1001];
   int anzahl_buffer;

   initFelder();

   if(der_string==NULL)
   {
      TextAddTextZeile("");
      return ;
   } /* if kein string uebergeben */

   if(*der_string=='\0')
   {
      TextAddTextZeile("");
      return ;
   } /* leerer string uebergeben */

   im_string = der_string;
   while(*im_string != '\0')
   {
      im_buffer= buffer;
      anzahl_buffer =0;

      /* eine zeile bis zum newline lesen */
      while((*im_string !='\n') && (*im_string !='\0') &&
            (anzahl_buffer < 1000) )
      {
         *im_buffer++ = *im_string++;
      }
      *im_buffer ='\0';  /* buffer terminieren lassen */

      TextAddTextZeile(buffer); /* zufuegen */

      if(*im_string=='\n')		/* newline ueberlesen */
         im_string++;

   } /* while nicht stringende */

} /* TextErzeugeTextAusString */

/*==========================================================================*/
/* gesamten text in eine datei schreiben
 * return TRUE  : ist gelungen,
 *        FALSE : ein fehler ist aufgetreten.
 */

bool TextType::TextSaveText(char *dateiname)
{
   TextZeileType *aktuelle_zeile;
   int i;
   FILE *outfile;
  
   if( dateiname==NULL)
      return false;
   aktuelle_zeile = wurzel;  /* auf beginn des textes gehen */

   outfile= fopen(dateiname,"w");   /* ausgabedatei oeffnen */
   if(outfile==NULL)
      return false;

   /* in einer schleife den gesamten text schreiben */
   for(i=0;i<anzahl_zeilen;i++)
   {
      int anzahl;
      anzahl = fprintf(outfile,"%s\n",aktuelle_zeile->string);
      if(anzahl<0)
      {
         fclose(outfile);
         return false;
      }
      aktuelle_zeile = aktuelle_zeile->nachfolger;
   } /* for i */

   fclose(outfile);

   return true;  /* alles ist gelungen */
} /* TextSaveText */


/*==========================================================================*/
/* eine textdatei in einen texttype einlesen. dabei wird der neue
 * text hinten an den schon bestehenden angefuegt.
 */ 
bool TextType::TextLoadText(char *dateiname)
{
   FILE *infile;
   char lesezeile[1000], *gelesen;
   if( dateiname ==NULL)
      return false;
  
   infile = fopen(dateiname,"r");  	/* datei zum lesen oeffnen */
   if(infile==NULL)
      return false;
  
   gelesen = fgets(lesezeile,999,infile);
   lesezeile[999]='\0';	/* immer terminieren lassen */
   while(gelesen != NULL)
   {
      int laenge;
      /* das \n am ende der zeile entfernen */
      laenge = strlen(lesezeile);
      if(laenge >0)
      {
         if(lesezeile[laenge-1] == '\n')
            lesezeile[laenge-1] ='\0';
      } /* if zeichen da */
      TextAddTextZeile(lesezeile); /* text hinten dran */


      gelesen = fgets(lesezeile,999,infile);
      lesezeile[999]='\0';	/* immer terminieren lassen */
   
   } /* while gelesen */
    
   fclose(infile); 			/* datei wieder schliessen */  

   return true;				/* alles ok */
  
} /* TextLoadText */

/*==========================================================================*/
TextType *TextType::TextAllocTextAusDatei(char *dateiname)
{
   bool gelungen;
   TextType *der_text;

   der_text = new TextType();	/* textstruktur holen */
   if(der_text==NULL)
      return NULL;
    
   gelungen = der_text->TextLoadText(dateiname); /* text einlesen */
  
   if(!gelungen)
   {
      delete der_text;
      return NULL;
   } /* lesen nicht gelungen */   
    
   return der_text;		/* text zurueckgeben */
} /* TextInitTextAusDatei */
/*==========================================================================*/

