/*
                                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

****************************************************************************************************
*/
// ********************************
// * * * F A K T Y  G R U P A * * *
// ********************************

char FROM fgrupa_pos_neg(char *grupa)
{
	// zwraca typ grupy: POS (twierdzâca) NEG (negacja, tj. z "nie")

	// w grupie NEG musi wystëpowaê czîon grupy + `nie `
	if (strstr(grupa,"nie ")!=NULL) return GRUPA_NEG;

	return GRUPA_POS;
}

char FROM fgrupa_get_body(char *ciag,char nr_grupy,char *wynik)
{
	// funkcja podstawia do wyniku treôê (body) grupy nr_grupy z ciâgu ciag

	char *ptr,nr1,temp[MAX_LINE_LENGHT];

	strcpy(temp,ciag);

	if ((ptr=strtok(temp,","))==NULL) goto error;

	for(nr1=0;nr1<nr_grupy;nr1++)
		if ((ptr=strtok(NULL,","))==NULL) goto error;

	strcpy(wynik,ptr);

	return RESULT_OK;

	error:
	sprintf(wynik,"");
	con_put("bîâd: próba pobrania nadmiarowej grupy\n");
	return RESULT_NO_OK;
}

char FROM fgrupa_cmp_body(char *grupa1,char *grupa2)
{
	/*
	funkcja porównuje wyrazy podstawowe podanych grup

	funkcja zwraca:
	- GRUPA_ZGODNA - gdy wyrazy sâ zgodne
	- GRUPA_SPRZECZNA - gdy wyrazy sâ zgodne i wystëpuje zaprzeczenie NIE
	- GRUPA_BRAK_DANYCH - gdy nie zachodzi ûadna z powyûszych sytuacji
	*/

   // kiedyô byîo int
   char len1,len2;

	// czy grupy zgodne?
	if (strcmp(grupa1,grupa2)==0) return GRUPA_ZGODNA;

	// czy grupy sprzeczne?
	// - jedna grupa musi byê POS druga NEG
	// - grupa NEG musi byê o 4 znaki dîuûsza od POS
	//   (proôciej/szybciej/krócej - róûnica dîugoôci musi byê 4,
	//   prawdopodobieïstwo bîëdnego zadziaîania jest praktycznie zerowe)
	// - grupa NEG musi zawieraê w sobie grupë POS

   len1=strlen(grupa1);
   len2=strlen(grupa2);   

	if (abs(len1-len2)!=4) return GRUPA_BRAK_DANYCH;

	switch (fgrupa_pos_neg(grupa1))
	{
		case GRUPA_POS:
			if (fgrupa_pos_neg(grupa2)==GRUPA_NEG && strstr(grupa2,grupa1)!=NULL) return GRUPA_SPRZECZNA;
			break;
		case GRUPA_NEG:
			if (fgrupa_pos_neg(grupa2)==GRUPA_POS && strstr(grupa1,grupa2)!=NULL) return GRUPA_SPRZECZNA;
			break;
	}

	return GRUPA_BRAK_DANYCH;
}
char FROM fgrupa_cmp_funkcja(char funkcja1,char funkcja2)
{
	/*
		funkcja zwraca wartoôê RESULT_OK, jeôli:
		- funkcja1==funkcja2 lub
		- funkcja1 jest toûsama z funkcja2, tj.:
			- funkcja1==ORZECZENIE_CZASOWNIKOWE funkcja2==LACZNIK lub
			- funkcja1==LACZNIK funkcja2==ORZECZNIE_CZASOWNIKOWE
	*/

	if ((funkcja1==funkcja2) ||
	    (funkcja1==ORZECZENIE_CZASOWNIKOWE && funkcja2==LACZNIK) ||
	    (funkcja1==LACZNIK && funkcja2==ORZECZENIE_CZASOWNIKOWE)) return RESULT_OK;

	return RESULT_NO_OK;
}

