{% macro forecast_route(forecast) %}
{% if forecast.constant_values is defined %}
forecasts/cdf/{{ forecast.forecast_id }}
{% elif forecast.constant_value is defined %}
forecasts/cdf/single/{{ forecast.forecast_id }}
{% else %}
forecasts/single/{{ forecast.forecast_id }}
{% endif %}
{% endmacro %}

{% macro metric_table_fx_vert(report_metrics, category, metric_ordering, open_default=False) %}
<details{% if open_default %} open=""{% endif %}>
  <summary>
    <h4>Table of {{ category|lower }} metrics</h4>
  </summary>
  <div class="report-table-wrapper">
  <table class="table table-striped metric-table-fx-vert" style="width:100%;">
    <thead>
      <tr class="header">
        <th style="text-align: left;">Forecast</th>
        {% for metric in metric_ordering %}
          <th style="text-align: left;">{{ human_metrics[metric] }}</th>
        {% endfor %}
      </tr>
    </thead>
    <tbody>
      {% for fx_result in report_metrics %}
        <tr>
          <td>{{ fx_result.name }}</td>
          {% for metric in metric_ordering %}
            {% set ns = namespace(found=false) %}
            {% for mval in fx_result.values %}
              {% if mval.category == category  and mval.metric == metric %}
                {% set ns.found = true %}
                {% if -0.01 < mval.value < 0.01 %}
                  <td>{{ '%.2e' | format(mval.value) }}</td>
                {% else %}
                  <td>{{ '%0.3g' | format(mval.value) }}</td>
                {% endif %}
              {% endif %}
            {% endfor %}
            {% if not ns.found %}
              <td></td>
            {% endif %}
          {% endfor %}
        </tr>
      {% endfor %}
    </tbody>
  </table>
  </div>
</details>
{% endmacro %}


{% macro summary_stats_table_vert(summary_stats, category) %}
{% set span = (human_statistics.values() | length) %}
<details>
  <summary>
    <h4>Table of {{ human_categories[category]|lower }} data summary statistics</h4>
  </summary>
  <div class="report-table-wrapper">
  <table class="table table-striped table-bordered summary-stats-table-vert" style="width:100%;">
    <thead>
      <tr class="header">
        <th scope="col" style="text-align: left;">Aligned Pair</th>
        <th scope="col" colspan="{{ span }}" style="text-align: center;">Observation</th>
        <th scope="col" colspan="{{ span }}" style="text-align: center;">Forecast</th>
        <th scope="col" colspan="{{ span }}" style="text-align: center;">Reference Forecast</th>
      </tr>
      <tr>
        <th></th>
        {% for i in range(3) %}
        {% for title in human_statistics.values() %}
        <th style="text-align: center;">{{ title }}</th>
        {% endfor %}
        {% endfor %}
      </tr>
    </thead>
    <tbody>
      {% for stat_result in summary_stats %}
        <tr>
          <td>{{ stat_result.name }}</td>
          {% for thing in ['observation', 'forecast', 'reference_forecast'] %}
            {% for metric in human_statistics.keys() %}
              {% set ns = namespace(found=false) %}
              {% for mval in stat_result.values %}
                {% if mval.category == category  and mval.metric == thing + '_' + metric %}
                  {% set ns.found = true %}
                  {% if -0.01 < mval.value < 0.01 %}
          <td>{{ '%.2e' | format(mval.value) }}</td>
                  {% else %}
          <td>{{ '%0.3g' | format(mval.value) }}</td>
                  {% endif %}
                {% endif %}
              {% endfor %}
              {% if not ns.found %}
          <td></td>
              {% endif %}
            {% endfor %}
          {% endfor %}
        </tr>
      {% endfor %}
    </tbody>
  </table>
  </div>
</details>
{% endmacro %}


