/*
                                Denise-MYTH Artificial Intelligence.

                                  Copyright (C) 2000 Szymon Jessa

                                         WERSJA ANGIELSKA:

This  program  is free software; you can redistribute it and/or modify it under the terms of the GNU
General  Public  License  as  published  by  the  Free  Software Foundation; either version 2 of the
License, or (at your option) any later version.

This  program  is  distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
even  the  implied  warranty  of  MERCHANTABILITY  or  FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.

You  should  have received a copy of the GNU General Public License along with this program; if not,
write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

                                           WERSJA POLSKA:

Niniejszy   program   jest  oprogramowaniem  wolnodostëpnym;  moûesz  go  rozprowadzaê  dalej  i/lub
modyfikowaê na warunkach Powszechnej Licencji Publicznej GNU, wydanej przez Fundacjë Wolnodostëpnego
Oprogramowania - wedîug wersji 2-giej tej Licencji lub którejô z póúniejszych wersji.

Niniejszy   program  rozpowszechniany  jest  z  nadziejâ,  iû  bëdzie  on  uûyteczny  -  jednak  BEZ
JAKIEJKOLWIEK  GWARANCJI,  nawet  domyôlnej  gwarancji  PRZYDATNOÔCI  HANDLOWEJ albo PRZYDATNOÔCI DO
OKREÔLONYCH ZASTOSOWAÏ. W celu uzyskania bliûszych informacji - Powszechna Licencja Publiczna GNU.

Z  pewnoôciâ  wraz  z niniejszym programem otrzymaîeô teû egzemplarz Powszechnej Licencji Publicznej
GNU; jeôli nie - napisz do Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

                                              KONTAKT:

snail: Szymon Jessa, ul. Warszawska 20/6, 89-600 Chojnice,
email: smoczek@ue.eti.pg.gda.pl (ew. noco@box43.gnet.pl)
phone: (052) 39-707-88

****************************************************************************************************
*/
// * * * B A Z A  F L E K S J I * * *

char FROM fleksja_load(void)
{
   FILE *stream;
   register UBYTE ile_klonow,nr1,nr2,dane_len;
   clock_t time=clock();
   char wyraz[MAX_LINE_LENGHT],podst[MAX_LINE_LENGHT],dane[MAX_LINE_LENGHT];
   ULONG linia_nr=0;
#ifdef DEMO
   ULONG ile_slow=0;
#endif

   fleksja_head=NULL; // przed wczytaniem baza fleksji jest pusta (zabezpieczenie!)

   if ((stream=data_open(FLEKSJA_FILE TYPE_DAT,"r"))==NULL) return RESULT_NO_OK;

   sprintf(wyraz,"(%.3fkB)\n",(float) file_lenght(stream)/1024);
   con_put(wyraz);

   for (;;)
   {
      linia_nr++;
      if (fgets(wyraz,MAX_LINE_LENGHT,stream)==NULL) goto loading_error;
      if (wyraz[0]==DATA_END) break;
      wyraz[strlen(wyraz)-1]=NULL; // kasujemy ENTER
      linia_nr++;
      if (fgets(podst,MAX_LINE_LENGHT,stream)==NULL)
      {
         con_put("bîâd: nie odnaleziono wyrazu podstawowego\n");
         goto loading_error;
      }
      podst[strlen(podst)-1]=NULL;
      linia_nr++;
      if (fgets(dane,MAX_LINE_LENGHT,stream)==NULL)
      {
         con_put("bîâd: nie odnaleziono danych fleksyjnych wyrazu\n");
         goto loading_error;
      }
      if ((ile_klonow=(char) atoi(dane))<=0)
      {
            con_put("bîâd: liczba klonów <= 0\n");
            goto loading_error;
      }

#ifdef DEMO
      ile_slow+=ile_klonow;
      if (ile_slow>=DEMO_MAX_ILE_SLOW)
      {
         con_put("wersja demonstracyjna ma ograniczonâ liczbë sîów w bazie\n");
         break;
      }
#endif

      // wczytujemy kolejne klony
      for (nr1=1;nr1<=ile_klonow;nr1++)
      {
        linia_nr++;
        if (fgets(dane,MAX_LINE_LENGHT,stream)==NULL) goto loading_error;
        // dane w pliku sa zapisane z offsetem, ktory trzeba usunac
        for (nr2=0,dane_len=(char) strlen(dane);nr2<dane_len;nr2++) dane[nr2]-=OFFSET_DANYCH-1;

        dane[dane_len-1]=NULL;

        // FLEKSJA_LOADING lub FLEKSJA_SORT
        if (fleksja_add(wyraz,podst,dane,FLEKSJA_LOADING)==0) goto loading_error;
      }
   }
   fclose(stream);
   sprintf(wyraz,"liczba odczytanych sîów/klonów: %ld/%ld (%.3fkB) (%.3fs)\n",
                  fleksja_count(),fleksja_count_klony(),(float) fleksja_memory_use()/1024,(float) (clock()-time)/CLK_TCK);
   con_put(wyraz);

   return RESULT_OK;

   // bîâd przy wczytywaniu
   loading_error:
   fclose(stream);
   sprintf(wyraz,"bîâd: nie mogë odczytaê danych (bîëdna linia: %d)\n",linia_nr);
   con_put(wyraz);
   fleksja_erase();
   return RESULT_NO_OK;
}