char FROM fgrupa_exist(struct obiekt_struct *obiekt,char *grupa,char funkcja,char *nr_grupy,char nr_start)
{
	/*
	funkcja szuka podanej grupy w podanym obiekcie poczâwszy od grupy nr_start
	funkcja znajduje numer zgodnej lub sprzecznej grupy w liôcie podstawowej
	jeôli nie ma takiej grupy to wynikiem jest -1

	funkcja zwraca:
	- GRUPA_ZGODNA - gdy wystëpuje grupa zgodna z podanâ
	- GRUPA_SPRZECZNA - gdy wystëpuje grupa sprzeczna
	- GRUPA_BRAK_DANYCH - gdy nie zachodzi ûadna z powyûszych sytuacji

	algorytm:
	- pobieramy grupë z obiektu - jeôli juû nie ma to BRAK DANYCH
	- jeôli grupy majâ róûne funkcje, to pobieramy nastëpnâ, i od poczâtku
	  w przeciwnym razie dalej:
	- jeôli grupy majâ te same wyrazy podstawowe to GRUPA ZGODNA
	- jeôli grupy sâ wzajemnie sprzeczne to GRUPA SPRZECZNA
	- skok na poczâtek algorytmu
	*/

	char *ptr,temp[MAX_LINE_LENGHT];

	strcpy(temp,obiekt->podstawowe);
	for (ptr=strtok(temp,","),(*nr_grupy)=0;ptr!=NULL;ptr=strtok(NULL,","),(*nr_grupy)++)
	{
		// rozpoczynamy proces od grupy nr_start
		if ((*nr_grupy)<=nr_start) continue;

		// wymagana jest zgodnoôê funkcji w zdaniu
		if (fgrupa_cmp_funkcja(obiekt->funkcja[(*nr_grupy)],funkcja)==RESULT_NO_OK) continue;

		switch (fgrupa_cmp_body(ptr,grupa))
		{
			case GRUPA_ZGODNA: return GRUPA_ZGODNA;
			case GRUPA_SPRZECZNA: return GRUPA_SPRZECZNA;
		}
	}

	(*nr_grupy)=-1; // brak takiej grupy
	return GRUPA_BRAK_DANYCH;
}

// ********************************
// * * * F A K T Y  L E A R N * * *
// ********************************

char FROM flearn_obiekt_add(void)
{
	struct obiekt_struct *obiekt;

	// tworzymy obiekt z danymi skopiowanymi z nowego obiektu
	if ((obiekt=fakty_malloc_obiekt())==NULL) goto no_memory;

	obiekt->ile_grup=rozbior.obiekt->ile_grup;

	if ((obiekt->time=(char *) malloc(strlen(rozbior.obiekt->time)+1))==NULL) goto no_memory;
	strcpy(obiekt->time,rozbior.obiekt->time);

	if ((obiekt->odmienione=(char *) malloc(strlen(rozbior.obiekt->odmienione)+1))==NULL) goto no_memory;
	strcpy(obiekt->odmienione,rozbior.obiekt->odmienione);

	if ((obiekt->podstawowe=(char *) malloc(strlen(rozbior.obiekt->podstawowe)+1))==NULL) goto no_memory;
	strcpy(obiekt->podstawowe,rozbior.obiekt->podstawowe);

	if ((obiekt->funkcja=(char *) malloc(rozbior.obiekt->ile_grup))==NULL) goto no_memory;
	memcpy(obiekt->funkcja,rozbior.obiekt->funkcja,obiekt->ile_grup);

	// kopiujemy wszystkie fakty proste
   if ((obiekt->ident_head=kopiuj_fakty_proste(rozbior.obiekt->ident_head))==NULL) goto error_fp;
   if ((obiekt->funkc_head=kopiuj_fakty_proste(rozbior.obiekt->funkc_head))==NULL) goto error_fp;

   obiekt->next=rozbior.fakt->obiekt_head;
   rozbior.fakt->obiekt_head=obiekt;

   mapa_learn(rozbior.fakt,rozbior.fakt->obiekt_head);

   return RESULT_OK;

   no_memory:
   con_put("bîâd: brak pamiëci dla danych\n");
   fakty_erase_obiekt(obiekt);
   return RESULT_NO_OK;

   error_fp:
   con_put("bîâd: nie mogë utworzyê faktów prostych\n");
   fakty_erase_obiekt(obiekt);
   return RESULT_NO_OK;
}

char FROM flearn_upgrade_this(struct obiekt_struct *obiekt)
{
	/*
	sprawdzamy, czy obiekt moûe zostaê skasowany - tj. czy w rozbior.obiekt
	zawarta jest caîa treôê tego obiektu, tj. wtedy gdy:
	- rozbior.obiekt zawiera WSZYSTKIE grupy obiekt (podstawowe)
	- zgadzajâ sië funkcje odpowiednich grup
	*/

	char nr1,nr2,txt[MAX_LINE_LENGHT];

	for (nr1=0;nr1<obiekt->ile_grup;nr1++)
	{
		if (fgrupa_get_body(obiekt->podstawowe,nr1,txt)==RESULT_NO_OK)
      {
         if (view.fakt==KOD_TRUE) con_put("bîâd: upgrade obiektu\n");
         return RESULT_NO_OK;
      }
		if (fgrupa_exist(rozbior.obiekt,txt,obiekt->funkcja[nr1],&nr2,-1)!=GRUPA_ZGODNA) return RESULT_NO_OK;
	}

	return RESULT_OK;
}

