/*
cod preluat din cartea(bibliografie[1]):
BALCAN Maria Florina, HRISTEA Florentina, 
Aspecte ale Cautarii si Reprezentarii Cunostintelor in Inteligenta Artificiala,
Editura Universitatii din Bucuresti, 2004, 
pg 216
*/

close_all:-current_stream(_,_,S),close(S),fail;true.

curata_bc:-current_predicate(P), abolish(P,[force(true)]), fail;true.


:-use_module(library(lists)).

:-use_module(library(file_systems)).
:-use_module(library(system)).

:-op(900,fy,not).

:-dynamic fapt/3.

:-dynamic interogat/1.

:-dynamic scop/1.

:-dynamic interogabil/3.

:-dynamic regula/3.


tab(N):-N>0,write(' '),N1 is N-1, tab(N1).
tab(0).


not(P):-P,!,fail.

not(_).


scrie_lista([]):-nl.

scrie_lista([H|T]) :-
	write(H), tab(1),
	scrie_lista(T).

             
afiseaza_fapte :-
	write('Fapte existente in baza de cunostinte:'),
	nl,nl, write(' (Atribut,valoare) '), nl,nl,
	listeaza_fapte,nl.


listeaza_fapte:-  
	fapt(av(Atr,Val),FC,_), 
	write('('),write(Atr),write(','),
	write(Val), write(')'),
	write(','), write(' certitudine '),
	FC1 is integer(FC),write(FC1),
	nl,fail.

listeaza_fapte.


lista_float_int([],[]).

lista_float_int([Regula|Reguli],[Regula1|Reguli1]):-
	(Regula \== utiliz,
	Regula1 is integer(Regula);
	Regula ==utiliz, Regula1=Regula),
	lista_float_int(Reguli,Reguli1).


pornire :-
	retractall(interogat(_)),
	retractall(fapt(_,_,_)),
	repeat,
	write('Introduceti una din urmatoarele optiuni: '),
	nl,nl,
	write(' (Incarca Consulta Reinitiaza  Afisare_fapte  Cum   Iesire) '),
	nl,nl,write('|: '),citeste_linie([H|T]),
	executa([H|T]), H == iesire.


executa([incarca]) :- 
	incarca,!,nl,
	write('Fisierul dorit a fost incarcat'),nl.

executa([consulta]) :- 
	scopuri_princ,!.


executa([reinitiaza]) :- 
	retractall(interogat(_)),
	retractall(fapt(_,_,_)),!.


executa([afisare_fapte]) :-
	afiseaza_fapte,!.


executa([cum|L]) :- cum(L),!.

executa([iesire]):-!.

executa([_|_]) :-
	write('Comanda incorecta! '),nl.


scopuri_princ :-
	scop(Atr),
	determina(Atr), 
	afiseaza_scop(Atr),
	fail.

scopuri_princ.


determina(Atr) :-
realizare_scop(av(Atr,_),_,[scop(Atr)]),!.

determina(_).


afiseaza_scop(Atr) :-
	nl,fapt(av(Atr,Val),FC,_),
	FC >= 20,scrie_scop(av(Atr,Val),FC),
	nl,fail.

afiseaza_scop(_):-nl,nl.


scrie_scop(av(Atr,Val),FC) :-
	transformare(av(Atr,Val), X),
	scrie_lista(X),tab(2),
	write(' '),
	write('factorul de certitudine este '),
	FC1 is integer(FC),write(FC1).


realizare_scop(not Scop,Not_FC,Istorie) :-
	realizare_scop(Scop,FC,Istorie),
	Not_FC is - FC, !.

realizare_scop(Scop,FC,_) :-
	fapt(Scop,FC,_), !.

realizare_scop(Scop,FC,Istorie) :-
	pot_interoga(Scop,Istorie),
	!,realizare_scop(Scop,FC,Istorie).

realizare_scop(Scop,FC_curent,Istorie) :-
	fg(Scop,FC_curent,Istorie).

        
fg(Scop,FC_curent,Istorie) :-
	regula(N, premise(Lista), concluzie(Scop,FC)),
	demonstreaza(N,Lista,FC_premise,Istorie),
	ajusteaza(FC,FC_premise,FC_nou),
	actualizeaza(Scop,FC_nou,FC_curent,N),
	FC_curent == 100,!.

fg(Scop,FC,_) :- fapt(Scop,FC,_).