char FROM fleksja_save_klony(FILE *stream,struct klon_struct *klon)
{
   char txt[MAX_LINE_LENGHT];
   UBYTE nr1;

   for (;klon!=NULL;klon=klon->next)
   {
      // dane zostana zapisane z offsetem (sa jasniejsze dla czlowieka)
      strcpy(txt,klon->dane);
      for (nr1=0;nr1<(char) strlen(txt);nr1++)
          txt[nr1]+=(char) OFFSET_DANYCH-1;

      if (fprintf(stream,"%s\n",txt)<0) return RESULT_NO_OK;
   }

   return RESULT_OK;
}

char FROM fleksja_save(void)
{
#ifdef DEMO
   con_put("w wersji demonstracyjnej nie moûna zapisaê bazy fleksji\n");
   return RESULT_OK;
#else
   FILE *stream;
   struct fleksja_struct *word;
   char txt[MAX_LINE_LENGHT];

   // zabezpieczenie - nie nagrywamy bazy jeôli jest pusta, nie kasujemy starej
   if (fleksja_head==NULL) return RESULT_OK;

   if ((stream=data_open(FLEKSJA_FILE TYPE_TMP,"w"))==NULL) return RESULT_NO_OK;

   for (word=fleksja_head;word!=NULL;word=word->next)
   {
      if (fprintf(stream,"%s\n%s\n%d\n",word->wyraz,word->podst,word->ile_klonow)<0) goto saving_error;
      if (fleksja_save_klony(stream,word->klon_head)==RESULT_NO_OK) goto saving_error;
   }
   if (fprintf(stream,"%c\n",DATA_END)<0) goto saving_error;
   fclose(stream);

   copy_and_remove(FLEKSJA_FILE TYPE_DAT,FLEKSJA_FILE TYPE_OLD);
   if (copy_and_remove(FLEKSJA_FILE TYPE_TMP,FLEKSJA_FILE TYPE_DAT)==RESULT_NO_OK) return RESULT_NO_OK;

   sprintf(txt,"liczba zapisanych sîów/klonów: %ld/%ld\n",fleksja_count(),fleksja_count_klony());
   con_put(txt);

   return RESULT_OK;

   // bîâd w nagrywaniu danych
   saving_error:
   fclose(stream);
   remove(FLEKSJA_FILE TYPE_TMP);
   con_put("bîâd: nie mogë zapisaê danych\n");
   return RESULT_NO_OK;
#endif
}

char FROM fleksja_erase(void)
{
   struct fleksja_struct *word,*word_next;
   struct klon_struct *klon,*klon_next;

   for (word=fleksja_head;word!=NULL;word=word_next)
   {
      word_next=word->next;

      free(word->wyraz);
      free(word->podst);

      for (klon=word->klon_head;klon!=NULL;klon=klon_next)
      {
         klon_next=klon->next;
         free(klon->dane);
         free(klon);
      }
      free(word);
   }
   fleksja_head=NULL;

   return RESULT_OK;
}

void FROM fleksja_add_sort(struct fleksja_struct *new_word)
{
   // funkcja wstawia wyraz w odpowiednim miejscu (sortowanie alfabetyczne)
   struct fleksja_struct *word,*last_word;

   for (word=fleksja_head,last_word=NULL;word;word=word->next)
   {
      // jeôli wyraz w bazie rozpoczyna sië na innâ literkë to pomijamy go
      // (bîâd! powinna byê jakaô nierównoôê! inaczej úle dziaîa!)
//      if (word->wyraz[0]!=new_word->wyraz[0]) continue;

      if (sort_insert(word->wyraz,new_word->wyraz)==RESULT_OK) break;
      last_word=word;
   }

   if (last_word==NULL)
   {
      new_word->next=fleksja_head;
      fleksja_head=new_word;
   }
   else
   {
      new_word->next=last_word->next;
      last_word->next=new_word;
   }
}