void FROM flearn_obiekt(void)
{
	/*
	algorytm:
	- w przypadku, gdy obiekt nie ma faktu macierzystego (jest caîkiem nowy):
		- tworzenie faktu macierzystego
	- w przypadku, gdy obiekt ma fakt macierzysty (fakt obiektu jest w bazie):
		- robiony jest upgrade - kasowane obiekty zawierajâce sië w nowym obiekcie
	- tworzenie obiektu doîâczonego do faktu macierzystego
	- kopiowanie danych z nowego obiektu do obiektu utworzonego
	- w przypadku, gdy byî tworzony fakt macierzysty:
		- doîâczenie nowego faktu do bazy na samym poczâtku
   - jeôli obiekt byî utworzony jako zupeînie nowy, to ew. uzupeînienie mapy korelacji
	*/

	struct obiekt_struct *obiekt,*last_obiekt;

	if (rozbior.fakt==NULL)
	{
		// tworzenie faktu macierzystego
		if ((rozbior.fakt=(struct fakt_struct *) malloc(sizeof(struct fakt_struct)))==NULL)
		{
			con_put("bîâd: brak pamiëci dla nowego faktu\n");
			return;
		}
		rozbior.fakt->obiekt_head=NULL;
		if (flearn_obiekt_add()==RESULT_NO_OK)
		{
			free(rozbior.fakt);
		}
		else
		{
			rozbior.fakt->next=fakt_head;
			fakt_head=rozbior.fakt;
		}
	}
	else
	{
		// upgrade
		NEXT:
		for (obiekt=rozbior.fakt->obiekt_head,last_obiekt=NULL;obiekt!=NULL;last_obiekt=obiekt,obiekt=obiekt->next)
		{
			if (flearn_upgrade_this(obiekt)==RESULT_OK)
			{
				if (last_obiekt==NULL) rozbior.fakt->obiekt_head=obiekt->next;
				else last_obiekt->next=obiekt->next;

				fakty_erase_obiekt(obiekt);
				goto NEXT;
			}
		}
		flearn_obiekt_add();
	}
}

// ******************************
// * * * F A K T Y  M A I N * * *
// ******************************

void FROM fmain_oznajm(void)
{
   // sprawdzenie, jak ma sië podany obiekt do danych w bazie

 	status.quality=100;
   switch (fident_obiekt_simple(rozbior.fakt,rozbior.obiekt,NULL,FULL_DEPTH,MODE_ZGODNOSC))
	{
		case FAKT_ZGODNY:
			if (status.quality==100) con_put_komunikat((fakty_main_define()==NOWY_ROZMOWCA) ? E_DENISE_ZMIANA_ROZMOWCY : E_DENISE_TWIERDZENIE_ZGODNE);
			// fakt jest w bazie, ale nie jest dokladnie taki sam, np.
			// trzeba go wywnioskowac, albo poprzez wyrazy bliskoznaczne
			else
			{
            fakty_main_define();
            con_put_komunikat(E_DENISE_ZWIEKSZAM_PEWNOSC);
				flearn_obiekt();
			}
			break;
		case FAKT_SPRZECZNY:
         fakty_main_define();
         if (status.quality==100) con_put_komunikat(E_DENISE_TWIERDZENIE_SPRZECZNE);
         else
         {
            con_put_komunikat(E_DENISE_WERYFIKUJE_SPRZECZNOSC);
				flearn_obiekt();
			}
			break;
		case FAKT_BRAK_DANYCH:
			flearn_obiekt();
         con_put_komunikat((fakty_main_define()==NOWY_ROZMOWCA) ? E_DENISE_ZMIANA_ROZMOWCY : E_DENISE_ZAPAMIETUJE_INFORMACJE);
			break;
	}
}

void FROM fmain_pyt(void)
{
	// odpowiadamy, tylko jeôli fakt zostaî zidentyfikowany,
   // lub gdy pytanie byîo o rdzeï
	// w przeciwnym razie brak danych

	if (rozbior.fakt!=NULL ||
       (rozbior.pytanie==PYTANIE_PODMIOTOWE && sentence.oznajm_pyt==ZDANIE_PYTAJACE) ||
       (rozbior.fakt==NULL && rozbior.pytanie==PYTANIE_SZCZEGOLOWE))
	{
		if (answer_main(rozbior.fakt)==RESULT_NO_OK) con_put("bîâd: nie znam tej formuîy pytania\n");
		skasuj_obiekt_rozbioru();
	}
	else con_put_komunikat(E_DENISE_PYTANIE_BRAK_DANYCH);
}

