% This buffer is for notes you don't want to save.
% If you want to create a file, visit that file with C-x C-f,
% then enter the text in that file's own buffer.

:- dynamic estado/2,ambiente/1, visitados/1,objetivo/3, restricao/2,atacado/2,cont/1.

%:- initialization
%        mutex_create(environment).


visitados([[sobre(a,mesa1),sobre(b,a),sobre(c,b),sobre(d,c),livre(d),livre(mesa2),livre(mesa3)]]).
ambiente([sobre(a,mesa1),sobre(b,a),sobre(c,b),sobre(d,c),livre(d),livre(mesa2),livre(mesa3)]).

cont(0).


incrementa_cont :-
		cont(X),
		X1 is X + 1,
		retractall(cont(_)),
		assert(cont(X1)).
/*visitados([[sobre(a,mesa1),sobre(b,a),sobre(c,b),sobre(d,c),livre(d),livre(mesa2),livre(mesa3)]]).
ambiente([sobre(a,mesa1),sobre(b,a),sobre(c,b),sobre(d,c),livre(d),livre(mesa2),livre(mesa3)]).


visitados([[sobre(g,f),sobre(f,e),sobre(e,d),sobre(d,c),sobre(a,mesa1),sobre(b,a),sobre(c,b),livre(g),livre(mesa2),livre(mesa3),livre(mesa4),livre(mesa5),livre(mesa6)]]).
ambiente([sobre(g,f),sobre(f,e),sobre(e,d),sobre(d,c),sobre(a,mesa1),sobre(b,a),sobre(c,b),livre(g),livre(mesa2),livre(mesa3),livre(mesa4),livre(mesa5),livre(mesa6)]).

visitados([[sobre(g,f),sobre(f,e),sobre(e,d),sobre(d,c),sobre(a,mesa1),sobre(b,a),sobre(c,b),livre(g),livre(mesa2),livre(mesa3)]]).
ambiente([sobre(g,f),sobre(f,e),sobre(e,d),sobre(d,c),sobre(a,mesa1),sobre(b,a),sobre(c,b),livre(g),livre(mesa2),livre(mesa3)]).
*/

mesa(mesa1).
mesa(mesa2).
mesa(mesa3).

objetivo(a,mesa3).
objetivo(b,f).
objetivo(c,d).
objetivo(d,b).
objetivo(e,a).
objetivo(f,g).
objetivo(g,e).

objetivo(user,user).

muda_estado(Agente,NovoEstado) :-
	retractall(estado(Agente,_)),
	assert(estado(Agente,NovoEstado)).

processa(Agente,satisfeito) :-
	\+ atacado(Agente,_), !.

processa(Agente,satisfeito) :-
	atacado(Agente,_), 
	muda_estado(Agente,busca_fuga).

processa(Agente,busca_fuga) :-
	retract(atacado(Agente,user)),fail.

processa(Agente,busca_fuga) :-
	atacado(Agente,Agressor),
	encontra_intrusos_fuga(Agente,Intrusos),
	objetivo(Agente,Objetivo),
	objetivo(Agressor,ObjetivoAgressor),
	ifthenelse(Intrusos \= [], 
		   atacar(Agente,Intrusos,Objetivo),
		   fugir(Agente,ObjetivoAgressor)),!.

processa(Agente,busca_fuga) :-
	\+ atacado(Agente,_),
	encontra_intrusos_fuga(Agente,Intrusos), Intrusos \= [],
	objetivo(Agente,Objetivo),
	atacar(Agente,Intrusos,Objetivo),!.

processa(Agente,busca_fuga) :-
	\+ atacado(Agente,_),
	encontra_intrusos_fuga(Agente,Intrusos), Intrusos = [],
	muda_estado(Agente,busca_satisfacao),!.

processa(_,busca_fuga).

processa(Agente,fuga) :-
	atacado(Agente,_),
	muda_estado(Agente, busca_fuga),!.

processa(Agente,fuga) :-
	\+ atacado(Agente,_),
	muda_estado(Agente, busca_satisfacao),!.

processa(Agente, busca_satisfacao) :-
	atacado(Agente,_),
	muda_estado(Agente, busca_fuga),!.

processa(Agente, busca_satisfacao) :-
	\+ atacado(Agente,_),
	encontra_intruso_satisfacao(Agente,Intrusos), Intrusos \= [],
	objetivo(Agente,Objetivo),
	atacar(Agente,Intrusos,Objetivo),!.

processa(Agente, busca_satisfacao) :-
	\+ atacado(Agente,_),
	encontra_intruso_satisfacao(Agente,Intrusos), Intrusos = [],
	satisfazer(Agente),
	muda_estado(Agente, satisfeito),!.

processa(_, busca_satisfacao).

encontra_intrusos_fuga(Agente,[X]) :-
	ambiente(Ambiente),
	member(sobre(X,Agente),Ambiente),
	member(livre(X),Ambiente),!.

encontra_intrusos_fuga(Agente,[X|Intrusos]) :-
	ambiente(Ambiente),
	member(sobre(X,Agente),Ambiente),
	\+ member(livre(X),Ambiente),
	encontra_intrusos_fuga(X,Intrusos),!.
encontra_intrusos_fuga(_,[]).

encontra_intruso_satisfacao(Agente,[X]) :-
	objetivo(Agente,Objetivo),
	ambiente(Ambiente),
	member(sobre(X,Objetivo),Ambiente),
	X \= Agente,!.
encontra_intruso_satisfacao(_,[]).

fugir(Agente,Restricao) :-
	ifthenelse(encontra_lugar_para_fugir(Agente,Restricao,Lugar),
		   (   move_agente(Agente,Lugar), muda_estado(Agente, fuga) ),
		   true).