char FROM fleksja_add(char *wyraz,char *podst,char *dane,char mode)
{
   // mode:
   // FLEKSJA_SORT - normalny tryb, z sortowaniem dodawanych wyrazów
   // FLEKSJA_LOADING - super szybki tryb TYLKO do wczytywania bazy

   static struct fleksja_struct *last_word=NULL;
   struct fleksja_struct *new_word;
   struct klon_struct *klon,*new_klon=NULL;
   UBYTE word_exist=0;

   // sprawdzamy czy w bazie jest juz ten wyraz
   // jesli istnieje, to dopisujemy po prostu nowe klony danych
   // jesli nie istnieje, to dodajemy nowy wezel

   // tryb super szybki wykorzystuje dodatkowe parametry
   if (mode==FLEKSJA_LOADING)
   {
      // poszukiwanie w tym trybie od ostatniego wpisu
      if ((new_word=fleksja_find(wyraz,podst,last_word,WITH))==NULL)
      {
         // nowy wyraz
         if ((new_word=(struct fleksja_struct *) malloc(sizeof(struct fleksja_struct)))==NULL) goto no_memory;
         new_word->next=NULL;
         new_word->ile_klonow=0; // zostana dopisane ponizej
         new_word->klon_head=NULL;

         // wyraz odmieniony i podstawowy
         // uwaga! +1 bo musimy mieê tez miejsce dla kodu koïca ciâgu,
         if ((new_word->wyraz=create_string(wyraz,strlen(wyraz)+1))==NULL) goto no_memory;
         if ((new_word->podst=create_string(podst,strlen(podst)+1))==NULL) goto no_memory;
      }
   }
   // tryby normalne
   else
   {
      if ((new_word=fleksja_find(wyraz,podst,NULL,FULL))==NULL)
      {
         // nowy wyraz
         if ((new_word=(struct fleksja_struct *) malloc(sizeof(struct fleksja_struct)))==NULL) goto no_memory;
         new_word->next=NULL;
         new_word->ile_klonow=0; // zostana dopisane ponizej
         new_word->klon_head=NULL;

         // wyraz odmieniony i podstawowy
         // uwaga! +1 bo musimy mieê tez miejsce dla kodu koïca ciâgu,
         if ((new_word->wyraz=create_string(wyraz,strlen(wyraz)+1))==NULL) goto no_memory;
         if ((new_word->podst=create_string(podst,strlen(podst)+1))==NULL) goto no_memory;
      }
      else
      {
         word_exist=1;
      }
   }

   // tworzymy klon
   if ((new_klon=(struct klon_struct *) malloc(sizeof(struct klon_struct)))==NULL) goto no_memory;
   new_klon->next=NULL;
   new_klon->dane=NULL;

   if ((new_klon->dane=create_string(dane,strlen(dane)+1))==NULL) goto no_memory;

   // dolaczamy klon do konca listy klonow
   if (new_word->klon_head==NULL)
      new_word->klon_head=new_klon;
   else
   {
      for(klon=new_word->klon_head;klon->next!=NULL;klon=klon->next) {};
		klon->next=new_klon;
   }

   new_word->ile_klonow++;

   // jesli slowo juz istnieje w bazie, to mozna wyjsc po dopisaniu klonow
   if (word_exist==1) return RESULT_OK;

   if (fleksja_head==NULL) fleksja_head=new_word;
   else
   {
      if (mode==FLEKSJA_SORT)
      {
         fleksja_add_sort(new_word);
      }
      // tryb FLEKSJA_LOADING - proste wstawienie wyrazu na koïcu bazy
      else
      {
         last_word->next=new_word;
         new_word->next=NULL;
      }
   }

   if (mode==FLEKSJA_LOADING) last_word=new_word;

   return RESULT_OK;

   // brak pamiëci (zwalniamy teû të pamiëê, którâ udaîo sië zarezerwowaê)
   no_memory:
   con_put("bîâd: brak pamiëci\n");
   if (new_word!=NULL && word_exist==0)
   {
      free(new_word->wyraz);
      free(new_word->podst);
      free(new_word);
   }
   if (new_klon!=NULL)
   {
      free(new_klon->dane);
      free(new_klon);
   }

	return RESULT_NO_OK;
}

char FROM fleksja_check_ascii_slowo(char *slowko)
{
	// jeôli litera ciâgu nie naleûy do kodu pl_ascii, to wypisujemy bîâd

	char nr1;

	for (nr1=0;nr1<(char) strlen(slowko);nr1++)
	{
		if (pl_ascii_exist(slowko[nr1])==NULL)
		{
			con_put("bîëdny wyraz: '");
			con_put(slowko);
			con_put("'\n");
			return RESULT_NO_OK;
		}
   }

   return RESULT_OK;
}

