/*
                                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  E L I Z Y * * *

char FROM eliza_load(void)
{
   FILE *stream;
   clock_t time=clock();
   char txt[MAX_LINE_LENGHT*2];
   struct eliza_struct *pozycja,*old_pozycja=NULL;
   struct eliza_lista_struct *lista,*old_lista;

   eliza_head=NULL; // przed wczytaniem baza elizy jest pusta (zabezpieczenie!)

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

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

   // pierwszy znak - koniec bazy danych lub poczâtek nowej pozycji
   if (fgets(txt,MAX_LINE_LENGHT*2,stream)==NULL) goto loading_error;
   if (txt[0]!=POZYCJA_START) goto loading_error;

   for (;;)
   {
      // rezerwujemy pamiëê dla nowej pozycji
      if ((pozycja=(struct eliza_struct *) malloc(sizeof(struct eliza_struct)))==NULL) goto no_memory;
      pozycja->next=NULL;
      pozycja->ident_head=NULL;
      pozycja->funkc_head=NULL;

      if (eliza_head==NULL) eliza_head=pozycja;
      else old_pozycja->next=pozycja;

      // odczytujemy ciâg list identyfikacyjnych
      for (old_lista=NULL;;old_lista=lista)
      {
         if (fgets(txt,MAX_LINE_LENGHT*2,stream)==NULL) goto loading_error;
         // jeôli to koniec ciâgu list identyfikacyjnych
         if (txt[0]==PART_NEXT_START) break;

         // zapisujemy w pamiëci nowâ listë
         if ((lista=(struct eliza_lista_struct *) malloc(sizeof(struct eliza_lista_struct)))==NULL) goto no_memory;
         lista->next=NULL;

         if ((lista->dane=create_string(txt,strlen(txt)))==NULL) goto no_memory;

         // doîâczamy do ciâgu list
         if (old_lista==NULL) pozycja->ident_head=lista;
         else old_lista->next=lista;
      }

      // odczytujemy ciâg list funkcyjnych
      for (old_lista=NULL;;old_lista=lista)
      {
         if (fgets(txt,MAX_LINE_LENGHT*2,stream)==NULL) goto loading_error;
         // jeôli to koniec ciâgu list
         if (txt[0]==POZYCJA_START) break;
         // jeôli to koniec bazy
         else if (txt[0]==DATA_END) goto END_FOR;

         // zapisujemy w pamiëci nowâ listë
         if ((lista=(struct eliza_lista_struct *) malloc(sizeof(struct eliza_lista_struct)))==NULL) goto no_memory;
         lista->next=NULL;

         if ((lista->dane=create_string(txt,strlen(txt)))==NULL) goto no_memory;

         // doîâczamy do ciâgu list
         if (old_lista==NULL) pozycja->funkc_head=lista;
         else old_lista->next=lista;
      }

      old_pozycja=pozycja;
   }
   END_FOR:

   fclose(stream);
   sprintf(txt,"liczba odczytanych pozycji/wejôê/wyjôê: %ld/%ld/%ld (%.3fkB) (%.3fs)\n",
                  eliza_count(),eliza_count_ident(),eliza_count_funkc(),(float) eliza_memory_use()/1024,(float) (clock()-time)/CLK_TCK);
   con_put(txt);

   return RESULT_OK;

   // bîâd przy wczytywaniu
   loading_error:
   fclose(stream);
   con_put("bîâd: nie mogë odczytaê danych\n");
   eliza_erase();
   return RESULT_NO_OK;

   no_memory:
   fclose(stream);
   con_put("bîâd: brak pamiëci\n");
   eliza_erase();
   return RESULT_NO_OK;
}

char FROM eliza_erase(void)
{
   struct eliza_struct *pozycja,*pozycja_next;
   struct eliza_lista_struct *lista,*lista_next;

   for (pozycja=eliza_head;pozycja!=NULL;pozycja=pozycja_next)
   {
      pozycja_next=pozycja->next;

      for (lista=pozycja->ident_head;lista!=NULL;lista=lista_next)
      {
         lista_next=lista->next;
         free(lista->dane);
         free(lista);
      }
      for (lista=pozycja->funkc_head;lista!=NULL;lista=lista_next)
      {
         lista_next=lista->next;
         free(lista->dane);
         free(lista);
      }

      free(pozycja);
   }
   eliza_head=NULL;

   return RESULT_OK;
}

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

ULONG FROM eliza_memory_use(void)
{
   struct eliza_struct *pozycja;
   struct eliza_lista_struct *lista;
   ULONG ile=0;

   for (pozycja=eliza_head;pozycja!=NULL;pozycja=pozycja->next,ile+=sizeof(struct eliza_struct))
   {
      for (lista=pozycja->ident_head;lista!=NULL;lista=lista->next,ile+=sizeof(struct eliza_lista_struct))
      {
         ile+=strlen(lista->dane);
         ile+=sizeof(char);
      }
      for (lista=pozycja->funkc_head;lista!=NULL;lista=lista->next,ile+=sizeof(struct eliza_lista_struct))
      {
         ile+=strlen(lista->dane);
         ile+=sizeof(char);
      }
   }

   return ile;
}

ULONG FROM eliza_count(void)
{
   struct eliza_struct *pozycja;
   ULONG ile=0;

   for (pozycja=eliza_head;pozycja!=NULL;pozycja=pozycja->next,ile++);

   return ile;
}

ULONG FROM eliza_count_ident(void)
{
   struct eliza_struct *pozycja;
   struct eliza_lista_struct *lista;
   ULONG ile=0;

   for (pozycja=eliza_head;pozycja!=NULL;pozycja=pozycja->next)
      for (lista=pozycja->ident_head;lista!=NULL;lista=lista->next,ile++);

   return ile;
}

ULONG FROM eliza_count_funkc(void)
{
   struct eliza_struct *pozycja;
   struct eliza_lista_struct *lista;
   ULONG ile=0;

   for (pozycja=eliza_head;pozycja!=NULL;pozycja=pozycja->next)
      for (lista=pozycja->funkc_head;lista!=NULL;lista=lista->next,ile++);

   return ile;
}

char FROM eliza_check_lista(char *lista,ULONG linia)
{
   /*
   funkcja sprawdza poprawnoôê zapisu podanej listy

   algorytm:
   - lista musi mieê okreôlonâ dîugoôê
   - moûe byê poczâtek zmiennej $VARIABLE, czyli znak E_VARIABLE to wtedy wyjôcie
   - na poczâtku musi byê prefiks:
      - moûe byê E_TWIERDZENIE, E_PYTANIE i wtedy:
         - jeôli dalej jest E_OR to musi byê kolejny prefiks inny niû pierwszy
   - transformacja nielegalna listy:
      - zamiast wszystkich E_OR oddzielajâcych wyrazy czîonów, wstawiamy
        E_AND, co uîatwi póúniejszâ analizë, choê zafaîszuje rzeczywistâ
        wartoôê czîonu (listy) (ale umoûliwi szybkâ kontrolë)
   - kontrola poszczególnych czîonów:
      - pierwszym znakiem musi byê poczâtek czîonu E_AND lub E_NOT
      - kolejnym znakiem musi byê znak poczâtku wyrazu odmienionego lub podstawowego
      - kontrola koïca czîonu:
         - jeôli we wnëtrzu czîonu rozpoznajemy poczâtek innego, tj.
           jest prefiks E_AND lub E_OR i znak poczâtku wyrazu to informujemy
         - czîon musi sië koïczyê odpowiednim znakiem koïca, innym dla
           wyrazu odmienionego i podstawowego,
         - zaraz za znakiem koïca musi wystëpowaê poczâtek nowego czîonu
           lub koniec listy
         - wyrazy poîâczone E_OR sâ traktowane jak jeden czîon, ale sâ
           kontrolowane, aby byîa poprawna skîadnia, tj. )/( lub podobnie
      - wycinamy zbadany czîon i ponawiamy dla nastëpnych

   UWAGA! BRAK KONTROLI SKLADNI ZMIENNYCH WEWNETRZNYCH!!!
   */

   char temp_lista[2*MAX_LINE_LENGHT],txt[MAX_LINE_LENGHT];
   int poz;

   // dîugoôê listy
   if (strlen(lista)>=2*MAX_LINE_LENGHT-1)
   {
      sprintf(txt,"bîâd: znaleziono zbyt dîugâ listë (linia %d)\n",linia);
      con_put(txt);
      return RESULT_NO_OK;
   }

   // jeôli to lista automatyczna definiowana przez jakâô zmiennâ wewnëtrznâ to bez kontroli
   if (lista[0]==E_VARIABLE) return RESULT_OK;

   // kontrola prefiksa
   if (lista[0]!=E_PYTANIE && lista[0]!=E_TWIERDZENIE)
   {
      sprintf(txt,"bîâd: brak okreôlenia typu listy: pytanie/twierdzenie (linia %d)\n",linia);
      con_put(txt);
      return RESULT_NO_OK;
   }
   if (lista[1]==E_OR)
   {
      if ((lista[0]==E_PYTANIE && lista[2]!=E_TWIERDZENIE) ||
          (lista[0]==E_TWIERDZENIE && lista[2]!=E_PYTANIE))
      {
         sprintf(txt,"bîâd: úle okreôlony podwójny typ listy (linia %d)\n",linia);
         con_put(txt);
         return RESULT_NO_OK;
      }
   }

   // dla dalszej kontroli wycinamy prefiks
   sprintf(temp_lista,"%s",lista+(lista[1]==E_OR ?  3 : 1));

   // likwidacja E_OR w poîâczeniach wyrazów w czîonach
   for (poz=2;poz<(char) strlen(temp_lista);poz++)
      if (temp_lista[poz-1]==E_OR && (temp_lista[poz-2]==E_PODSTAWOWY_END || temp_lista[poz-2]==E_ODMIENIONY_END) &&
          (temp_lista[poz]==E_ODMIENIONY_START || temp_lista[poz]==E_PODSTAWOWY_START))
         temp_lista[poz-1]=E_AND;

   // kontrola kolejnych czîonów
   for (;;)
   {
      // pierwsza litera musi rozpoczynaê czîon KONIECZNY lub ZABRONIONY
      if (temp_lista[0]!=E_AND && temp_lista[0]!=E_NOT)
      {
         sprintf(txt,"bîâd: úle okreôlony typ czîonu (konieczny/zabroniony) (linia %d)\n",linia);
         con_put(txt);
         return RESULT_NO_OK;
      }
      // jeôli druga litera jest E_HARD to teû spoko, ale kopiujemy tutaj 3 literkë
      if (temp_lista[1]==E_HARD) temp_lista[1]=temp_lista[2];
      // druga litera musi okreôlaê czy wyraz jest PODSTAWOWY czy ODMIENIONY
      if (temp_lista[1]!=E_PODSTAWOWY_START && temp_lista[1]!=E_ODMIENIONY_START)
      {
         sprintf(txt,"bîâd: úle okreôlony wyraz (podstawowy/odmieniony) (linia %d)\n",linia);
         con_put(txt);
         return RESULT_NO_OK;
      }
      // szukamy koïca czîonu i sprawdzamy wnëtrze
      for (poz=2;poz<strlen(temp_lista);poz++)
      {
         // jeôli znak moûe byê poczâtkiem nowego czîonu to bîâd (kontrola wsteczna)
         if ((temp_lista[poz]==E_PODSTAWOWY_START || temp_lista[poz]==E_ODMIENIONY_START) &&
             (temp_lista[poz-1]==E_AND || temp_lista[poz-1]==E_NOT))
         {
            sprintf(txt,"bîâd: wykryto poczâtek nowego czîonu wewnâtrz innego (linia %d)\n",linia);
            con_put(txt);
            return RESULT_NO_OK;
         }

         // jeôli znak jest koïcem czîonu to ALL_OK (kontrola wsteczna)
         if ((temp_lista[poz]==E_AND || temp_lista[poz]==E_NOT) &&
             ((temp_lista[1]==E_PODSTAWOWY_START && temp_lista[poz-1]==E_PODSTAWOWY_END) ||
            (temp_lista[1]==E_ODMIENIONY_START && temp_lista[poz-1]==E_ODMIENIONY_END))) goto ALL_OK;
      }

      // jeôli znak jest koïcem czîonu to ALL_OK (z uwzglëdnieniemkoïca listy)
      if ((temp_lista[1]==E_PODSTAWOWY_START && temp_lista[poz-1]==E_PODSTAWOWY_END) ||
         (temp_lista[1]==E_ODMIENIONY_START && temp_lista[poz-1]==E_ODMIENIONY_END)) goto ALL_OK;

      sprintf(txt,"bîâd: czîon nie zostaî wîaôciwie zakoïczony (linia %d)\n",linia);
      con_put(txt);
      return RESULT_NO_OK;

      ALL_OK:

      // jeôli to koniec listy, to wychodzimy
      if (poz==(char) strlen(temp_lista)) return RESULT_OK;

      // kasujemy przetestowany czîon z listy
      memmove(temp_lista,temp_lista+poz,strlen(temp_lista+poz)+1);
   }
}