{% macro metric_table_fx_horz(report_metrics, category, metric_ordering) %}
{% set metric_indexes = [] %}
{% for mresult in report_metrics %}
  {% for mval in mresult.values %}
    {% if mval.category == category and mval.index not in metric_indexes %}
      {% set metric_indexes = metric_indexes.append(mval.index) %}
    {% endif %}
  {% endfor %}
{% endfor %}
<table class="table table-striped" style="width:100%;">
  <caption style="caption-side:top; text-align:center">
    Table of {{ category }} metrics
  </caption>
  <thead>
    <tr class="header">
      <th></th>
      {% for mresult in report_metrics %}
        <th colspan="{{metric_ordering|length}}" style="text-align: center;">{{ mresult.name }}</th>
      {% endfor %}
    </tr>
    <tr class="header">
      <th style="text-align: left;">{{ category }} Value</th>
      {% for mresult in report_metrics %}
        {% for metric in metric_ordering %}
          <th style="test-align: center;">{{ human_metrics[metric] }}</th>
        {% endfor %}
      {% endfor %}
    </tr>
  </thead>
  <tbody>
    {% for mindex in metric_indexes %}
      <tr>
        <td>{{ mindex }}</td>
        {% for mresult in report_metrics %}
          {% for metric in metric_ordering %}
            {% set ns = namespace(found=false) %}
            {% for mval in mresult.values %}
              {% if mval.category == category  and mval.metric == metric and mval.index == mindex %}
                {% set ns.found = true %}
                {% if -1 < mval.value < 1 %}
                  <td>{{ '%.2e' | format(mval.value) }}</td>
                {% else %}
                  <td>{{ '%0.3g' | format(mval.value) }}</td>
                {% endif %}
              {% endif %}
            {% endfor %}
            {% if not ns.found %}
              <td></td>
            {% endif %}
          {% endfor %}
        {% endfor %}
      </tr>
    {% endfor %}
  </tbody>
</table>
{% endmacro %}


{% macro validation_metadata_table(quality_filters) %}
<details>
  <summary>
    <h4>Table of data validation filters</h4>
  </summary>
  <div class="report-table-wrapper">
  <table class="table table-striped validation-table" style="width:100%;" id="data-validation-filters-table">
    <thead>
      <tr class="header">
        <th style="text-align: left;">Quality flags</th>
        <th style="text-align: left;">Discard before resample</th>
        <th style="text-align: left;">Resample threshold (%)</th>
      </tr>
    </thead>
    <tbody>
      {% for qfilter in quality_filters %}
        <tr>
          <td style="text-align: left">{{ qfilter.quality_flags | join(', ') }}</td>
          <td style="text-align: left">{{ qfilter.discard_before_resample }}</td>
          <td style="text-align: left">{{ qfilter.resample_threshold_percentage }}</td>
        </tr>
      {% endfor %}
    </tbody>
  </table>
  </div>
</details>
{% endmacro %}


{% macro validation_results_table(proc_fxobs_list, before_resample) %}
<details>
  <summary>
    <h4>Table of data validation results {{'before' if before_resample else 'after'}} resampling</h4>
  </summary>
  <div class="report-table-wrapper">
  <table class="table table-striped validation-table" style="width:100%;" id="data-validation-results-{{'before' if before_resample else 'after'}}-table">
    <thead>
      <tr class="header">
        <th style="text-align: left;">Aligned Pair</th>
        {% for proc_fxobs in proc_fxobs_list %}
        <th style="text-align: center; vertical-align: middle">
          {{ proc_fxobs.name }}
        </th>
        {% endfor %}
      </tr>
      <tr class="header">
        <th style="text-align: left;">Observation</th>
        {% for proc_fxobs in proc_fxobs_list %}
        <th style="text-align: center; vertical-align: middle">
          {{ proc_fxobs.original.data_object.name }}
        </th>
        {% endfor %}
      </tr>
    </thead>
    <tbody>
      {% for flag in (proc_fxobs_list | unique_flags_filter(before_resample)) %}
        <tr>
          <td style="text-align: left">{{ flag }}</td>
          {% for proc_fxobs in proc_fxobs_list %}
            {% set ns = namespace(found=false) %}
              {% for res in proc_fxobs.validation_results %}
                {% if res.flag == flag %}
                  {% set ns.found = true %}
          <td style="text-align: center">{{ res.count }}</td>
                {% endif %}
              {% endfor %}
            {% if not ns.found %}
          <td></td>
            {% endif %}
          {% endfor %}
        </tr>
      {% endfor %}
    </tbody>
  </table>
  </div>
