:- use_module(library(dcg/basics)).
:- use_module(library(unicode)).

:- set_prolog_flag(double_quotes, codes).

cr_lf        --> "\r\n". % [13, 10].
tab          --> "\t". % [9].
null         --> [0].

call_with_arg1(Arg, Callable) :- call(Callable, Arg).

ascii([C]) :- between(0, 127, C).
ascii_string([]).
ascii_string([C|Cs]) :- ascii([C]), ascii_string(Cs).

contains_substring([], []).
contains_substring(_, []).
contains_substring([X|Xs], [Y|Ys]) :-
   (X = Y -> contains_substring(Xs, Ys)
   ; contains_substring(Xs, [Y|Ys])), !.

ascii_contains_substring([], []).
ascii_contains_substring(Xs, []) :- ascii_string(Xs).
ascii_contains_substring([X|Xs], [Y|Ys]) :-
   ascii([X]),
   (X = Y -> ascii_contains_substring(Xs, Ys)
   ; ascii_contains_substring(Xs, [Y|Ys])), !.

unascii([C|Cs]) -->
   [C|Cs],
   {
       \+ascii_contains_substring([C|Cs], "\r\n"),
       \+ascii_contains_substring([C|Cs], "\t"),
       \+ascii_contains_substring([C|Cs], [0])
   }.

unascii_without([C|Cs], ExcludedSubStrings), [C|Cs] -->
   {
       \+maplist(
           ascii_contains_substring([C|Cs]),
           ExcludedSubStrings)
   },
   unascii([C|Cs]), !.


last_line --> ".", cr_lf.
last_line(L) :- phrase(last_line, L).

text_block(Blk) -->
   {
       last_line(L),
       \+ascii_contains_substring(Blk, L)
   }, Blk.

type(T) --> { T = [_] }, unascii(T).

red_type --> "+".

user_name([]) --> [].
user_name(U)  --> unascii(U).

selector([]) --> [].
selector(S)  --> unascii(S).

host_part([H|Hs]) --> string_without(".\t", [H|Hs]).
host([H])         --> host_part(H).
host([H|Hs])      --> host_part(H), ".", host(Hs).

port(P) --> digits(P), { number_codes(N, P), N >= 0, N =< 65536 }.

dir_entity(
   [type-Type, user-User, selector-Selector, host-Host, port-Port]
) -->
   type(Type), user_name(User),
   tab,
   selector(Selector),
   tab,
   host(Host),
   tab,
   port(Port), cr_lf.
dir_entity(
   [type-"+", user-User, selector-Selector, host-Host, port-Port]
) -->
   red_type, user_name(User),
   tab,
   selector(Selector),
   tab,
   host(Host),
   tab,
   port(Port), cr_lf.
dir_entity([]) --> [].

menu_entity([]) --> last_line.
menu_entity([D|Ds]) --> dir_entity(D), dir_entity(Ds), last_line.