char FROM fleksja_check_ascii(char mode)
{
	struct fleksja_struct *word,*word_prev;
	ULONG ile_bledow=0;
   char txt[MAX_LINE_LENGHT];

	// funkcja sprawdza, czy w bazie fleksji sîowa uûywajâ poprawnych znaków
	con_put("sprawdzam kody ASCII...\n");

	for (word=fleksja_head,word_prev=NULL;word!=NULL;word_prev=word,word=word->next)
	{
		if (fleksja_check_ascii_slowo(word->wyraz)==RESULT_NO_OK ||
          fleksja_check_ascii_slowo(word->podst)==RESULT_NO_OK)
		{
			ile_bledow++;
			if (mode==MODE_REPAIR)
			{
				con_put("operacja: kasujë sîowo...\n");
				// word - wskazuje nastëpne sîowo, jeôli brak - to wyjôcie
				if ((word=del_word_from_base(word_prev,word))==NULL) break;
				word=word_prev; // zapewnia, ze nic nie zostanie pominiete
			}
			continue;
		}
	}

	sprintf(txt,"liczba bîëdnych sîów: %d\n",ile_bledow);
	con_put(txt);

    if (mode==MODE_REPAIR && ile_bledow>0) con_put("bîëdne sîowa zostaîy skasowane z bazy\n");

	return RESULT_OK;
}

char FROM fleksja_check_dane_slowo(struct fleksja_struct *word)
{
	// sprawdzamy poszczególne klony pod wzglëdem danych fleksyjnych

	struct klon_struct *klon;
   char temp_dane[MAX_FLEKSJA_LENGHT];

	for (klon=word->klon_head;klon!=NULL;klon=klon->next)
	{
      // przepisanie danych jest bezpieczniejsze (byîy kiedyô wypadki... ;))
      sprintf(temp_dane,klon->dane);
		// jeôli klon ma bîëdne / nie peîne dane to RESULT_NO_OK
		if (fleksja_data_maker(temp_dane,CHECK)==RESULT_NO_OK)
		{
			con_put("bîëdny wyraz: ");
			con_put(word->wyraz);
			con_put("\n");
			return RESULT_NO_OK;
		}
	}

	return RESULT_OK;
}

char FROM fleksja_check_dane(char mode)
{
	struct fleksja_struct *word,*word_prev;
	ULONG ile_bledow=0;
   char txt[MAX_LINE_LENGHT];

	// funkcja sprawdza, czy w bazie fleksji sîowa sâ poprawnie zdefiniowane
	con_put("sprawdzam dane fleksyjne...\n");

	for (word=fleksja_head,word_prev=NULL;word!=NULL;word_prev=word,word=word->next)
	{
		if (fleksja_check_dane_slowo(word)==RESULT_NO_OK)
		{
			ile_bledow++;
			if (mode==MODE_REPAIR)
			{
				con_put("operacja: kasujë sîowo...\n");
				// word - wskazuje nastëpne sîowo, jeôli brak - to wyjôcie
				if ((word=del_word_from_base(word_prev,word))==NULL) break;
				word=word_prev; // zapewnia, ze nic nie zostanie pominiete
			}
			continue;
		}
	}

	sprintf(txt,"liczba bîëdnych sîów: %ld\n",ile_bledow);
	con_put(txt);

   if (mode==MODE_REPAIR && ile_bledow>0) con_put("bîëdne sîowa zostaîy skasowane z bazy\n");

	return RESULT_OK;
}


// * * * funkcje podajâce informacje iloôciowe o bazach * * *

ULONG FROM fleksja_memory_use(void)
{
   struct fleksja_struct *word;
   struct klon_struct *klon;
   ULONG ile=0;

   for (word=fleksja_head;word!=NULL;word=word->next,ile+=sizeof(struct fleksja_struct))
   {
      for (klon=word->klon_head;klon!=NULL;klon=klon->next,ile+=sizeof(struct klon_struct))
      {
         ile+=sizeof(char); // dane
         ile+=strlen(klon->dane);
      }
   }

   return ile;
}

ULONG FROM fleksja_count(void)
{
   struct fleksja_struct *word;
   ULONG ile=0;

   for (word=fleksja_head;word!=NULL;word=word->next,ile++);

   return ile;
}

ULONG FROM fleksja_count_klony(void)
{
   struct fleksja_struct *word;
   ULONG ile=0;

	// ponizsza konstrukcja NIE MOZE byc zawarta w 1 lini FOR!!!
	// przy kompilacji z BorlandC++ pod Windows program sie powiesi
   for (word=fleksja_head;word!=NULL;word=word->next)
   {
   	ile+=word->ile_klonow;
   }

   return ile;
}