</details>
{% endmacro %}


{% macro preprocessing_table(proc_fxobs_list) %}
{% set preproc_steps = [] %}
{% for proc_fxobs in proc_fxobs_list %}
  {% for res in proc_fxobs.preprocessing_results %}
    {% if res.name not in preproc_steps %}
      {% set preproc_steps = preproc_steps.append(res.name) %}
    {% endif %}
  {% endfor %}
{% endfor %}
<details open="">
  <summary>
    <h4>Table of data preprocessing results</h4>
  </summary>
  <div class="report-table-wrapper">
  <table class="anchor table table-striped preprocessing-table" style="width:100%;" id="data-preprocessing-results-table">
    <thead>
      <tr class="header">
        <th style="text-align: left;">Preprocessing Description</th>
        {% for proc_fxobs in proc_fxobs_list %}
        <th style="text-align: center;">
          {{ proc_fxobs.name }} <br>Number of Points
        </th>
        {% endfor %}
      </tr>
    </thead>
    <tbody>
      {% for pstep in preproc_steps %}
        <tr>
        <td style="test-align: left">{{ pstep }}</td>
        {% for proc_fxobs in proc_fxobs_list %}
          {% set ns = namespace(found=false) %}
          {% for res in proc_fxobs.preprocessing_results %}
            {% if res.name == pstep %}
              {% set ns.found = true %}
              <td style="text-align: center">{{ res.count }}</td>
            {% endif %}
          {% endfor %}
          {% if not ns.found %}
            <td></td>
          {% endif %}
        {% endfor %}
        </tr>
      {% endfor %}
    </tbody>
  </table>
  </div>
</details>
{% endmacro %}