char FROM eliza_check_data(void)
{
   /*
   funkcja sprawdza poprawnoôê zapisu danych w bazie Elizy i wypisuje
   bîëdne listy oraz inne informacje o zauwaûonych bîëdach

   algorytm:
   - pobieramy kolejne pozycje (jeôli bîâd to informacja)
   - pobieramy kolejne listy pozycji (jeôli bîâd to informacja)
   - testujemy pojedyncze listy za pomocâ odpowiedniej funkcji
   */

   struct eliza_struct *pozycja;
   struct eliza_lista_struct *lista;
   ULONG linia=1,bledy=0;
   char txt[MAX_LINE_LENGHT];

   if (eliza_head==NULL)
   {
      con_put("nie ma bazy Elizy w pamiëci\n");
      return RESULT_OK;
   }

   con_put("rozpoczynam sprawdzanie bazy...\n");

   // pobieramy kolejne pozycje
   for (pozycja=eliza_head;pozycja!=NULL;pozycja=pozycja->next)
   {
      linia++; // znak poczâtku pozycji
      if (pozycja->ident_head==NULL)
      {
      	sprintf(txt,"uwaga: w pozycji nie podano list identyfikacyjnych (linia %d)\n",linia);
        con_put(txt);
      }

      for (lista=pozycja->ident_head;lista!=NULL;lista=lista->next,linia++)
         if (eliza_check_lista(lista->dane,linia)==RESULT_NO_OK) bledy++;

      linia++; // znak poczâtku list funkcyjnych
      if (pozycja->funkc_head==NULL)
      {
      	sprintf(txt,"uwaga: w pozycji nie podano list funkcyjnych (linia %d)\n",linia);
        con_put(txt);
      }

      for (lista=pozycja->funkc_head;lista!=NULL;lista=lista->next,linia++)
         if (eliza_check_lista(lista->dane,linia)==RESULT_NO_OK) bledy++;
   }

   if (bledy==0) sprintf(txt,"wynik sprawdzania: nie wykryto ûadnych bîëdów\n");
   else sprintf(txt,"wynik sprawdzania - bîëdnych linii: %d\n",bledy);
   con_put(txt);

   return RESULT_OK;
}
