%Visual:
%Blue face :- [rope, poison]
%Flames :- [matches]
%Convulsing :- [poison]
%Blood :- [gun, knife]
%Nothing :- [gun, knife, rope, poison, none]

%Audial:
%Gunshot :- [gun]
%Screaming :- [gun, knife, matches]
%Coughing :- [poison, matches]
%Banging :- [gun, knife, rope]
%Nothing :- [knife, rope, poison, matches, none]

% Weapons: [gun, knife, rope, poison, matches, none]
% Suspects: [john, craig, theresa, matthew, daniel, victim]

% All Weapons and their correlated symptoms
weapon(gun, Saw, Heard) :- 		member(Saw, [blood, nothing]), 					member(Heard, [gunshot, screaming, banging]).
weapon(knife, Saw, Heard) :- 	member(Saw, [blood, nothing]), 					member(Heard, [screaming, banging, nothing]).
weapon(rope, Saw, Heard) :- 	member(Saw, [blue_face, nothing]), 				member(Heard, [banging, nothing]).
weapon(poison, Saw, Heard) :- 	member(Saw, [blue_face, convulsing, nothing]), 	member(Heard, [coughing, nothing]).
weapon(matches, Saw, Heard) :- 	member(Saw, [flames]), 							member(Heard, [screaming, coughing, nothing]).
weapon(none, Saw, Heard) :- 	member(Saw, [nothing]), 						member(Heard, [nothing]).


% Relationship Info
married(craig, victim).
married(matthew, daniel).
have_relationship(theresa, victim).
have_relationship(craig, theresa).
have_relationship(john, victim).
have_relationship(daniel, victim).

% S has an affair on R with P if S is married to R, and S is in a relationship with P
affair(S, R, P) :- have_relationship(S, P), married(S, R), \+(R = P). 

% owes_money(S, P) = S owes P money
% S = P implies S is in debt/owes money to the bank
owes_money(matthew, victim).
owes_money(victim, john).
owes_money(victim, victim).
owes_money(daniel, victim).
owes_money(john, john).

% arsonist(S) = S is an arsonist
arsonist(daniel).
arsonist(craig).
arsonist(victim).

% threatened(S, P) = S has threatened P
threatened(theresa, victim).
threatened(victim, craig).
threatened(craig, victim).
threatened(daniel, john).
threatened(victim, john).

