academic(project).
academic(homework).
work(tutor).
work(ta).
eca(club).

dueMonth(1).
dueMonth(2).
dueMonth(3).
dueMonth(4).
dueMonth(5).
dueMonth(6).
dueMonth(7).
dueMonth(8).
dueMonth(9).
dueMonth(10).
dueMonth(11).
dueMonth(12).

dueDay(1).
dueDay(2).
dueDay(3).
dueDay(4).
dueDay(5).
dueDay(6).
dueDay(7).
dueDay(8).
dueDay(9).
dueDay(10).
dueDay(11).
dueDay(12).
dueDay(13).
dueDay(14).
dueDay(15).
dueDay(16).
dueDay(17).
dueDay(18).
dueDay(19).
dueDay(20).
dueDay(21).
dueDay(22).
dueDay(23).
dueDay(24).
dueDay(25).
dueDay(26).
dueDay(27).
dueDay(28).
dueDay(29).
dueDay(30).
dueDay(31).

parse_due(Type, String, [due, DueMonth, (/), DueDay, Difficulty], Task) :- 
    dueMonth(DueMonth), dueDay(DueDay), 
    Task = [Type, String, [DueMonth, DueDay], Difficulty].
parse_due(Type, String, [on, DueMonth, (/), DueDay, Difficulty], Task) :- 
    dueMonth(DueMonth), dueDay(DueDay), 
    Task = [Type, String, [DueMonth, DueDay], Difficulty].
parse_due(Type, String, [First, due, DueMonth, (/), DueDay, Difficulty], Task) :- 
    string_concat(String, First, Name), 
    dueMonth(DueMonth), dueDay(DueDay), 
    Task = [Type, Name, [DueMonth, DueDay], Difficulty].
parse_due(Type, String, [First, on, DueMonth, (/), DueDay, Difficulty], Task) :- 
    string_concat(String, First, Name), 
    dueMonth(DueMonth), dueDay(DueDay), 
    Task = [Type, Name, [DueMonth, DueDay], Difficulty].
parse_due(Type, String, [First|Rest], Task) :- 
    string_concat(String, First, Temp), 
    string_concat(Temp, " ", NextString),
    parse_due(Type, NextString, Rest, Task).
parse_task([First|Rest], Task) :- string_concat(First, " ", String), 
    parse_task(String, Rest, Task).
parse_task([First|Rest], Task) :-
    downcase_atom(First, LowerFirst), academic(LowerFirst), 
    string_concat(First, " ", NextString), 
    parse_due(academic, NextString, Rest, Task).
parse_task([First|Rest], Task) :- 
    downcase_atom(First, LowerFirst), work(LowerFirst),  
    string_concat(First, " ", NextString), 
    parse_due(academic, NextString, Rest, Task).
parse_task([First|Rest], Task) :- 
    downcase_atom(First, LowerFirst), eca(LowerFirst),  
    string_concat(First, " ", NextString), 
    parse_due(academic, NextString, Rest, Task).
parse_task(String, [First|Rest], Task) :-
    downcase_atom(First, LowerFirst), academic(LowerFirst), 
    string_concat(String, First, Temp), 
    string_concat(Temp, " ", NextString), 
    parse_due(academic, NextString, Rest, Task).
parse_task(String, [First|Rest], Task) :- 
    downcase_atom(First, LowerFirst), work(LowerFirst),  
    string_concat(String, First, Temp), 
    string_concat(Temp, " ", NextString), 
    parse_due(work, NextString, Rest, Task).
parse_task(String, [First|Rest], Task) :- 
    downcase_atom(First, LowerFirst), eca(LowerFirst),  
    string_concat(String, First, Temp), 
    string_concat(Temp, " ", NextString), 
    parse_due(eca, NextString, Rest, Task).
parse_task(String, [First|Rest], Task) :- string_concat(String, First, Temp), 
    string_concat(Temp, " ", NextString), 
    parse_task(NextString, Rest, Task).

indexList([_], [1]).
indexList([Element|Rest], Indices) :- indexList(Rest, RestIndices), 
    length([Element|Rest], Length),
    append(RestIndices, [Length], Indices).

display_tasks([Index], [[_, Name, [DueMonth, DueDay], Diff]]) :- write(Index), write(". "), 
    write(Name), write(" due:"), write(DueMonth), write("/"), write(DueDay), 
    write(" difficulty:"), write(Diff), nl.
