/*
    Term to Html converter. 

    use node_html(Nodes, Html) to convert the prolog structure to an HTML string.

    Each element type can take children and some can take attributes. 
    Text content must be wrapped in an s(_) term to identify it as different to other content.
    Examples; 
    1. A paragraph with some text.

    p(s("This is the text")).

    2. A div with class and two paragraphs.

    div([class("my-div")], [
        p(s("This is the first paragraph.")),
        p(s("This is the second paragraph."))
    ]).

    Not every attribute type or HTML type has been mapped, but they are easy to map below. 
    - el_type(ElementTerm, Definition) maps the elements, there are plenty of examples.
    - el_attribute(AttributeTerm, StringName, Value) maps attribute types. 
*/

% 
% HTML Elements 
%
el_unify(A,A).

% Section Elements
el_type(html(C), node("html", C)).
el_type(body(C), node("body", C)).
el_type(head(C), node("head", C)).
el_type(header(C), node("header", C)).
el_type(main(C), node("main", C)).
el_type(footer(C), node("footer", C)).
el_type(nav(C), node("nav", C)).
el_type(article(C), node("article", C)).
el_type(section(C), node("section", C)).
el_type(section(A, C), attr("section", C, A)).

% Format elements
el_type(div(C), node("div", C)).
el_type(div(A, C), node("div", C, A)).
el_type(span(C), node("span", C)).
el_type(span(A, C), node("span", C, A)).
el_type(hr, attr_only("hr", [])).
el_type(br, attr_only("br", [])).
el_type(p(C), node("p", C)).
el_type(pre(C), node("pre", C)).
el_type(code(C), node("code", C)).
el_type(blockquote(C), node("blockquote", C)).

% Header elements
el_type(title(C), node("title", C)).
el_type(script(C), node("script", C)).
el_type(script(A, C), attr("script", C, A)).
el_type(style(C), node("style", C)).
el_type(link(A), attr_only("link",  A)).
el_type(meta(A), attr_only("meta", A)).

% Text elements
el_type(h1(C), node("h1", C)).
el_type(h2(C), node("h2", C)).
el_type(h3(C), node("h3", C)).
el_type(a(A,C), attr("a", C, A)).

% List elements
el_type(ol(C), node("ol", C)).
el_type(ul(C), node("ul", C)).
el_type(li(C), node("li", C)).
el_type(dl(C), node("dl", C)).
el_type(dt(C), node("dt", C)).
el_type(dd(C), node("dd", C)).

% String types
el_type(s(C), any(C)).

% Valid HTML Attributes
el_attribute(href(V),    "href",    V).
el_attribute(class(V),   "class",   V).
el_attribute(rel(V),     "rel",     V).
el_attribute(title(V),   "title",   V).
el_attribute(id(V),      "id",      V).
el_attribute(name(V),    "name",    V).
el_attribute(content(V), "content", V).

%
% Node to HTML predicates.
%

% Convert a node tree to a HTML string.
node_html(RootNode, Html) :-
    el(RootNode, Html, []).

% An HTML elememt
el(Node, A, B) :- 
    el_type(Node, Type),
    el_typed(Type, A, B).

% An element categorised by type.
el_typed(node(Text, C), A, B) :- 
    el_node(Text, [], C, A, B).
el_typed(attr(Text, C, Attr), A, B) :-
    el_node(Text, Attr, C, A, B).
el_typed(attr_only(Text, Attr), A, B) :-
    el_attr_only_node(Text, Attr, A, B).
el_typed(any(C), A, B) :- 
    el_any(C, A, B).

% Generate a single HTML element node.
el_node(Node, Attributes, Children, [<|A], B) :-
    el_any(Node, A, C),
    el_attributes(Attributes, C, D),
    el_unify(D, [>|E]),
    el_children(Children, E, F),
    el_unify(F, [<, /|G]),
    el_any(Node, G, H),
    el_unify(H, [>|B]).

el_attr_only_node(Node, Attributes, [<|A], B) :-
    el_any(Node, A, C),
    el_attributes(Attributes, C, D),
    el_unify(D, [' ',/,>|B]).
 
% Write any attributes to the HTML node.
el_attributes([], A, A).
el_attributes([Att|Next], A, B) :-
    el_unify(A, [' '|C]),
    el_attribute(Att, AttName, Value),
    el_any(AttName, C, D),
    el_unify(D, ['="'|E]),
    el_any(Value, E, F),
    el_unify(F, ['"'|G]),
    el_attributes(Next, G, B).

% Generate the children for an HTML node.
el_children(s(S), A, B) :- el_any(S, A, B).
el_children([], A, A).
el_children([El|Next], A, B) :-
    el(El, A, C),
    el_children(Next, C, B).

% Write the contents to the output directly
el_any([], A, A).
el_any([C|T], [C|A], B) :-
    el_any(T, A, B).