pot_interoga(av(Atr,_),Istorie) :-
	not interogat(av(Atr,_)),
	interogabil(Atr,Optiuni,Mesaj),
	interogheaza(Atr,Mesaj,Optiuni,Istorie),nl,
	asserta( interogat(av(Atr,_)) ).


cum([]) :- write('Scop? '),nl,
	write('|:'),citeste_linie(Linie),nl,
	transformare(Scop,Linie), cum(Scop).

cum(L) :- 
	transformare(Scop,L),nl, cum(Scop).

cum(not Scop) :- 
	fapt(Scop,FC,Reguli),
	lista_float_int(Reguli,Reguli1),
	FC < -20,transformare(not Scop,PG),
	append(PG,[a,fost,derivat,cu, ajutorul, 'regulilor: '|Reguli1],LL),
	scrie_lista(LL),nl,afis_reguli(Reguli),fail.

cum(Scop) :-
	fapt(Scop,FC,Reguli),
	lista_float_int(Reguli,Reguli1),
	FC > 20,transformare(Scop,PG),
	append(PG,[a,fost,derivat,cu, ajutorul, 'regulilor: '|Reguli1],LL),
	scrie_lista(LL),nl,afis_reguli(Reguli),
	fail.

cum(_).


afis_reguli([]).

afis_reguli([N|X]) :-
	afis_regula(N),
	premisele(N),
	afis_reguli(X).

afis_regula(N) :-
	regula(N, premise(Lista_premise),
	concluzie(Scop,FC)),NN is integer(N),
	scrie_lista(['regula  ',NN]),
	scrie_lista(['  Daca']),
	scrie_lista_premise(Lista_premise),
	scrie_lista(['  Atunci']),
	transformare(Scop,Scop_tr),
	append(['   '],Scop_tr,L1),
	FC1 is integer(FC),append(L1,[FC1],LL),
	scrie_lista(LL),nl.


scrie_lista_premise([]).

scrie_lista_premise([H|T]) :-
	transformare(H,H_tr),
	tab(5),scrie_lista(H_tr),
	scrie_lista_premise(T).


transformare(av(A,da),[A]) :- !.

transformare(not av(A,da), [not,A]) :- !.

transformare(av(A,nu),[not,A]) :- !.

transformare(av(A,V),[A,este,V]).


premisele(N) :-
	regula(N, premise(Lista_premise), _),
	!, cum_premise(Lista_premise).

        
cum_premise([]).

cum_premise([Scop|X]) :-
	cum(Scop),
	cum_premise(X).

        
interogheaza(Atr,Mesaj,[da,nu],Istorie) :-
	!,write(Mesaj),nl,
	de_la_utiliz(X,Istorie,[da,nu]),
	det_val_fc(X,Val,FC),
	asserta( fapt(av(Atr,Val),FC,[utiliz]) ).

interogheaza(Atr,Mesaj,Optiuni,Istorie) :-
	write(Mesaj),nl,
	citeste_opt(VLista,Optiuni,Istorie),
	assert_fapt(Atr,VLista).


citeste_opt(X,Optiuni,Istorie) :-
	append(['('],Optiuni,Opt1),
	append(Opt1,[')'],Opt),
	scrie_lista(Opt),
	de_la_utiliz(X,Istorie,Optiuni).


de_la_utiliz(X,Istorie,Lista_opt) :-
	repeat,write(': '),citeste_linie(X),
	proceseaza_raspuns(X,Istorie,Lista_opt).


proceseaza_raspuns([de_ce],Istorie,_) :- nl,afis_istorie(Istorie),!,fail.


proceseaza_raspuns([X],_,Lista_opt):-
	member(X,Lista_opt).

proceseaza_raspuns([X,fc,FC],_,Lista_opt):-
	member(X,Lista_opt),float(FC).


assert_fapt(Atr,[Val,fc,FC]) :-
!,asserta( fapt(av(Atr,Val),FC,[utiliz]) ).

assert_fapt(Atr,[Val]) :-
asserta( fapt(av(Atr,Val),100,[utiliz])).


det_val_fc([nu],da,-100).

det_val_fc([nu,FC],da,NFC) :- NFC is -FC.

det_val_fc([nu,fc,FC],da,NFC) :- NFC is -FC.

det_val_fc([Val,FC],Val,FC).

det_val_fc([Val,fc,FC],Val,FC).

det_val_fc([Val],Val,100).

        
afis_istorie([]) :- nl.