display_tasks([Index|RestIndices], [[_, Name, [DueMonth, DueDay], Diff]|Rest]) :- write(Index), write(". "), 
    write(Name), write(" due:"), write(DueMonth), write("/"), write(DueDay), 
    write(" difficulty:"), write(Diff), nl, display_tasks(RestIndices, Rest).

show_current_state([]) :- write("There is no task currently."), nl.
show_current_state(Tasks) :- write("Here is the list of tasks, ordered in terms of their priority levels:"), 
    nl, indexList(Tasks, Indices), display_tasks(Indices, Tasks).

add_task(Task, Tasks, Tasks) :- member(Task, Tasks), 
    write("This task exists in the list already."), nl.
add_task(Task, [], [Task]).
add_task(Task, Tasks, NewTasks) :- prioritize(Task, Tasks, NewTasks).

dueSooner([DueMonth1, _], [DueMonth2, _]) :- 
    DueMonth1 < DueMonth2.
dueSooner([DueMonth, DueDay1], [DueMonth, DueDay2]) :- 
    DueDay1 < DueDay2.

prioritize([Type1, Name1, Due1, Diff1], [[Type2, Name2, Due2, Diff2]], NewTasks) :- 
    (Type1 = academic, Type2 = work ;
     Type1 = academic, Type2 = eca ;
     Type1 = work, Type2 = eca), 
    NewTasks = [[Type1, Name1, Due1, Diff1], [Type2, Name2, Due2, Diff2]].
prioritize([Type1, Name1, Due1, Diff1], [[Type2, Name2, Due2, Diff2]], NewTasks) :- 
    (Type2 = academic, Type1 = work ;
     Type2 = academic, Type1 = eca ;
     Type2 = work, Type1 = eca), 
    NewTasks = [[Type2, Name2, Due2, Diff2], [Type1, Name1, Due1, Diff1]].
prioritize([Type1, Name1, Due1, Diff1], [[Type2, Name2, Due2, Diff2]], NewTasks) :- 
    Type1 = Type2, Due1 = Due2, 
    (Diff1 = easy, Diff2 = normal ;
     Diff1 = normal, Diff2 = hard ;
     Diff1 = easy, Diff2 = hard), 
    NewTasks = [[Type1, Name1, Due1, Diff1], [Type2, Name2, Due2, Diff2]].
prioritize([Type1, Name1, Due1, Diff1], [[Type2, Name2, Due2, Diff2]], NewTasks) :- 
    Type1 = Type2, Due1 = Due2, 
    (Diff2 = easy, Diff1 = normal ;
     Diff2 = normal, Diff1 = hard ;
     Diff2 = easy, Diff1 = hard), 
    NewTasks = [[Type2, Name2, Due2, Diff2], [Type1, Name1, Due1, Diff1]].
prioritize([Type1, Name1, Due1, Diff1], [[Type2, Name2, Due2, Diff2]|Rest], NewTasks) :- 
    (Type1 = academic, Type2 = work ;
     Type1 = academic, Type2 = eca ;
     Type1 = work, Type2 = eca), 
    NewTasks = [[Type1, Name1, Due1, Diff1], [Type2, Name2, Due2, Diff2]|Rest].
prioritize([Type1, Name1, Due1, Diff1], [[Type2, Name2, Due2, Diff2]|Rest], NewTasks) :- 
    (Type2 = academic, Type1 = work ;
     Type2 = academic, Type1 = eca ;
     Type2 = work, Type1 = eca), 
    prioritize([Type1, Name1, Due1, Diff1], Rest, PartNewTasks),
    NewTasks = [[Type2, Name2, Due2, Diff2]|PartNewTasks].
prioritize([Type1, Name1, Due1, Diff1], [[Type2, Name2, Due2, Diff2]|Rest], NewTasks) :- 
    Type1 = Type2, dueSooner(Due1, Due2), 
    NewTasks = [[Type1, Name1, Due1, Diff1], [Type2, Name2, Due2, Diff2]|Rest].
prioritize([Type1, Name1, Due1, Diff1], [[Type2, Name2, Due2, Diff2]|Rest], NewTasks) :- 
    Type1 = Type2, dueSooner(Due2, Due1), 
    prioritize([Type1, Name1, Due1, Diff1], Rest, PartNewTasks),
    NewTasks = [[Type2, Name2, Due2, Diff2]|PartNewTasks].
