%Inicio do planExecute
planExecute(P) :- testPlan(P,E), writeExec(E).


testPlan(Plan,[I|Exec]) :-  
   inicial(I),                 % I = situação inicial  
   testPlan(Plan,I,Exec,Fn),    % Testa o plano linear Plan e executa-o a partir da                                   
                                % situação inicial I e devolve o resultado da execução em Exec
                                % A situação final da execução é devolvida em Fn.       
   satisfiedGoal(Fn).           % Verifica se os predicados do objectivo estão presentes em Fn.   
 
 testPlan([],Fn,[],Fn).
 testPlan([N|Lan], S,[N,S1|Exec],Fn):-  
	accao(nome: N, condicoes: Cn, efeitos: E, restricoes: R), 
	membro(Cn,S), 
	estadoseguinte(E,S,S1), 
	aplicarrestricoes(R), 
	testPlan(Lan,S1, Exec, Fn).
	

% TestPlan para o Plan()
testPlan(Plan,[I|Exec],NA) :-       % NA = número de acções  
   inicial(I),                      % I = situação inicial  
   testPlan(Plan,I,Exec,Fn, NA, [], LR),    % Testa o plano linear Plan e executa-o a partir da                                   
                                    % situação inicial I e devolve o resultado da execução em Exec
                                    % A situação final da execução é devolvida em Fn.       
   satisfiedGoal(Fn, LR).               % Verifica se os predicados do objectivo estão presentes em Fn.   
 
 testPlan([],Fn,[],Fn, 0, R, R).
 testPlan([N|Lan], S,[N,S1|Exec],Fn, NA, R0, R):- NA > 0,  
	accao(nome: N, condicoes: Cn, efeitos: E, restricoes: Re), 
	membro(Cn,S), 
	append(Re,R0,R1),
	aplicarrestricoes(R1,R2),
	estadoseguinte(E,S,S1), 
	S \== S1,
	NA1 is NA-1, 
	testPlan(Lan,S1, Exec, Fn, NA1,R2,R).	
	
	
% verifica se uma lista está contida dentro de outra	
membro([],_).
membro([E|L], S) :-  member(E,S), membro(L,S).

naomember(_,[]).
naomember(E,[X|L]):-E \== X, naomember(E,L).


% constrói o estado seguinte retirando os efeitos negativos e adicionando os positivos
estadoseguinte([],S1,S1).
estadoseguinte([-E|L1], S, S1) :- !, remover(E,S,S0), estadoseguinte(L1,S0,S1).
estadoseguinte([E|L], S, S1):- naomember(E,S),!, estadoseguinte(L,[E|S],S1).
estadoseguinte([_|L], S, S1):-  estadoseguinte(L,S,S1).


% remove um elemnto de uma lista
remover(_,[],[]).
remover(E,[X|S],S):- E == X, !. %nao pode unificar tem de ser mesmo igual
remover(E,[X|S],[X|S0]):- remover(E,S,S0).


% aplicar as restrições. ground verifica se as vars da retricao estao instanciadas
aplicarrestricoes([]).
aplicarrestricoes([R|L]) :- ground(R), R, aplicarrestricoes(L).

%aplicar as restições que sao possiveis de aplicar, quando nao dá guardam-se para mais tarde aplicar
aplicarrestricoes([],[]).
aplicarrestricoes([R|L],L1) :- ground(R),!, R, aplicarrestricoes(L,L1).
aplicarrestricoes([R|L],[R|L1]) :- aplicarrestricoes(L,L1).

% objectivos(L) esta a instanciar L com uma lista lida ou dada
satisfiedGoal(Fn):- objectivos(L), membro(L, Fn). 

% aplicar as restricoes no fim 
satisfiedGoal(Fn,R):- objectivos(L), membro(L, Fn), aplicarrestricoes(R,[]). 


%Faz a escrita dos resultados no ecran
writeExec([I|Exec]):- write('Initial Situation: '), write(I), nl, writeExec2(Exec).
writeExec2([]):- write('Goal : '),objectivos(I), write(I), write(' satisfied') ,nl.
writeExec2([N,I|Exec]):- write('Action performed: '), write(N),nl, write('Situation: '), write(I), nl, writeExec2(Exec).


planoN(Plan, NA):- testPlan(Plan,_, NA).
planoN(Plan, NA):- \+ testPlan(Plan,_, NA),NA1 is NA+1, planoN(Plan, NA1).

plano(Plan):- planoN(Plan, 0).