move_agente(Agente,Destino) :-
%	mutex_lock(environment),
	\+ atacado(Agente,Destino),
	incrementa_cont,
	cont(X),
	format(user_output,'\n~w - Movendo Agente ~w para cima de ~w',[X,Agente,Destino]),
	ambiente(Ambiente),
	\+ member(sobre(Agente,Destino),Ambiente), %o agente já está sobre o destino
	member(livre(Destino),Ambiente),
	delete(Ambiente,livre(Destino),A1),
	delete(A1,sobre(Agente,_),A2),
	member(sobre(Agente,B),Ambiente),
	append(A2,[livre(B),sobre(Agente,Destino)],L),
	retractall(ambiente(_)),
	assert(ambiente(L)),
%	retract(atacado(Agente,_)),  % retira a restrição que provocou o movimento
	retractall(atacado(_,Agente)).
%	mutex_unlock(environment).  %(Agente,_)
move_agente(_,_).

/*
atacar(_,[],_).
atacar(Agente,[Intruso|_],_):-
	\+ atacado(Intruso,Agente),
	assert(atacado(Intruso,Agente)),
	format(user_output,'\nAgente ~w agredindo ~w',[Agente,Intruso]),!.
*/

atacar(_,[],_).
atacar(Agente,[Intruso|Intrusos],_):-
	\+ atacado(Intruso,Agente),
	assert(atacado(Intruso,Agente)),
	format(user_output,'\nAgente ~w agredindo ~w',[Agente,Intruso]),
	atacar(Agente,Intrusos,_),!.
atacar(Agente,[_|Intrusos],_) :-
	atacar(Agente,Intrusos,_).

encontra_lugar_para_fugir(Agente,Restricao,X) :-
	ambiente(Ambiente),
	member(livre(X),Ambiente),
	mesa(X),X \= Restricao,X \= Agente,
	\+ atacado(Agente,X),!.

encontra_lugar_para_fugir(Agente,Restricao,X) :-
	ambiente(Ambiente),
	member(livre(X),Ambiente),
	X \= Restricao,X \= Agente,
	\+ atacado(Agente,X),!.

satisfazer(Agente) :-
%	\+ estado(agente,satisfeito),
	objetivo(Agente,Objetivo),
	\+ atacado(Agente,Objetivo),
	move_agente(Agente,Objetivo),
	retractall(atacado(_,Agente)),  % colocado
	retractall(estado(Agente,_)),
	assert(estado(Agente,satisfeito)),
	format(user_output,'\nAgente [[~w]] satisfeito\n',[Agente]),!.

ifthenelse(Condicao,Then,_) :-
	Condicao,!,Then.
ifthenelse(_,_,Else) :-
	Else.

/********************************************************
	Manipulação de Threads 
**********************************************************/

stop_threads :-
	estado(Agent,_),
	thread_send_message(Agent,message(user,Agent,true)),
	thread_join(Agent,_),fail.

restart :-
	estado(Agent,_),
	thread_exit(Agent),
	thread_join(Agent,_), fail.

create_all_threads :-
	estado(Name,_),
	\+ mesa(Name),
	ifthenelse(current_thread(Name,_),(thread_signal(Name,thread_exit(_))),true),
	fail.
create_all_threads :-
	threads,
	estado(Name,_),
	\+ mesa(Name),
	thread_create(read_message(Name),_,[alias(Name)]),fail.
create_all_threads.

read_message(Agent) :-
	repeat,
	estado(Agent,Estado),
	processa(Agent,Estado),
	fail.

snips(X) :-
	X, !.

evaluate_message(M) :-
	M.

processa_agente(Agente) :-
	estado(Agente,Estado),
	processa(Agente,Estado),!.

run :-
	repeat,
	processa_agente(a),
	processa_agente(b),
	processa_agente(c),
	processa_agente(d),
	processa_agente(e),
	processa_agente(f),
	processa_agente(g),
	ifthenelse((estado(a,satisfeito),
		    estado(b,satisfeito),
		    estado(c,satisfeito),
		    estado(d,satisfeito),
		    estado(e,satisfeito),
		    estado(f,satisfeito),
		    estado(g,satisfeito)),true,fail).

/*
:- muda_estado(mesa1,satisfeito).
:- muda_estado(mesa2,satisfeito).
:- muda_estado(mesa3,satisfeito).
:- muda_estado(d,busca_fuga).
:- muda_estado(c,busca_fuga).
:- muda_estado(b,busca_fuga).
:- muda_estado(a,busca_fuga).
*/

/*
thread_send_message(a,message(user,a,fuga(a,user,[mesa1]))).

ambiente(X),thread_send_message(a,message(user,a,tenta_satisfazer(a,X))).

*/

configura :-
	retractall(atacado(_,_)),
	retractall(ambiente(_)),
	retractall(cont(_)), assert(cont(0)),
	muda_estado(mesa1,satisfeito),
	muda_estado(mesa2,satisfeito),
	muda_estado(mesa3,satisfeito),
	muda_estado(g,satisfeito),
	muda_estado(f,satisfeito),
	muda_estado(e,satisfeito),
	muda_estado(d,satisfeito),
	muda_estado(c,satisfeito),
	muda_estado(b,satisfeito),
	muda_estado(a,satisfeito),
	assert(ambiente([sobre(a,mesa1),sobre(b,a),sobre(c,b),sobre(d,c),sobre(e,d),sobre(f,e),sobre(g,f),livre(g),livre(mesa2),livre(mesa3)])).

/* Para rodar:
?- configura, assert(atacado(a,user)), run.
*/