char FROM fakty_main_define(void)
{
   // funkcja definiuje temat, rozmócë, denise, jeôli to moûliwe
   // zwraca informacjë, czy zostaî zmieniony rozmówca

   if ((sentence.oznajm_pyt==ZDANIE_OZNAJMUJACE) ||
       (sentence.oznajm_pyt==ZDANIE_PYTAJACE && rozbior.pytanie!=PYTANIE_PODMIOTOWE))
   {
     	// rozpoznanie podmiotu
  	   if (rozbior.typ_podmiotu==PODMIOT_OKRESLONY && rozbior.fakt!=NULL)
      	switch (rozbior.podmiot_ident)
  	   	{
            case PODMIOT_ROZMOWCA:
               if (rozmowca.fakt!=rozbior.fakt &&
                   sentence.twierdz_przecz==ZDANIE_TWIERDZACE &&
                   sentence.oznajm_pyt==ZDANIE_OZNAJMUJACE &&
                   cmp_wyraz_podst(rozbior.orzeczenie,"byê")==RESULT_OK)
               {
                  rozmowca.fakt=rozbior.fakt;
                  rozmowca.rodzaj=*ptr_rodzaj(rozbior.dane[rozbior.podmiot]);
                  strcpy(rozmowca.name,sentence.wyraz[rozbior.grupa[rozbior.podmiot].wyraz[0]]);
                  return NOWY_ROZMOWCA;
               }
               return NIC_NOWEGO;
      		case PODMIOT_DENISE:
               if (sentence.twierdz_przecz==ZDANIE_TWIERDZACE &&
                   sentence.oznajm_pyt==ZDANIE_OZNAJMUJACE &&
                   cmp_wyraz_podst(rozbior.orzeczenie,"byê")==RESULT_OK)
               {
                  denise.fakt=rozbior.fakt;
               }
               return NIC_NOWEGO;
      		case PODMIOT_TEMAT:
               temat.fakt=rozbior.fakt;
               temat.rodzaj=*ptr_rodzaj(rozbior.dane[rozbior.podmiot]);
               return NIC_NOWEGO;
   	   }
   }

   return NIC_NOWEGO;
}

char FROM fakty_main(void)
{
	/*
	TWIERDZENIE
	1. identyfikacja faktu dla nowego obiektu
	3. jeôli obiekt ZGODNY - POSIADAM TE INFORMACJE
	4. jeôli obiekt SPRZECZNY - INFORMACJE SPRZECZNE
	5. jeôli obiekt BRAK DANYCH - dopisujë dane

	PYTANIE
   * jeôli pytanie szczegóîowe lub ogólne:
   	1. identyfikacja faktu dla nowego obiektu
	   2. jeôli obiekt ZGODNY - POSIADAM TE INFORMACJE
   	3. jeôli obiekt SPRZECZNY - INFORMACJE SPRZECZNE
   	4. jeôli obiekt BRAK DANYCH - NIE POSIADAM TYCH INFORMACJI
   * jeôli pytanie podmiotowe:
      1. utworzenie listy odpowiedzi za pomocâ inteligencji
      2. wybranie jednej za pomocâ intuicji
	*/

	clock_t time=clock();
	char txt[MAX_LINE_LENGHT];

	if (view.fakt==KOD_TRUE) con_put("\n* * * A N A L I Z A  F A K T U * * *\n");

	// utworzenie opisowej formy faktów prostych
	utworz_fakty_proste();

	// utworzenie obiektu faktów prostych na podstawie rozbioru zdania
	if (utworz_obiekt_rozbioru()==RESULT_NO_OK) return RESULT_NO_OK;

   if (sentence.oznajm_pyt==ZDANIE_OZNAJMUJACE || (sentence.oznajm_pyt==ZDANIE_PYTAJACE && rozbior.pytanie!=PYTANIE_PODMIOTOWE))
   {
     	// próba identyfikacji - rozpoznanie macierzystego faktu
  	   rozbior.fakt=fident_mother_fakt(rozbior.obiekt,NULL);
    	if (view.fakt==KOD_TRUE) rozbior.fakt ? con_put("* fakt zostaî zidentyfikowany\n") : con_put("* fakt nie zostaî zidentyfikowany\n");
   }
   else rozbior.fakt=NULL;

   switch (sentence.oznajm_pyt)
   {
      case ZDANIE_OZNAJMUJACE:
#ifdef PC_WIN
         sprintf(txt,"(%.3f sec.) analiza faktu: zdanie oznajmujâce...",(clock()-proces_clock)/CLK_TCK);
         win_con_put(LINE_PROCES,txt);
#endif
         fmain_oznajm();
         break;
      case ZDANIE_PYTAJACE:
#ifdef PC_WIN
         sprintf(txt,"(%.3f sec.) analiza faktu: zdanie pytajâce...",(clock()-proces_clock)/CLK_TCK);
         win_con_put(LINE_PROCES,txt);
#endif
      	fmain_pyt();
         fakty_main_define();
      	break;
   }

	if (view.fakt==KOD_TRUE)
	{
		sprintf(txt,"czas analizy faktu: %.3fs\n",(float) (clock()-time)/CLK_TCK);
		con_put(txt);
	}

	skasuj_obiekt_rozbioru();

	return RESULT_OK;
}