{% set getblob = "
        //addresses IE
        if (navigator.msSaveBlob) {
            navigator.msSaveBlob(blob, filename)
        } else {
            const link = document.createElement('a')
            link.href = URL.createObjectURL(blob)
            link.download = filename
            link.target = '_blank'
            link.style.visibility = 'hidden'
            link.dispatchEvent(new MouseEvent('click'))
        }
" %}

{% macro download_metrics_script() %}
<script>
    function json_to_csv(source) {
        const columns = Object.getOwnPropertyNames(source[0])
        const nrows = source.length
        const lines = [columns.join(',')]

        for (let i = 0; i < nrows; i++) {
            let row = [];
            for (let j = 0; j < columns.length; j++) {
                const column = columns[j]
                var val = source[i][column]
                if (column == 'index' && typeof val == 'number' && val > 100000000) {
                    val = new Date(val).toISOString().split('T')[0]
                } else {
                    if(val == null){
                        val = 'nan';
                    }else{
                        val = val.toString()
                    }
                }
                row.push(val)
            }
            lines.push(row.join(','))
        }
        return lines.join('\n').concat('\n')
    }

    function download_metrics_as_csv(report_name) {
        const filename = report_name.replace(/ /g, '_') + '_metrics.csv'
        const filetext = json_to_csv(metrics_json)
        const blob = new Blob([filetext], { type: 'text/csv;charset=utf-8;' })
        {{ getblob }}
    }

    function download_metrics_as_json(report_name) {
        const filename = report_name.replace(/ /g, '_') + '_metrics.json'
        const blob = new Blob([JSON.stringify(metrics_json)], { type: 'application/json;charset=utf-8;' })
        {{ getblob }}
    }

    function download_stats_as_csv(report_name) {
        const filename = report_name.replace(/ /g, '_') + '_summary_stats.csv'
        const filetext = json_to_csv(summary_stats)
        const blob = new Blob([filetext], { type: 'text/csv;charset=utf-8;' })
        {{ getblob }}
    }

    function download_stats_as_json(report_name) {
        const filename = report_name.replace(/ /g, '_') + '_summary_stats.json'
        const blob = new Blob([JSON.stringify(summary_stats)], { type: 'application/json;charset=utf-8;' })
        {{ getblob }}
    }
</script>
{% endmacro %}


{% macro download_metadata_script() %}
<script>
    function download_metadata_as_json(report_name) {
        const filename = report_name.replace(/ /g, '_') + '_metadata.json'
        const blob = new Blob([JSON.stringify(metadata_json)], { type: 'application/json;charset=utf-8;' })
        {{ getblob }}
    }
</script>
{% endmacro %}


{% macro download_timeseries_script() %}
<script>
    function strip_constant_value_from_hovertext(hovertext) {
        const end = hovertext.indexOf('<br>');
        const constant_value = hovertext.slice(3, end).trim();
        return constant_value;
    }

    function extract_distribution_data(data, distribution_name) {
        // Collect all of the constant values in a single csv
        const dist_values = data.filter(column => column.name == distribution_name && !column.showlegend);
        const dist_percentages = dist_values.map(
          series => strip_constant_value_from_hovertext(series.hovertemplate)
        );
        const index = dist_values[0].x;
        let lines = [];
        // push header
        lines.push("timestamp,"+ dist_percentages.join(','));
        const nrows = index.length;

        for (let j = 0; j < nrows; j++) {
            let row = [];
            row.push(index[j])
            for (let i = 0; i < dist_percentages.length; i++) {
              let val = dist_values[i].y[j];
              if(val == null){
                val = 'nan';
              }else{
                val = val.toString()
              }
              row.push(val)
            }
            lines.push(row.join(','))
        }
        return lines.join('\n').concat('\n')
    }

    function extract_plotly_data(plot, ts_names=null) {
      if (typeof plot === 'undefined') { return ''}
      const data = plot.data;
      const colnames = data.map(d => d.name.replace(/\<br\>/g, ' '));
      const indexes = data.map(d => d.x);
      const values = data.map(d => d.y);
      const csvs = {};

      function get_column_lines(column_index) {
        let lines = [];
        // push header
        lines.push("timestamp,"+ colnames[column_index]);
        const nrows = indexes[column_index].length;
        for (let j = 0; j < nrows; j++) {
          let row = [];
          row.push(indexes[column_index][j])
          let val = values[column_index][j];
          if(val == null){
            val = 'nan';
          }else{
            val = val.toString()
          }
          row.push(val)
          
          lines.push(row.join(','))
        }
        return lines
      }
      // col_idxs is an array of integer indexes we can parse from the plot data
      // directly
      let col_idxs = [];

      // dist names is an array of string names that are shared by all
      // series in a distribution. We will use it to parse out the series
      // for each constant value in the distribution.
      let dist_names = [];

      function get_all_indices(to_search, value) {
        let indices = [];
        let i = to_search.indexOf(value);
        while(i >= 0) {
          indices.push(i);
          i = to_search.indexOf(value, i+1);
        }
        return indices;
      }

      if (ts_names != null) {
        // if a timeseries name was provided, only extract data
        // for that series
        for (ts_name of ts_names) {
          let indices = get_all_indices(colnames, ts_name);
          if (indices.length) {
            if (indices.length == 1) {
              col_idxs.push(indices[0]);
            } else {
              dist_names.push(ts_name);
            }
          }
        }
      } else {
        col_idxs = colnames;
      }
      for (const col_idx of col_idxs) {
        if (col_idx < 0) {
          // if col_idx is negative, a column was provided that does not exist in
          // the data
          continue;
        }
        const lines = get_column_lines(col_idx)
        csvs[colnames[col_idx]] = lines.join('\n').concat('\n');
      } 
      for (const dist_name of dist_names) {
        csvs[dist_name] = extract_distribution_data(data, dist_name);
      }
      return csvs;
    }

    function make_download_table() {
        // Adds a table of timeseries names and download checkboxes
        // to the collapsible "details" element above the timeseries
        let download_container = document.getElementById("timeseries-downloads");
        // css here to include in downloads
        download_container.style.border = "1px solid #dee2e6";
        download_container.style["border-radius"] = ".5em";
        download_container.style.padding = ".5em";

        if (download_container) {
            function update_file_count() {
                let count = document.querySelectorAll('[name=download-flag]:checked').length;
                let download_button = document.getElementById("download-timeseries-button");
                let buttonText = `Download ${count} CSVs`;
                if (count == 0) {
                    buttonText = "Select CSVs to download";
                    download_button.disabled = true;
                } else {
                    download_button.disabled = false;
                }
                download_button.textContent = buttonText;
            }
            function select_all_downloads() {
                Array.from(
                    document.querySelectorAll('[name=download-flag]')
                ).map(checkbox => checkbox.checked = true);
                update_file_count();
            }
            let series_names = ts_plot.data.map(d => d.name.replace(/\<br\>/g, ' '));
            // remove duplicates
            let columns = series_names.filter((val, idx, arr) => arr.indexOf(val) === idx);
            const table = document.createElement("table");
            table.classList.add("table");
            const thead = document.createElement("thead");
            const th_row = document.createElement("tr");
            thead.appendChild(th_row);
            const name_thead = document.createElement("th");
            name_thead.appendChild(document.createTextNode("Name"));
            th_row.appendChild(name_thead);
            const download_thead = document.createElement("th");
            download_thead.appendChild(document.createTextNode("Download "));
            let check_all = document.createElement("button");
            check_all.classList.add("btn", "btn-sm", "btn-primary");
            check_all.appendChild(document.createTextNode("check all"));
            check_all.onclick = select_all_downloads;
            download_thead.appendChild(check_all);
            th_row.appendChild(download_thead);
            thead.appendChild(th_row);
            table.appendChild(thead);
            const tbody = document.createElement("tbody");

            for (const col_name of columns) {
                const tr = document.createElement("tr");
                const name_td = document.createElement("td");
                name_td.appendChild(document.createTextNode(col_name));
                tr.appendChild(name_td);

                const download_td = document.createElement("td");
                const download_checkbox = document.createElement("input");
                download_checkbox.onclick = update_file_count;
                download_checkbox.setAttribute("name", "download-flag");
                download_checkbox.setAttribute("value", col_name);
                download_checkbox.setAttribute("type", "checkbox");
                download_td.appendChild(download_checkbox);
                tr.appendChild(download_td);
                tbody.appendChild(tr);
            }
            table.appendChild(tbody);
            download_container.appendChild(table);

            function download_selected() {
                const selected = Array.from(
                    document.querySelectorAll('[name=download-flag]:checked')
                ).map(x => x.value);
                download_timeseries_data_as_csv(selected);
            }
            const download_button = document.createElement("button");
            download_button.id = "download-timeseries-button";
            download_button.disabled = true;
            download_button.appendChild(document.createTextNode("Select CSVs to download"));
            download_button.classList.add("btn", "btn-sm", "btn-primary");
            download_button.onclick = download_selected;
            download_container.appendChild(download_button);
        }
    }

    function download_timeseries_data_as_csv(ts_names) {
        const csvdata = extract_plotly_data(ts_plot, ts_names)
        for (const csvname in csvdata){
          // replace spaces with underscores and remove commonly forbidden
          // filename characters
          const filename = csvname.replace(/ /g, '_').replace(/[<>:"/\|?*]/g,'') + '_timeseries.csv'
          const blob = new Blob([csvdata[csvname]], { type: 'text/csv;charset=utf-8;' })
            {{ getblob }}
        }
    }
    if (ts_plot) {
        make_download_table();
    }
</script>
{% endmacro %}

{% macro start_end_query(report_parameters) %}
{% if report_parameters is defined %}
?start={{ report_parameters.start }}&end={{ report_parameters.end }}
{% endif %}
{% endmacro %}
