{# Default way of showing errors #}
{% macro render_error(message) -%}
    <div class="ui icon message error">
        <i class="times circle icon"></i>
        <div class="content">
            <div class="header">
                Error
            </div>
            <p>{{ message }}</p>
        </div>
    </div>
{%- endmacro %}

{# Default macro for showing/wrapping the fetch results #}
{% macro render_fetch_results(content, error) -%}
    {% if error %}
        {{ render_error(error) }}
    {% else %}
        {% if content is undefined %}
            {{ render_error("Some content expected but none was found.") }}
        {% else %}
            {{ caller() }}
        {% endif %}
    {% endif %}
{%- endmacro %}

{# draw a generic pandas table#}
{% macro render_pandas_table(df, caption, column_labels={}) -%}
    {% if (df is defined) and (df is not none) %}
        <table class="display">
            <thead class="center aligned">
            <tr>
                {% for column in df.columns %}
                    {% if column in column_labels %}
                        <th>{{ column_labels[column] }}</th>
                    {% else %}
                        <th>{{ column }}</th>
                    {% endif %}
                {% endfor %}
            </tr>
            </thead>
            <tbody>
            {% for idx, row in df.iterrows() %}
                <tr>
                    {% for colname in df.columns %}
                        {# handle decimal format: float, float64, float32 #}
                        {% if 'float' in (df.dtypes[colname] | string) %}
                            <td class="center aligned collapsing">{{ row[colname] | round(precision=2) }}</td>
                        {% else %}
                            <td class="center aligned collapsing">{{ row[colname]|e }}</td>
                        {% endif %}
                    {% endfor %}
                </tr>
            {% endfor %}
            </tbody>
            <caption>{{ caption }}</caption>
        </table>
    {% else %}
        {{ render_error("How did you get here? did you forget to use 'render_fetch_results' macro?") }}
    {% endif %}
{%- endmacro %}

{############################################}
{# fingerpritinging macros #}
{############################################}

{# select the graphs to fingerprint: either the ones grom the config file or all from the endpoint #}
{% macro select_graphs() -%}
    {# caller_ is a workaround necessary to handle two levels of callers #}
    {% set caller_ = caller %}
    {# if there are some graphs defined in the configuration, then select those #}
    {% if conf.selected_graphs %}
        {% for graph in conf.selected_graphs | unique | list %}
            {% if graph %}
                {{ caller_(graph) }}
            {% else %}
                {{ caller_(None) }}
            {% endif %}
        {% endfor %}
    {% else %}
        {# if no graphs are defined in the configurations, then select all the graphs in the endpoint #}
        {% set select_query =
            """select distinct ?graph
            {graph ?graph {?s ?p []}}""" %}
        {% set content, error = from_endpoint(conf.default_endpoint).with_query(select_query).fetch_tabular() %}
        {# Fingerpring the gefault graph before going to named graphs #}
        {{ caller_(None) }}
        {% call render_fetch_results(content, error) %}
            {% if not content.empty %}
                {% for row in content.itertuples() %}
                    {{ caller_(row.graph) }}
                {% endfor %}
            {% endif %}
        {% endcall %}

    {% endif %}


{%- endmacro %}

{# select all the classes is the given graph #}
{% macro select_distinct_classes(graph=None) -%}
    {% set from_statement = 'FROM <'~graph~'>' if graph else '' %}
    {% set select_query =
    "SELECT DISTINCT ?cls \n"~from_statement~"
    WHERE {[] a ?cls}" %}
    {% set content, error = from_endpoint(conf.default_endpoint).with_query(select_query).fetch_tabular() %}
    {% set caller_ = caller %}
    {% call render_fetch_results(content, error) %}
        {% if not content.empty %}
            {% for row in content.itertuples() %}
                {{ caller_(row.cls) }}
            {% endfor %}
        {% endif %}
    {% endcall %}
{%- endmacro %}


{# fingerprint a given class in a given graph #}
{% macro fingerprint_class(class, graph=None) -%}
    {% set from_statement = 'FROM <'~graph~'>' if graph else '' %}
    {% set select_query =
    "SELECT DISTINCT (?p as ?property)
        (GROUP_CONCAT(distinct ?tto; separator=', ') as ?object_type)
        (GROUP_CONCAT(distinct ?tp; separator=', ') as ?property_type)
        (count(distinct ?s) as ?subject_cnt)
        (count(distinct ?o) as ?object_cnt)
        (count(*) as ?pattern_cnt)
        (if(?subject_cnt < ?total_distinct_s, 0, min(?sp_star)) as ?min)
        (max(?sp_star) as ?max)
        (avg(?sp_star) * ?subject_cnt/?total_distinct_s as ?avg)
    "~from_statement~"
    WHERE {
        values ?ts {<"~class~">}
        ?s a ?ts .
        ?s ?p ?o .
        OPTIONAL { ?o a ?to . }
        BIND(datatype(?o) as ?dto)
        BIND(IF(BOUND(?to),?to, IF (bound(?dto), ?dto, <http://www.w3.org/2000/01/rdf-schema#Resource>) ) as ?tto)
        BIND(IF(isURI(?o),'object' , 'data') as ?tp)
        {
            select ?s ?p (count(*) as ?sp_star)
            {
                ?s ?p [] .
            }
            group by ?s ?p
        }
        {
            select ?ts (count(distinct ?i) as ?total_distinct_s)
            {
                ?i a ?ts
            }
            group by ?ts
        }
    }
    GROUP BY ?ts ?p ?total_distinct_s
    ORDER BY ?subject_type desc(?property_type) desc(?count) ?property ?object_type" %}
    {% set content, error = from_endpoint(conf.default_endpoint).with_query(select_query).fetch_tabular() %}
    {% set caller_ = caller %}
    {% call render_fetch_results(content, error) %}
        {% if not content.empty %}
            {{ caller_(content) }}
        {% endif %}
    {% endcall %}
{%- endmacro %}


{# render a class fingerprint #}
{% macro render_class_fingerprint(content, class_uri, graph_uri, inverted_prefixes) -%}
    {# fingerprint resultset signature #}
    {% set labels = {"property": "Property name",
    "object_type": "Object class",
    "property_type": "Property type",
    "subject_cnt": "Distinct Subjects",
    "object_cnt": "Distinct Objects",
    "pattern_cnt": "Occurences",
    "min": "Min",
    "max": "Max",
    "avg": "Avg",} %}

    {% set substituted_content = replace_strings_in_tabular(content,
                                    target_columns=['property','object_type','property_type'],
                                    value_mapping_dict=inverted_prefixes,
                                    mark_touched_rows=False ) %}
    {% set table_title = "The shape of <" + class_uri | safe + "> class instances" %}
    {{ render_pandas_table(content, table_title, column_labels=labels) }}
{%- endmacro %}

{# build fingerpring for each class in every graph from the endpoint #}
{% macro build_default_endpoint_fingerprint(inverted_prefixes=None) -%}
    {% call(graph) select_graphs() %}
        <h2>{{ 'Named graph ' + graph|safe + " fingerprints" if graph else "Default graph fingerprints" }}</h2>
        <section>
            {% call(class) select_distinct_classes(graph) %}
                {% call(fingerprint_content) fingerprint_class(class) %}
                    {{ render_class_fingerprint(fingerprint_content, class, graph, inverted_prefixes) }}
                {% endcall %}
            {% endcall %}
        </section>
    {% endcall %}
{%- endmacro %}