afis_istorie([scop(X)|T]) :-
	scrie_lista([scop,X]),!,
	afis_istorie(T).

afis_istorie([N|T]) :-
	afis_regula(N),!,afis_istorie(T).


demonstreaza(N,ListaPremise,Val_finala,Istorie) :-
	dem(ListaPremise,100,Val_finala,[N|Istorie]),!.


dem([],Val_finala,Val_finala,_).

dem([H|T],Val_actuala,Val_finala,Istorie) :-
	realizare_scop(H,FC,Istorie),
	Val_interm is min(Val_actuala,FC),
	Val_interm >= 20,
	dem(T,Val_interm,Val_finala,Istorie).

 
actualizeaza(Scop,FC_nou,FC,RegulaN) :-
	fapt(Scop,FC_vechi,_),
	combina(FC_nou,FC_vechi,FC),
	retract( fapt(Scop,FC_vechi,Reguli_vechi) ),
	asserta( fapt(Scop,FC,[RegulaN | Reguli_vechi]) ),!.

actualizeaza(Scop,FC,FC,RegulaN) :-
	asserta( fapt(Scop,FC,[RegulaN]) ).


ajusteaza(FC1,FC2,FC) :-
	X is FC1 * FC2 / 100,
	FC is round(X).

combina(FC1,FC2,FC) :-
	FC1 >= 0,FC2 >= 0,
	X is FC2*(100 - FC1)/100 + FC1,
	FC is round(X).

combina(FC1,FC2,FC) :-
	FC1 < 0,FC2 < 0,
	X is - ( -FC1 -FC2 * (100 + FC1)/100),
	FC is round(X).

combina(FC1,FC2,FC) :-
	(FC1 < 0; FC2 < 0),
	(FC1 > 0; FC2 > 0),
	FCM1 is abs(FC1),FCM2 is abs(FC2),
	MFC is min(FCM1,FCM2),
	X is 100 * (FC1 + FC2) / (100 - MFC),
	FC is round(X).


incarca :-
	write('Introduceti numele fisierului care doriti sa fie incarcat: '),nl, write('|:'),read(F),
	file_exists(F),!,incarca(F).

incarca:-write('Nume incorect de fisier! '),nl,fail.


incarca(F) :-
	retractall(interogat(_)),retractall(fapt(_,_,_)),
	retractall(scop(_)),retractall(interogabil(_,_,_)),
	retractall(regula(_,_,_)),
	see(F),incarca_reguli,seen,!.


incarca_reguli :-
	repeat,citeste_propozitie(L),
	proceseaza(L),L == [end_of_file],nl.


proceseaza([end_of_file]):-!.

proceseaza(L) :-
	trad(R,L,[]),assertz(R), !.

trad(scop(X)) --> [scopul,este,X].

trad(scop(X)) --> [scopul,X].

trad(interogabil(Atr,M,P)) --> 
	[intreaba,Atr],lista_optiuni(M),afiseaza(Atr,P).

trad(regula(N,premise(Daca),concluzie(Atunci,F))) --> identificator(N),daca(Daca),atunci(Atunci,F).

trad('Eroare la parsare'-L,L,_).


lista_optiuni(M) --> [optiuni,'('],lista_de_optiuni(M).

lista_de_optiuni([Element]) -->  [Element,')'].

lista_de_optiuni([Element|T]) --> [Element],lista_de_optiuni(T).


afiseaza(_,P) -->  [afiseaza,P].

afiseaza(P,P) -->  [].

identificator(N) --> [regula,N].


daca(Daca) --> [daca],lista_premise(Daca).


lista_premise([Daca]) --> propoz(Daca),[atunci].

lista_premise([Prima|Celalalte]) --> propoz(Prima),[si],lista_premise(Celalalte).

lista_premise([Prima|Celalalte]) --> propoz(Prima),[','],lista_premise(Celalalte).


atunci(Atunci,FC) --> propoz(Atunci),[fc],[FC].

atunci(Atunci,100) --> propoz(Atunci).


propoz(not av(Atr,da)) --> [not,Atr].

propoz(av(Atr,Val)) --> [Atr,este,Val].

propoz(av(Atr,da)) --> [Atr].


citeste_linie([Cuv|Lista_cuv]) :-
	get_code(Car),
	citeste_cuvant(Car, Cuv, Car1), 
	rest_cuvinte_linie(Car1, Lista_cuv).
 
      
% -1 este codul ASCII pt EOF