motive(adultery, Suspect) :- affair(Suspect, victim, _); affair(victim, Suspect, _).%married(Suspect, victim), (have_relationship(Suspect, !victim) | had_relationship(!Suspect, victim)
motive(money, Suspect) :- owes_money(Suspect, victim); owes_money(victim, Suspect).
motive(pyromania, Suspect) :- arsonist(Suspect). %added this so insanity is really a catch-all
motive(protection, Suspect) :- threatened(Suspect, victim); threatened(victim, Suspect).
motive(suicide, victim) :- owes_money(victim, victim); affair(_, victim, _). % figure this out later
motive(insanity, _).

read_word_list(Ws) :-
    read_line_to_codes(user_input, Cs),
    atom_codes(A, Cs),
    tokenize_atom(A, Ws).

% Grammar for input
%Saw, Heard, Weapon, Killer, Motive, Owner
%the odd names is to avoid posible overlap with rules in the future
sentence(s(Testimony)) --> open_parens, testi(Testimony), close_parens.
sentence(s(Bailif)) --> open_parens, bail(Bailif), close_parens.
sentence(s(Testimony)) --> open_parens, testi(Testimony), close_parens, dot.
sentence(s(Bailif)) --> open_parens, bail(Bailif), close_parens, dot.

testi(t(Saw, Heard, Weapon, Killer, Motive, Witness)) --> eyes(Saw), comma, ears(Heard), comma, weap(Weapon),
    comma, kill(Killer), comma, moti(Motive), comma, wit(Witness).

bail(b(Weapon, Motive)) --> weap(Weapon), comma, moti(Motive). 

eyes(v(blue_face)) --> [blue,face]. %this is how prolog will match to 'blue face' (w/o the ' marks)
eyes(v(flames)) --> [flames].
eyes(v(convulsing)) --> [convulsing].
eyes(v(blood)) --> [blood].
eyes(v(nothing)) --> [nothing].
eyes(v(nothing)) --> [n, (/), a]. %this looks odd but this is how prolog will match to 'n/a' (w/o the ' marks)

ears(a(gunshot)) --> [gunshot].
ears(a(screaming)) --> [screaming].
ears(a(coughing)) --> [coughing].
ears(a(banging)) --> [banging].
ears(a(nothing)) --> [nothing].
ears(a(nothing)) --> [n, (/), a].

weap(w(gun)) --> [gun].
weap(w(knife)) --> [knife].
weap(w(rope)) --> [rope].
weap(w(poison)) --> [poison].
weap(w(matches)) --> [matches].
weap(w(none)) --> [none].
weap(w(none)) --> [n, (/), a].

kill(k(john)) --> [john].
kill(k(craig)) --> [craig].
kill(k(theresa)) --> [theresa].
kill(k(matthew)) --> [matthew].
kill(k(daniel)) --> [daniel].
kill(k(victim)) --> [victim].

moti(m(adultery)) --> [adultery].
moti(m(money)) --> [money].
moti(m(protection)) --> [protection].
moti(m(pyromania)) --> [pyromania].
moti(m(suicide)) --> [suicide].
moti(m(insanity)) --> [insanity].
moti(m(insanity)) --> [n, (/), a].

wit(o(john)) --> [john].
wit(o(craig)) --> [craig].
wit(o(theresa)) --> [theresa].
wit(o(matthew)) --> [matthew].
wit(o(daniel)) --> [daniel].

open_parens --> ['('].
close_parens --> [')'].
dot --> ['.'].
comma --> [','].

output([Killer, Weapon, Motive]) :- write("I currently believe that "), write(Killer),
    write(" killed the victim with "), write_weapon(Weapon), write(" and that the motive was "),
    write(Motive), write("."), nl.

%I went over the top formatting this for fun
write_weapon(gun) :- write(a), write(" "), write(gun).
write_weapon(knife) :- write(a), write(" "), write(knife).
write_weapon(rope) :- write(rope).
write_weapon(matches) :- write(matches).
write_weapon(poison) :- write(poison).
write_weapon(none) :- write("... actually, I "), write(don), write("'"), write(t),
    write(" "), write(know), write(" what the murder weapon is").

start :- take_input([], [], 0).

take_input(Beliefs, Testimonies, 5) :- final_remarks(Beliefs, Testimonies), !.%final_remarks(), output, !. (basically)
take_input(Beliefs, Testimonies, Count) :-
    Count < 5,
    read_word_list(Input), 
    sentence(Parse, Input, []),
    process_testimony(Beliefs, Testimonies, Parse, NewTestimonies),
    revise_beliefs(Beliefs, NewTestimonies, NewBeliefs),
    recalculate_truth_values(NewTestimonies, NewBeliefs, FinalTestimonies),
    output(NewBeliefs),
    Count1 is Count +1,
    take_input(NewBeliefs, FinalTestimonies, Count1).

% Rough idea of how input processing can go
process_testimony(Beliefs, Testimonies, Parse, NewTestimonies) :-
    extract(Parse, Statement),
    Statement = (Saw, Heard, Weapon, Killer, Motive, Witness),
    ((      internal_contradiction(Statement, Testimonies), 
    		append(Testimonies, [(0, Saw, Heard, Weapon, Killer, Motive, Witness)], NewTestimonies));
     (		get_truth_value(Statement, Beliefs, TruthValue),
            append(Testimonies, [(TruthValue, Saw, Heard, Weapon, Killer, Motive, Witness)], NewTestimonies))).

extract(s(t(v(Saw), a(Heard), w(Weapon), k(Killer), m(Motive), o(Witness))), 
        (Saw, Heard, Weapon, Killer, Motive, Witness)).

extract(s(b(w(Weapon), m(Motive))), Weapon, Motive).

final_remarks([Killer, _, _], Testimonies) :- 
    read_word_list(Input),
    sentence(Parse, Input, []),
    extract(Parse, Weapon, Motive),
    TempBeliefs = [Killer, Weapon, Motive],
    recalculate_truth_values(Testimonies, TempBeliefs, FinalTestimonies),
    %this is being weird and not grabbing everything, even though it grabs everything in revise_beliefs
    findall((TruthValue, TKiller, Witness), 
        (member((TruthValue, _, _, _, TKiller, _, Witness), FinalTestimonies)),KL), 
    revise_killer(KL, FinalKiller),
    motive(Motive, FinalKiller),
    FinalBeliefs = [FinalKiller, Weapon, Motive],
    write("My final beliefs are as follows:"), nl,
    output(FinalBeliefs).


% There's an internal contradiction if...
internal_contradiction((Saw, Heard, Weapon, Killer, Motive, Witness), Testimonies) :- 
    (   \+ weapon(Weapon, Saw, Heard), !); % the weapon doesn't line up...
    (   \+ motive(Motive, Killer), !); % or the motive doesn't line up...
    (   member((_, _, _, _, _, _, Witness), Testimonies), !). % or they've already given testimony.

get_truth_value((_, _, Weapon, Killer, Motive, _), Beliefs, TruthValue) :-
    intersection([Killer, Weapon, Motive], Beliefs, Intersection),
    union([Killer, Weapon, Motive], Beliefs, Union),
    length(Intersection, ILength),
    length(Union, ULength),
    TruthValue is (ILength/ULength)+0.1.

recalculate_truth_values([], _, NewTestimonies) :- NewTestimonies = [].
recalculate_truth_values([H|T], Beliefs, NewTestimonies) :- 
    H = (OldTruthValue, Saw, Heard, Weapon, Killer, Motive, Witness),
    OldTruthValue \= 0,
    get_truth_value((Saw, Heard, Weapon, Killer, Motive, Witness), Beliefs, TruthValue),
    recalculate_truth_values(T, Beliefs, RestOfList),
    NewTestimonies = [(TruthValue, Saw, Heard, Weapon, Killer, Motive, Witness)|RestOfList].
recalculate_truth_values([H|T], Beliefs, NewTestimonies) :- 
    H = (0, _, _, _, _, _, _),
    recalculate_truth_values(T, Beliefs, RestOfList),
    NewTestimonies = [H|RestOfList].

revise_beliefs([], [(_, _, _, Weapon, Killer, Motive, _)], [Killer, Weapon, Motive]).

% revise the program's beliefs
revise_beliefs([_, _, _], Testimonies, [NewKiller, NewWeapon, NewMotive]) :-
    %get_killers_and_truth(KL, Testimonies),
    findall((TruthValue, Killer, Witness), 
        (member((TruthValue, _, _, _, Killer, _, Witness), Testimonies)),KL),
	findall((TruthValue, Weapon), 
        (member((TruthValue, _, _, Weapon, _, _, _), Testimonies), TruthValue \= 0), WL),
    findall((TruthValue, Motive), 
        (member((TruthValue, _, _, _, _, Motive, _), Testimonies), TruthValue \= 0), ML),
    sort(KL, KillerList),
    sort(WL, WeaponList),
    sort(ML, MotiveList),
    revise_killer(KillerList, NewKiller),
    revise_weapon(WeaponList, NewWeapon),
    revise_motive(MotiveList, NewMotive),
    motive(NewMotive, NewKiller).

revise_killer(List, NewKiller) :-
    Killers = [(L1, john),  (L2, craig), (L3, theresa), (L4, matthew), (L5, daniel), (L6, victim)],
    findall((Value1, john, Witness), 		member((Value1, john, Witness), List), 		JAccuse),
    findall((Value2, craig, Witness2), 		member((Value2, craig, Witness2), List), 	CAccuse),
    findall((Value3, theresa, Witness3),	member((Value3, theresa, Witness3), List), 	TAccuse),
    findall((Value4, matthew, Witness4), 	member((Value4, matthew, Witness4),List), 	MAccuse),
    findall((Value5, daniel, Witness5), 	member((Value5, daniel, Witness5), List), 	DAccuse),
    findall((Value6, victim, Witness6), 	member((Value6, victim, Witness6), List), 	VAccuse),
    sum(JAccuse, 0, S1),
    sum(CAccuse, 0, S2),
    sum(TAccuse, 0, S3),
    sum(MAccuse, 0, S4),
    sum(DAccuse, 0, S5),
    sum(VAccuse, 0, L6),
    modify_sum(S1, S2, S3, S4, S5, L1, L2, L3, L4, L5, List),
    sort(Killers, Sorted),
    reverse(Sorted, KL),
    get_killer(KL, NewKiller).

get_killer([(_, NewKiller)|_], NewKiller).
get_killer([_| R], NewKiller) :- get_killer(R, NewKiller).
get_killer([], _) :- false.
    

revise_motive(List, NewMotive) :-
    Motives = [(S1, adultery), (S2, money), (S3, pyromania), (S4, protection), (S5, suicide), (S6, insanity)],
    findall((Value1, adultery), 	member((Value1, adultery), List), 	JAccuse),
    findall((Value2, money), 		member((Value2, money), List), 		CAccuse),
    findall((Value3, pyromania), 	member((Value3, pyromania), List), 	TAccuse),
    findall((Value4, protection), 	member((Value4, protection), List), MAccuse),
    findall((Value5, suicide), 		member((Value5, suicide), List), 	DAccuse),
    findall((Value6, insanity), 	member((Value6, insanity), List), 	VAccuse),
    sum(JAccuse, 0, S1),
    sum(CAccuse, 0, S2),
    sum(TAccuse, 0, S3),
    sum(MAccuse, 0, S4),
    sum(DAccuse, 0, S5),
    sum(VAccuse, 0, S6),
    sort(Motives, Sorted),
    reverse(Sorted, [(_, NewMotive)|_]).

revise_weapon(List, NewWeapon) :-
    Weapons = [(S1, gun), (S2, knife), (S3, rope), (S4, poison), (S5, matches), (S6, none)],
    findall((Value1, gun), 		member((Value1, gun), List), 		JAccuse),
    findall((Value2, knife), 	member((Value2, knife), List), 		CAccuse),
    findall((Value3, rope), 	member((Value3, rope), List), 		TAccuse),
    findall((Value4, poison), 	member((Value4, poison), List), 	MAccuse),
    findall((Value5, matches), 	member((Value5, matches), List), 	DAccuse),
    findall((Value6, none), 	member((Value6, none), List), 		VAccuse),
    sum(JAccuse, 0, S1),
    sum(CAccuse, 0, S2),
    sum(TAccuse, 0, S3),
    sum(MAccuse, 0, S4),
    sum(DAccuse, 0, S5),
    sum(VAccuse, 0, S6),
    sort(Weapons, Sorted),
    reverse(Sorted, [(_, NewWeapon)|_]).

%basecase
sum([], Sum, Sum).
%sum for a 3-tuple
sum([(0,_,_)|Rest], CurSum, FinalSum) :-
    sum(Rest, CurSum, FinalSum).
sum([(Val,_,_)|Rest], CurSum, FinalSum) :-
    NewSum is CurSum + Val+1,
    sum(Rest, NewSum, FinalSum).
%sum for a 2-tuple
sum([(0,_)|Rest], CurSum, FinalSum) :-
    sum(Rest, CurSum, FinalSum).
sum([(Val,_)|Rest], CurSum, FinalSum) :- 
    NewSum is CurSum + Val+1,
    sum(Rest, NewSum, FinalSum).

% modify the sum of the each suspect's odds by 1.1 - their truth value
modify_sum(SJ, SC, ST, SM, SD, LJ, LC, LT, LM, LD, List) :-
    (   (   member((JTV, _, john), List), LJ is SJ + JTV); LJ = SJ),
    (   (   member((CTV, _, craig), List), LC is SC + CTV); LC = SC),
    (   (   member((TTV, _, theresa), List), LT is ST + TTV); LT = ST),
    (   (   member((MTV, _, matthew), List), LM is SM + MTV); LM = SM),
    (   (   member((DTV, _, daniel), List), LD is SD + DTV); LD = SD).

%program flow: 
% start
% take suspect testimony
% determine if testimony is self-contradictory. If so, throw it out
% if not, save it, then attempt to refine beliefs based on it
% once all suspects (aside from the victim) have given their testimony, the program states its final beliefs (count to 5)
% the bailiff comes in and presents the actual motive and murder weapon
% the program reprocesses with this final belief revision, then concludes who the killer is

% testimony that uses the current assumed murder weapon/cause makes the suspect more likely to be the killer and more credible