%--------------------------------- - - - - - - - - - -  -  -  -  -   -
% TRABALHO PLCR - MIEBIOM
% PARTE 1 - LOCALIZAÇĂO DE SERVIÇOS
%consult('/Users/anasofiaquintas/Desktop/PARTE[1]GRUPO[2]).

%--------------------------------- - - - - - - - - - -  -  -  -  -   -
%--------------------------------- - - - - - - - - - -  -  -  -  -   -
%BASES DE CONHECIMENTO
%--------------------------------- - - - - - - - - - -  -  -  -  -   -
% Base de Conhecimento: zonas do país: regiőes e cidades existentes.
% Factos

regiao( centro ).
regiao( norte ).

cidade( braga ).
cidade( porto ).
cidade( lisboa ).

%--------------------------------- - - - - - - - - - -  -  -  -  -   -
% Base de Conhecimento: serviços existentes

servico( farmacia ).
servico( hospital ).
servico( praia ).

%--------------------------------- - - - - - - - - - -  -  -  -  -   -
% Base de conhecimento: cidades que pertencem a determinada regiăo.

pertence(braga,norte).
pertence(porto,norte).
pertence(lisboa,centro).

%--------------------------------- - - - - - - - - - -  -  -  -  -   -
%ponto_descric‹o(Tipo_ponto, descriŤcao,Nome, Cidade, CoordenadaA, CoordenadaB).
ponto_descricao(servico, praia, praia_mira_sol, braga, 3,3).
ponto_descricao(servico, farmacia, farmacia_natercia, braga, 10,1).
ponto_descricao(servico, hospital, hospital_braga, braga, 8,7).
ponto_descricao(cidade, centro, centro_braga, braga, 13, 5).

ponto_descricao(cidade, centro, centro_porto, porto, 5,9).
ponto_descricao(servico, praia, praia_esposende, porto, 15,15).
ponto_descricao(servico, farmacia, farmacia_gago, porto, 8,13).
ponto_descricao(servico, hospital, hospital_porto, porto, 7,11).
ponto_descricao(servico, farmacia, farmacia_fernandes, porto, 2, 14).

ponto_descricao(cidade, centro, centro_lisboa, lisboa, 5, 18).
ponto_descricao(servico, farmacia, farmacia_antunes,lisboa, 3, 22).
ponto_descricao(servico, hospital, hospital_lisboa, lisboa, 11, 21).
ponto_descricao(servico, farmacia, farmacia_dias, lisboa, 14, 17).




%--------------------------------- - - - - - - - - - -  -  -  -  -   -
% Base de conhecimento: tipo_arco( PO,PD,TE,NE,Vel )
% Inclui descriçăo completa das carateristicas de cada arco
% PO - Ponto de Origem
% PD - Ponto de Destino
% TE - Tipo de Estrada
% Vel - Velocidade



arco(farmacia_antunes, hospital_lisboa,nacional, n1 ,80).
arco(hospital_lisboa, centro_lisboa, nacional, n2 ,80).
arco(farmacia_antunes, centro_lisboa,rupestre, r1 ,50).
arco(hospital_lisboa,farmacia_dias, rupestre, r2 ,50).
arco(farmacia_dias, praia_esposende,rupestre, r3 ,50).
arco(farmacia_antunes, farmacia_fernandes, auto_estrada, a2 ,120).
arco(centro_lisboa, hospital_porto, auto_estrada, a3 ,120).
arco(hospital_lisboa, farmacia_gago, auto_estrada, a4 ,120).
arco(farmacia_gago, hospital_porto, rupestre, r4, 50).
arco(farmacia_fernandes, hospital_porto, nacional,n3, 80).
arco(praia_esposende, hospital_braga,auto_estrada, a5,120).
arco(praia_esposende, centro_braga, auto_estrada, a6,120).
arco(hospital_porto, hospital_braga, nacional, n4, 80).
arco(hospital_porto, centro_porto, rupestre, r5, 50).
arco(farmacia_fernandes, praia_mira_sol, auto_estrada, a7, 120).
arco(centro_porto, praia_mira_sol, nacional, n5, 80).
arco(hospital_braga,  praia_mira_sol, rupestre, r6, 50).
arco(hospital_braga,  farmacia_natercia, nacional, n6, 80).
arco(praia_mira_sol, farmacia_natercia, nacional, n7, 80).
arco(farmacia_natercia, centro_braga, rupestre, r7,50).


%--------------------------------- - - - - - - - - - -  -  -  -  -   -
% Base de conhecimento: horários das farmácias
% horario_farmacia: Tempo, Nome_farmacia, Estado

horario_farmacia(H:M, farmacia_antunes,fechado):- H>=20, H <24, M>=00, M <60.
horario_farmacia(H:M, farmacia_antunes, aberto):- H>=9, H <20, M>=00, M <60.
horario_farmacia(H:M, farmacia_antunes, invalido):- H>=24.
horario_farmacia(H:M, farmacia_antunes, invalido):- M >=60.

horario_farmacia(H:M, farmacia_dias, aberto):- H>00, H <24, M>=00, M <60.
horario_farmacia(H:M, farmacia_dias, invalido):- H>=24.
horario_farmacia(H:M, farmacia_dias, invalido):- M >=60.

horario_farmacia(H:M, farmacia_gago,fechado):- H>=20, H <24, M>=00, M <60.
horario_farmacia(H:M, farmacia_gago, aberto):- H>=9, H <20, M>=00, M <60.
horario_farmacia(H:M, farmacia_gago, invalido):- H>=24.
horario_farmacia(H:M, farmacia_gago, invalido):- M >=60.

horario_farmacia(H:M, farmacia_fernandes, aberto):- H>00, H <24, M>=00, M <60.
horario_farmacia(H:M, farmacia_fernandes, invalido):- H>=24.
horario_farmacia(H:M, farmacia_fernandes, invalido):- M >=60.

horario_farmacia(H:M, farmacia_natercia,fechado):- H>=20, H <24, M>=00, M <60.
horario_farmacia(H:M,farmacia_natercia, aberto):- H>=9, H <20, M>=00, M <60.
horario_farmacia(H:M, farmacia_natercia, invalido):- H>=24.
horario_farmacia(H:M, farmacia_natercia, invalido):- M >=60.



%--------------------------------- - - - - - - - - - -  -  -  -  -   -
%--------------------------------- - - - - - - - - - -  -  -  -  -   -
%PREDICADOS
%--------------------------------- - - - - - - - - - -  -  -  -  -   -
%--------------------------------- - - - - - - - - - -  -  -  -  -   -
%Legenda
%O - Ponto de origem
%D - Ponto de destino
%L - Local
%D - Destino
%C - Cidade
%R - Regiao
%S - Servico
%A - Variável auxiliar A
%B - Variável auxiliar B
%Lista - Lista
%H - Cabeça de uma lista
%T - Cauda de uma lista
%T1 - Cauda auxiliar 1
%T2 - Cauda auxiliar 2
%LA1 - Lista auxiliar 1
%LA2 - Lista auxiliar 2
%LS - Lista de servicos
%LC - Lista de cidades
%E - Estado de uma farmácia: aberto/fechado
%NF - Nome de uma farmácia
%LF - Lista de farmácias
%Cam - Caminho
%Dis - Distância (km)
%X - Valor das abcissas de uma coordenada
%Y - Valor das ordenadas de uma coordenada
%C - Custo (€)
%Tempo - Tempo (min)

%--------------------------------- - - - - - - - - - -  -  -  -  -   -
% Extensăo do predicado local
% Local: A -> {V,F}


local( A ) :-
	cidade( A ).

local( A ) :-
	regiao( A ).


%--------------------------------- - - - - - - - - - -  -  -  -  -   -
% Extensăo do predicado que associa um determinado local como pertencente a uma regiăo.
% pertence: L,Lista -> {V,F}


pertence(A,[A|T]).

pertence(A,[H|T]) :-
	A \= H,
	pertence(A,T).

%--------------------------------- - - - - - - - - - -  -  -  -  -   -
% Extensăo do predicado nao_pertence
% nao_pertence: S,L -> {V,F}

nao_pertence( L,[] ).
nao_pertence( L,[H|T] ) :-
	L \= H,
	nao_pertence( L,T ).


%--------------------------------- - - - - - - - - - -  -  -  -  -   -
% Extensăo do predicado para determinar se dada regiăo tem determinado serviço
% tem_servico: R,S -> {V,F}

tem_servico( R,S ) :-
	regiao( R ),
    pertence( C,R ),
	ponto_descricao(servico,S,_,C,_,_).

tem_servico(C,S):-
    ponto_descricao(servico,S,_,C,_,_).


%--------------------------------- - - - - - - - - - -  -  -  -  -   -
% Extensăo do predicado dos serviços que existem num dado local
% servicos_num_local: L, LS -> {V,F}

servicos_num_local( L,LS ) :-
	findall(S,tem_servico( L,S ),Lista),
	elimina_duplicados( Lista,LS ).

% --------------------------------- - - - - - - - - - -  -  -  -  -   -
% Predicado auxiliar: elimina_duplicados
% elimina_duplicados: recebe uma lista e devolve outra sem elementos repetidos

elimina_duplicados([], []).
elimina_duplicados([H|T],Lista) :-
pertence(H,T),
elimina_duplicados(T,Lista).

elimina_duplicados([H|T], [H|T1]) :-
nao_pertence(H,T),
elimina_duplicados(T,T1).



% --------------------------------- - - - - - - - - - -  -  -  -  -   -
% Extensăo do predicado que devolve uma lista de cidades que tem determinado serviço
% cidades_com_servico: S,C -> {V,F}
% S - serviço; LC - cidade

cidades_com_servico( S,LC ) :-
	findall( C,(

		cidade( C ),tem_servico( C,S )),Lista ),

	elimina_duplicados( Lista,LC ).


% --------------------------------- - - - - - - - - - -  -  -  -  -   -
% Extensăo do predicado dos serviços que năo existem num dado local: Local,Lista -> {V,F}
% 1ş Lista de todos os serviços existentes, independentemente do local
% 2ş Lista dos serviços que existem num determinado local
% 3ş Lista de serviços que năo existem num local 

servicos_que_nao_existem( L,LS ) :-
	findall( S,servico( S ),LA1 ),
	servicos_num_local( L,LA2 ),
	elim12( LA2,LA1,LS ).

% --------------------------------- - - - - - - - - - -  -  -  -  -   -
% PREDICADO AUXILIAR elim12

% Elimina todos os elementos da lista 2 que estăo na lista 1
% elim12: L1,L2,L3 -> {V,F}

elim12( [],Lista,Lista ) :-!.

elim12( [H|T],LA1,Lista ) :-
	elimina_todos_niveis( H,LA1,LA2 ),
	elim12( T,LA2,Lista ).

% --------------------------------- - - - - - - - - - -  -  -  -  -   -
% PREDICADO AUXILIAR elimina_todos_niveis
% Elimina o elemento A de uma lista a todos os nível
% Elimina de listas que contenha sublistas
% elimina_todos_niveis: X, Lista, Lista de Resultado -> {V,F}

elimina_todos_niveis( A,[],[] ):-!.
elimina_todos_niveis( A,[A],[] ):-!.

elimina_todos_niveis( A,[A|T],Lista ) :-
	elimina_todos_niveis( A,T,Lista ),!.

elimina_todos_niveis( A,[H|T],Lista ) :-
	verifica_lista( H ),
	elimina_todos_niveis( A,H,LA1 ),
	elimina_todos_niveis( A,T,LA2 ),
	concatenar( [LA1],LA2,Lista ),!.

elimina_todos_niveis( A,[H|T],Lista ):-
	elimina_todos_niveis( A,T,LA1 ),
	concatenar( [H],LA1,Lista ).

% --------------------------------- - - - - - - - - - -  -  -  -  -   -
% PREDICADO AUXILIAR verifica_lista
% Determina se o que recebe é uma lista
% verifica_lista: L -> {V,F}


verifica_lista( [] ) :- !.
verifica_lista( [H|T] ) :-
	verifica_lista( T ).

% --------------------------------- - - - - - - - - - -  -  -  -  -   -
% PREDICADO AUXILIAR concatenar
% Extensăo do predicado «concatenar», que é a concatenaçăo dos elementos da lista L1 com os da L2
% concatenar: Lista1,Lista2,Resultado -> {V,F}

concatenar( [],Lista,Lista ) :- !.

concatenar( [H|T],LA1,[H|T1] ) :-
	concatenar( T,LA1,T1 ).

% --------------------------------- - - - - - - - - - -  -  -  -  -   -
% --------------------------------- - - - - - - - - - -  -  -  -  -   -
% Predicado farmacia det. qual a farmacia aberta a det hora numa cidade
% Predicado lista_farmacias det a lista de farmacias num det. estado para
% dada cidade
% farmacia: H:M,Cidade,Estado,Nome -> {V,F}
% lista_farmacias: H:M,Cidade,Estado,LF -> {V,F}
% LF -> lista de farmácias

farmacia( H:M,C,E,NF ) :-
    ponto_descricao(servico, farmacia, NF, C,_,_),
	horario_farmacia( H:M,NF,E ).


lista_farmacias( H:M,C,E,LF ) :-
	findall( NF,farmacia( H:M,C,E,NF ),LF ).


%--------------------------------- - - - - - - - - - -  -  -  -  -   -
% FAZER CAMINHO EM AMBOS OS SENTIDOS NECESSITA DE VARIOS PREDICADOS AUX
%--------------------------------- - - - - - - - - - -  -  -  -  -   -
%Predicado arco 2, permite ir de A para B mas também de B para A
%(bidireccionais)
%arco2: A,B,_,TE,_ -> {V,F}


arco2( A,B,TE,NE,Vel ) :-
	arco( A,B,TE,NE,Vel );
	arco( B,A,TE,NE,Vel ).



%--------------------------------- - - - - - - - - - -  -  -  -  -   -
%Evitar ciclos
%O - Origem; D -Destino; Lista de estradas por onde se tem que passar
%Vis - Lista de nodos/estrada já visitados (a năo percorrer novamente)
%Se a origem e o destino forem dois pontos consecutivos:

evita_ciclos( O,D,[NE],_ ) :-
	arco2( O,D,_,NE,_ ).

%Se năo:

evita_ciclos( O,D,[H|T],Vis ) :-
	arco2( O,A,_,H,_ ),
	nao_pertence( A,Vis ),
	evita_ciclos( A,D,T,[A|Vis]),
	A \= D.

%--------------------------------- - - - - - - - - - -  -  -  -  -   -
%Caminho - caminho nos 2 sentidos
%Caminho: O,D,Lista de Cidades em q se passa sem repetir nodos,

caminho( O,D,Cam ) :- evita_ciclos( O,D,Cam,[]).

%--------------------------------- - - - - - - - - - -  -  -  -  -   -
%VER TODOS OS CAMINHOS POSSÍVEIS DE UMA SÓ VEZ (NOS 2 SENTIDOS)

todos_caminhos(O,D,Lista) :-
	findall(LA1,caminho(O,D,LA1),Lista).

%--------------------------------- - - - - - - - - - -  -  -  -  -   -
% Predicado seq_servicos_caminho: LS, C -> {V,F}
% LS - Lista de Serviços
% C- Caminho

% O caminho para percorrer um só serviço é uma lista vazia
% O caminho para percorrer 2 ou mais serviços consiste na determinaçăo
% do caminho entre os 2 primeiros serviços na lista
% posterior eliminaçăo do 1ş serviço da lista
% determinaçăo do caminho entre os dois primeiros elementos da lista resultante


seq_servicos_caminho([H|[]],[]).

seq_servicos_caminho( [H|[H1|T1]],[H2|T2] ) :-
	caminho( H,H1,H2 ),
	apagarPri( [H|[H1|T1]],LA ),
	seq_servicos_caminho( LA,T2 ).
	
%--------------------------------- - - - - - - - - - -  -  -  -  -   -
% Predicado auxiliar apagarPri: apagar o primeiro elemento de uma lista
% apagarPri: L1, L2 -> {V,F}

apagarPri([],[]).

apagarPri([H|T],T).

 
%----------------DISTANCIA-------------------------

%Esta funçăo utiliza o teorema de pitágoras. Assim, dadas as coordenadas entre dois pontos,
%será "construido" um triangulo cujos catetos săo respetivamente, a diferença entre as abcissas e 
%as ordenadas dos dois pontos, calculando-se, de seguida, a hipotenusa do mesmo que corresponde ŕ distancia entre os dois pontos.

distancia2pontosconsecutivos( A,B,Cam,Dis ) :-
	caminho( A,B,Cam ),
	ponto_descricao(_,_,A,_,XA,YA),
	ponto_descricao(_,_,B,_,XB,YB),
	Dis is sqrt((XB - XA) **2 + (YB - YA) **2),!.

%--------------------------------- - - - - - - - - - -  -  -  -  -   -
%DISTANCIA DE UM CAMINHO

distanciaCaminho( A,A,[],0 ).

distanciaCaminho( O,D,[Cam1|Cam2],Dis ) :-
	distancia2pontosconsecutivos( O,A,Cam1,Dis1),
	distanciaCaminho( A,D,Cam2,Dis2 ),
	Dis is Dis1 + Dis2.

	
% O predicado distanciaSeqPontos, como o nome indica, retribui-nos a distancia total a percorrer, dada uma determinada sequencia de pontos.
% Caso se apresente, uma lista vazia, a distancia percorrida será, obviamente, 0 km. Neste caso esta é a primeira condiçăo do predicado em questao.
% Caso contrário, dada uma lista de pontos a funçăo distanciaSeqPontos calculará arco por arco a distancia percorrida, usando a regra da recursividade.
% Se nao existir arco entre elementos consecutivos da lista será impossivel calcular a distância. A distancia total percorrida é, entăo, dada pela soma entre
% a distancia percorrida em cada arco.

%-----------CUSTO-------------------
%Dois pontos interessantes de analisar, quando falamos em localizaçăo de serviços, săo sem duvida o custo e o tempo associados ao caminho percorrido.
%Deste modo construiram-se os predicados custoCaminho e tempoCaminho.


% distancia*custokm= custo_arco
% custo_km=(1,3€/l)*(7l/100km)=0,091€/km


custoCaminho( A,A,[],0 ).
custoCaminho( O,D,Cam,Cus ) :-
	distanciaCaminho( O,D,Cam,Dis ),
	Cus is Dis * 0.091.


	
% A funçao custoCaminho devolve o custo que tem percorrer determinado caminho, sendo este dado pelo utilizador como uma sequęncia de pontos.
% Para desenvolver esta funçăo considerou-se um preço médio do gasóleo de 1,3€/l e um consumo médio de gasóleo de 7l/100km, dando um custo médio de 0,091€/km.
	

%--------------TEMPO------------------
% distancia/velocidade= tempo_arco

% Esta funçăo devolve-nos o tempo que demora a atravessar um arco em minutos.

tempoCaminho(O,O,[],0).
tempoCaminho(O,D,[Cam1|Cam2],Tempo):-
	tempo2pontosconsecutivos(O,A,Cam1,Tem1),
	tempoCaminho(A,D,Cam2,Tem2),
	Tempo is Tem1 + Tem2.


tempo2pontosconsecutivos(A,B,Cam,Tempo) :-
	arco2( A,B,_,_,Vel),
	distancia2pontosconsecutivos( A,B,Cam,Dis ),
	Tempo is (Dis/Vel) * 60,!.


% Para a funçao tempoCaminho utilizou-se novamente a regra da recursividade.
% Posto isto, o tempo de um caminho é calculado arco a arco, sendo que, primeiramente a funçăo verifica que tipo de arco está a ser utilizado para a travessia
%e a sua respetiva velocidade equivalente. Da soma dos tempos de travessia de cada arco resulta o tempo total que se demorou a percorrer a sequencia de pontos dada pelo utilizador.

%--------------------------------- - - - - - - - - - -  -  -  -  -   -
% Predicado min - devolve o menor valor de uma lista

% Calcula o minimo de uma lista

min([A],A).
min([H|T], H) :- min(T,B), H =< B.
min([H|T], B) :- min(T,B), H > B.

%--------------------------------- - - - - - - - - - -  -  -  -  -   -
%  Predicado Caminho + curto

caminho_mais_curto2( O,D,Cam,Dis ) :- distanciaCaminho(O,D,Cam,Dis),!.	


%caminho_mais_curto( A,A,[],0 ).

%caminho_mais_curto( O,D,Cam,Dis ) :-
%	findall( (B,A),distanciaCaminho( O,D,B,A ),LA1 ),
%	min( LA1,Dis ),
%	distanciaCaminho( O,D,Cam,Dis ).


%--------------------------------- - - - - - - - - - -  -  -  -  -   -
% Predicado Caminho + barato
	

caminho_mais_barato2( O,D,Cam,Cus ) :- custoCaminho(O,D,Cam,Cus),!.

%caminho_mais_barato( A,A,[],0 ).

%caminho_mais_barato( O,D,Cam,Cus ) :-
%	findall( A,custoCaminho( O,D,B,A ),LA1 ),
%	min( LA1,Cus ),
%	custoCaminho( O,D,Cam,Cus ).


%--------------------------------- - - - - - - - - - -  -  -  -  -   -
% Predicado Caminho + rápido

caminho_mais_rapido2( O,D,Cam,Tem ) :- tempoCaminho(O,D,Cam,Tem),!.

%caminho_mais_rapido( A,A,[],0 ).

%caminho_mais_rapido( O,D,Cam,Tem ) :-
%	findall( A,tempoCaminho( O,D,B,A ),LA1 ),
%	min( LA1,Tem),
%	tempoCaminho( O,D,Cam,Tem).


%--------------------------------- 
% Predicado năo_passa_numa_estrada

%O : Origem;
%D :Destino;
%E : Tipo de estrada;
%P : Lista dos percursos obtidos.

nao_passa_numa_estrada(O, D , E, P):-
    findall(X, nao_passa(O, D , E, X   ), K ),
    elimina_todos_niveis([], K, P).


nao_passa(O, D , E, X   ):-
    caminho(O,D,CAM ),
    findall(NE, arco2(_,_,E ,NE  ,_ ), Lista),
    pertenceListaRetornaVazia(CAM, Lista, X).


pertenceListaRetornaVazia(A, B , []):- intersec_conj(A,B, [L]),  !.
pertenceListaRetornaVazia(A,B,A ).

intersec_conj([],_,[]).

intersec_conj([Cabeca|Cauda],L, [Cabeca|U ]):-
    pertence(Cabeca, L ), !, intersec_conj(Cauda, L, U ).

intersec_conj([_|Cauda], L , U ):- intersec_conj(Cauda, L, U ).