% ICD: it does not take into account missing values
%
% utility to translate from CSV style files to Prolog facts.
%
% assumes key is first argument.
%
% call as yap csv2pl -- prefix < Inp.csv > Out
%
% ICD: yap -l csv2pl_v3 -- --modes in.csv out 
% alternatively you can call yap and invoke: main(['--modes','in.csv',out]).
% this will generate three files: out.facts, out.modes and out.txt
% out.facts contains all prolog facts for the csv table
% out.modes contains all modes to run with aleph (change as appropriate)
% out.txt contains basic data description (counters)

:- source.

:- style_check(all).

:- yap_flag(unknown, error).

:- yap_flag(write_strings, on).

:- use_module(library(readutil),
	      [read_line_to_codes/2]).

:- use_module(library(lineutils),
	      [split/3]).

:- use_module(library(system),
	      [mktime/2]).

:- ensure_loaded(order_by). % order predicate values

:- ensure_loaded(daynumber).

:- initialization(main).

:- dynamic output_modes/0.

main :-
	unix(argv(Args)),
%        write(Args),
	main(Args).

main(['--modes'|R]) :- !,
	assert(output_modes),
	main(R).
%ICD: changed here to write in two different files
% also added counters
main([F,O]) :-
	open(F, read, S),
        atom_concat(O,'.modes',W1),
        atom_concat(O,'.facts',W2),
	open(W1, write, WModes),
	open(W2, write, WFacts),
	do(S, WModes, WFacts), 
        close(WModes), close(WFacts),
	close(S),
        write('WILL START COUNTING'), nl,
        count(O).
/*
main([F]) :-
	unix(argv([F])), !,
	open(F, read, S),
	W = user_output,
	do(S, W),
	close(S).
main([]) :-	
	S = user_input,
	W = user_output,
	do(S, W).
*/
do(S, WModes, WFacts) :-
	get_titles(S, WModes, Titles),
	get_lines(S, WFacts, Titles).

get_titles(S, W, Titles) :-
	read_line_to_codes(S,Line),
	split(Line, ",", OTitles),
	list_of_titles(OTitles, Titles),
%	format('~q~n~q~n',[OTitles,Titles]),
	output_modes(Titles, W).

%ICD: changed here to use the Key name
%output_modes(_Key.Titles, W) :-
output_modes([Key|Titles], W) :-
	format('~q~n',[[Key|Titles]]),
	output_modes, !,
	send_determinations(Titles, W),
%	format(W, ':- modeh(*,upgrade(+key)).~n',[]),
	format(W, ':- modeh(*,upgrade(+~q)).~n',[Key]),
%	send_modes(Titles, W).
	send_modes(Titles, Key, W).

send_determinations([], W) :-
	nl(W).
send_determinations([T|Titles], W) :-
	format(W, ':- determination(upgrade/1,~q/2).~n',[T]),
	send_determinations(Titles, W).

%ICD: changed to use the key name
send_modes([], _, W) :-
	nl(W).
send_modes([T|Titles], Key, W) :-
	format(W, ':- modeb(*,~q(+~q,-~q)).~n',[T,Key,T]),
	format(W, ':- modeb(*,~q(+~q,#~q)).~n',[T,Key,T]),
	send_modes(Titles, Key, W).

list_of_titles([],[]).
list_of_titles([S|Ss], [A|As]) :-
%	atom(A, S), % ICD: convert first letter to lowercase, remove plics
        S = [H|T], char_type(LowH,to_lower(H)), atom_codes(A,[LowH|T]),
	list_of_titles(Ss, As).

continue_list(Titles) -->
	",", !,
	list_of_titles(Titles).
continue_list([]) --> [].


get_lines(S, W, Titles) :-
	read_line_to_codes(S, Line),
        write(Line), nl, % ICD
	add_line(Line, S, W, Titles).

add_line(end_of_file, _, _, _) :- !.
add_line(Line, S, W, Titles) :-
	get_data(Titles, W, Line, []), !,
%	write('Parsed correctly'), nl,
	get_lines(S, W, Titles).

get_data([_|Titles], W, S1, S) :-
	get_field(N, S1,S2),
%	write(S1), nl, write(N), nl, write(S2), nl,
	get_more_data(Titles, W, N, S2, S).

get_more_data([Field|L], W, Key, L, L) :- !,
	add_as_empty([Field|L], W, Key).
get_more_data([Field|L], W, Key) -->
%	{write([Field|L]), nl},
	",", !,
	get_field(N),
%	{ write(N), write('--'), nl },
	{ output_field(W, Field, Key, N) },
	get_more_data(L, W, Key).
get_more_data([], _, _) --> [].

get_field(N) -->
	"\"", time(N), "\"", !.
get_field(N) -->
	time(N), !.
get_field(N) -->
	timeHours(N), !.
get_field(N) -->
	atom(N), !.
get_field(N) -->
	number(N), !.
get_field(?) -->
	empty, !.
get_field(A) -->
	any(S),
	{atom_codes(A, S) }.

any([], [0',|L],  [0',|L]) :- !.
any([], [],  []) :- !.
any([C|Cs]) --> [C], any(Cs).
	
time(N) -->
	natural(Year),
	"-",
%	"/",
	month(Month),
	"-",
%	"/",
	natural(Day),
	{ %writeln(Day:Month:Year), 
          cvt_to_n(Day, Month, Year, N) }.