rest_cuvinte_linie(-1, []):-!.
    
rest_cuvinte_linie(Car,[]) :-(Car==13;Car==10), !.

rest_cuvinte_linie(Car,[Cuv1|Lista_cuv]) :-
	citeste_cuvant(Car,Cuv1,Car1),      
	rest_cuvinte_linie(Car1,Lista_cuv).


citeste_propozitie([Cuv|Lista_cuv]) :-
	get_code(Car),citeste_cuvant(Car, Cuv, Car1), 
	rest_cuvinte_propozitie(Car1, Lista_cuv).
 
     
rest_cuvinte_propozitie(-1, []):-!.
    
rest_cuvinte_propozitie(Car,[]) :-Car==46, !.

rest_cuvinte_propozitie(Car,[Cuv1|Lista_cuv]) :-
	citeste_cuvant(Car,Cuv1,Car1),      
	rest_cuvinte_propozitie(Car1,Lista_cuv).


citeste_cuvant(-1,end_of_file,-1):-!.

citeste_cuvant(Caracter,Cuvant,Caracter1) :-   
	caracter_cuvant(Caracter),!, 
	name(Cuvant, [Caracter]),get_code(Caracter1).

citeste_cuvant(Caracter, Numar, Caracter1) :-
	caracter_numar(Caracter),!,
	citeste_tot_numarul(Caracter, Numar, Caracter1).
 

citeste_tot_numarul(Caracter,Numar,Caracter1):-
	determina_lista(Lista1,Caracter1),
	append([Caracter],Lista1,Lista),
	transforma_lista_numar(Lista,Numar).


determina_lista(Lista,Caracter1):-
	get_code(Caracter), 
	(caracter_numar(Caracter),
	determina_lista(Lista1,Caracter1),
	append([Caracter],Lista1,Lista); 
	\+(caracter_numar(Caracter)),
	Lista=[],Caracter1=Caracter).
 

transforma_lista_numar([],0).

transforma_lista_numar([H|T],N):-
	transforma_lista_numar(T,NN), 
	lungime(T,L), Aux is exp(10,L),
	HH is H-48,N is HH*Aux+NN.


lungime([],0).

lungime([_|T],L):-
	lungime(T,L1),
	L is L1+1.


% 39 este codul ASCII pt '

citeste_cuvant(Caracter,Cuvant,Caracter1) :-
	Caracter==39,!,
	pana_la_urmatorul_apostrof(Lista_caractere),
	L=[Caracter|Lista_caractere],
	name(Cuvant, L),get_code(Caracter1).
        

pana_la_urmatorul_apostrof(Lista_caractere):-
	get_code(Caracter),
	(Caracter == 39,Lista_caractere=[Caracter];
	Caracter\==39,
	pana_la_urmatorul_apostrof(Lista_caractere1),
	Lista_caractere=[Caracter|Lista_caractere1]).


citeste_cuvant(Caracter,Cuvant,Caracter1) :-          
	caractere_in_interiorul_unui_cuvant(Caracter),!,              
	((Caracter>64,Caracter<91),!,
	Caracter_modificat is Caracter+32;
	Caracter_modificat is Caracter),                              
	citeste_intreg_cuvantul(Caractere,Caracter1),
	name(Cuvant,[Caracter_modificat|Caractere]).
        

citeste_intreg_cuvantul(Lista_Caractere,Caracter1) :-
	get_code(Caracter),
	(caractere_in_interiorul_unui_cuvant(Caracter),
	((Caracter>64,Caracter<91),!, 
	Caracter_modificat is Caracter+32;
	Caracter_modificat is Caracter),
	citeste_intreg_cuvantul(Lista_Caractere1, Caracter1),
	Lista_Caractere=[Caracter_modificat|Lista_Caractere1]; \+(caractere_in_interiorul_unui_cuvant(Caracter)),
	Lista_Caractere=[], Caracter1=Caracter).


citeste_cuvant(_,Cuvant,Caracter1) :-                
	get_code(Caracter),       
	citeste_cuvant(Caracter,Cuvant,Caracter1).
 

caracter_cuvant(C):-member(C,[44,59,58,63,33,46,41,40]).


% am specificat codurile ASCII pentru , ; : ? ! . ) (

caractere_in_interiorul_unui_cuvant(C):-
	C>64,C<91;C>47,C<58;
	C==45;C==95;C>96,C<123.

caracter_numar(C):-C<58,C>=48.