prioritize([Type1, Name1, Due1, Diff1], [[Type2, Name2, Due2, Diff2]|Rest], NewTasks) :- 
    Type1 = Type2, Due1 = Due2, 
    (Diff1 = easy, Diff2 = normal ;
     Diff1 = normal, Diff2 = hard ;
     Diff1 = easy, Diff2 = hard),
    NewTasks = [[Type1, Name1, Due1, Diff1], [Type2, Name2, Due2, Diff2]|Rest].
prioritize([Type1, Name1, Due1, Diff1], [[Type2, Name2, Due2, Diff2]|Rest], NewTasks) :- 
    Type1 = Type2, Due1 = Due2, 
    (Diff2 = easy, Diff1 = normal ;
     Diff2 = normal, Diff1 = hard ;
     Diff2 = easy, Diff1 = hard), 
    prioritize([Type1, Name1, Due1, Diff1], Rest, PartNewTasks),
    NewTasks = [[Type2, Name2, Due2, Diff2]|PartNewTasks].

find_task(1, [Task|_], Task).
find_task(Index, [_|Rest], Task) :- 
    Next is Index - 1, find_task(Next, Rest, Task).

remove_task([Task|Rest], Task, Rest).
remove_task([DiffTask|Rest], Task, TempTasks) :- 
    remove_task(Rest, Task, Temp), TempTasks = [DiffTask|Temp].

determine_type([1], academic).
determine_type([2], work).
determine_type([3], eca).

determine_due([Month, (/), Due], [Month, Due]).

determine_diff([1], easy).
determine_diff([2], normal).
determine_diff([3], hard).

modify([1], [_, Name, Due, Diff], [NewType, Name, Due, Diff]) :- 
    write("Type 1 for academic..."), nl, 
    write("Type 2 for work..."), nl, 
    write("Type 3 for eca..."), nl,
    read_word_list(List),
    determine_type(List, NewType).
modify([2], [Type, _, Due, Diff], [Type, String, Due, Diff]) :- 
    write("Type task new name..."), nl, 
    read_word_list(List), atomics_to_string(List, String).
modify([3], [Type, Name, _, Diff], [Type, Name, NewDue, Diff]) :- 
    write("Type task new due date (Month/Date)..."), nl,
    read_word_list(List),
    determine_due(List, NewDue).
modify([4], [Type, Name, Due, _], [Type, Name, Due, NewDiff]) :- 
    write("Type 1 for easy..."), nl, 
    write("Type 2 for normal..."), nl, 
    write("Type 3 for hard..."), nl,
    read_word_list(List), 
    determine_diff(List, NewDiff).

modify_task([Index], Tasks, NewTasks) :-
    find_task(Index, Tasks, Task),
    remove_task(Tasks, Task, TempTasks),
    write("Type 1 to modify task type..."), nl, 
    write("Type 2 to modify task name..."), nl, 
    write("Type 3 to modify task due date..."), nl,
    write("Type 4 to modify task difficulty..."), nl, 
    read_word_list(List),
    modify(List, Task, ModifiedTask),
    prioritize(ModifiedTask, TempTasks, NewTasks).

process_action([1], Tasks, NewTasks) :-
    write("Add a task..."), nl,
    read_word_list(List),
    parse_task(List, Task), 
    add_task(Task, Tasks, NewTasks).
process_action([2], Tasks, NewTasks) :-
    write("Type the index of the task you want to modify..."), nl,
    indexList(Tasks, Indices), display_tasks(Indices, Tasks),
    read_word_list(List),
    modify_task(List, Tasks, NewTasks).

prioritizer :- 
    Tasks = [],
    write("Task Prioritizer..."), nl,
    show_current_state(Tasks),
    write("Add a task..."), nl,
    read_word_list(List),
    (List = [exit] -> write("Bye!"), nl, !;
    (parse_task(List, Task),
    add_task(Task, Tasks, NewTasks),
    nl, prioritizer(NewTasks))), !.

prioritizer(Tasks) :- show_current_state(Tasks), nl, 
    write("Type 1 to add a task..."), nl,
    write("Type 2 to modify a task..."), nl,
    write("Type <exit> to end this program..."), nl,
    read_word_list(List),
    (List = [exit] -> write("Bye!"), nl, !;
    (process_action(List, Tasks, NewTasks), 
    nl, prioritizer(NewTasks))), !.

read_word_list(Ws) :-
    read_line_to_codes(user_input, Cs),
    atom_codes(A, Cs),
    tokenize_atom(A, Ws).