timeHours(H:M:S) -->
	natural(H),
	{format('~q~n',[H])},
	":",
	natural(M),
	":",
	natural(S).

cvt_to_n(D, M, Y0, N) :-
	Y0 =< 10,
	!,
	Y is 2000+Y0,
	days(Y,M,D,N).
cvt_to_n(D, M, Y0, N) :-
	Y0 > 10,
	Y0 < 100,
	!,
	Y is 1900+Y0,
	days(Y,M,D,N).
cvt_to_n(D, M, Y, N) :-
	days(Y,M,D,N).
	
number(N) -->
	"-", !,
	pos_number(N1),
	{ N is -N1 }.
number(N) -->
	"+", !,
	pos_number(N1),
	{ N is -N1 }.
number(N) -->
	pos_number(N).

pos_number(N) -->
	natural(L, L0),
	do_float(L0),
	{ number_codes(N,L) }.

do_float([0'e|L0]) -->
	"e", !,
	integer(L0, []).
do_float([0'E|L0]) -->
	"E", !,
	integer(L0, []).
do_float([0'.|L0]) -->
	".", !,
	natural(L0, []).
do_float([]) -->
	[].

natural(N) -->
	natural(L, []),
	{ number_codes(N, L) }.

natural([C|L], L0) -->
	[C],
	{ C >= 0'0, C =< 0'9 }, !,
	more_naturals(L, L0).

more_naturals([C|L], L0) -->
	[C],
	{ C >= 0'0, C =< 0'9 }, !,
	more_naturals(L, L0).
more_naturals(L0, L0) --> [].

integer(L,L0) -->
	"+", !,
	natural(L, L0).
integer([0'-|L],L0) -->
	"-", !,
	natural(L, L0).
integer(L,L0) -->
	natural(L, L0).

atom(T) -->
	"\"",
	quoted(Name),
	{ atom_codes(T, Name) }.

quoted([0'"|Name]) --> "\"\"", !, %"
	quoted(Name).
quoted([]) --> "\"", !.
quoted([C|Name]) --> [C],
	quoted(Name).

empty([0',|L],  [0',|L]).
empty([],  []).

month(1) --> "Jan", !.
month(2) --> "Feb", !.
month(3) --> "Mar", !.
month(4) --> "Apr", !.
month(5) --> "May", !.
month(6) --> "Jun", !.
month(7) --> "Jul", !.
month(8) --> "Aug", !.
month(9) --> "Sep", !.
month(10) --> "Oct", !.
month(11) --> "Nov", !.
month(12) --> "Dec", !.
month(I) --> natural(I).

add_as_empty([], _, _).
add_as_empty([Field|L], W, Key) :-
	output_field(W, Field, Key, ?),
	add_as_empty(L, W, Key).

% ICD: changed
%output_field(_W, _Field, _Key, ?) :- !.
%	format(W,'~q(~q,~q).~n',[Field,Key,N]).
%output_field(W, Field, Key, N) :-
%	format(W,'~q(~q,~q).~n',[Field,Key,N]).
% ICD: included counters for Field/Value
output_field(W, Field, Key, ?) :- !,
	format(W,'~q(~q,~q).~n',[Field,Key,?]),
        counting(Field,missing).
/*
output_field(W, Field, Key, N) :-
        not atom(N), !,
	format(W,'~q(~q,~q).~n',[Field,Key,N]),
        counting(Field,N).
output_field(W, Field, Key, N) :-
% convert first letter of predicate value N to lower case if it is uppercase
        atom_chars(N,[Char|Chars]), 
        char_type(Char,upper),
        atom_codes(Char,Code),
        char_type(LowChar,to_lower(Code)), atom_chars(A,[LowChar|Chars]),
	format(W,'~q(~q,~q).~n',[Field,Key,A]),
        counting(Field,A), !.
*/
output_field(W, Field, Key, N) :-
	format(W,'~q(~q,~q).~n',[Field,Key,N]),
        counting(Field,N).

% ICD: include counters
counting(Field,Value) :-
        retract(counter(Field,Value,C)),
        C1 is C + 1,
        assertz(counter(Field,Value,C1)), !.
counting(Field,Value) :-
        assertz(counter(Field,Value,1)).

% when it ends
count(FileDescription) :-
write('WILL START COUNTING'), nl,
        atom_concat(FileDescription,'.txt',File),
        tell(File),
%        listing(counter),
        counter(Field,_Value,_C),
        once(counter(Field,_Value,_C)),
%        format('**** WILL WRITE ALL VALUES FOR FIELD: ~q~n',[Field]),
        mydisplay(Field),
%        format('**** WROTE ALL VALUES FOR FIELD: ~q~n',[Field]),
        fail.
count(_) :- told.

/*
mydisplay(Field) :-
        write(Field), nl,
        counter(Field,Value,C),
        tab(4),
        format('~q: ~q~n',[Value,C]),
        fail.
mydisplay(Field) :-
%        format('**** REMOVING ALL FIELD: ~q~n',[Field]),
        retractall(counter(Field,_,_)).
*/
% other solution
mydisplay(Field) :-
        write(Field), nl,
        order_by(counter/3, 2),
        forall(counter(Field,Value,C), 
               (tab(4), format('~q: ~q~n',[Value,C]))
        ),
        retractall(counter(Field,_,_)). %,
%        format('REMOVED ALL FIELD: ~q~n',[Field]). %,
%        listing(counter).