/*
 * Licensed to The OpenNMS Group, Inc (TOG) under one or more
 * contributor license agreements.  See the LICENSE.md file
 * distributed with this work for additional information
 * regarding copyright ownership.
 *
 * TOG licenses this file to You under the GNU Affero General
 * Public License Version 3 (the "License") or (at your option)
 * any later version.  You may not use this file except in
 * compliance with the License.  You may obtain a copy of the
 * License at:
 *
 *      https://www.gnu.org/licenses/agpl-3.0.txt
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied.  See the License for the specific
 * language governing permissions and limitations under the
 * License.
 */
const $ = require('vendor/jquery-js');
const c3 = require('vendor/c3-js');
const d3 = require('vendor/d3-js');
require('./status-box.css');

d3.document = window.document;

// the color palette to map each severity to
const colorPalette = {
  Normal: '#336600',
  Warning: '#ffcc00',
  Minor: '#ff9900',
  Major: '#ff3300',
  Critical: '#cc0000'
};

// the size of each donut
const donutSize = {
  // Don't set size to allow resizing automatically based on space available
  width: 0,
  height: 0
};

const normalizeDonutTops = function() {
  const arcs = document.querySelectorAll('#chart-content .c3-chart-arcs');
  if (arcs.length < 2) {
    return;
  }

  let minY = Infinity;
  arcs.forEach(function(arc) {
    const transform = arc.getAttribute('transform');
    const match = transform && transform.match(/translate\(\s*[^,]+,\s*([^)]+)\)/);
    if (match) {
      minY = Math.min(minY, parseFloat(match[1]));
    }
  });

  if (!isFinite(minY)) {
    return;
  }

  arcs.forEach(function(arc) {
    const transform = arc.getAttribute('transform');
    const match = transform && transform.match(/translate\(\s*([^,]+),\s*[^)]+\)/);
    if (match) {
      arc.setAttribute('transform', 'translate(' + match[1] + ',' + minY + ')');
    }
  });
};

const loadChartData = function(graph) {
  return new Promise(function(resolve) {
    $.ajax({
      method: 'GET',
      url: graph.url,
      headers: {
        'X-Requested-With': 'XMLHttpRequest'
      },
      dataType: 'json',
      success: function(data) {
      const columns = [];

      // Only include values > 0
      for (let i = 0; i < data.length; i++) {
        if (data[i].length >= 2) {
          if (data[i][1] > 0) {
            columns.push(data[i]);
          }
        }
      }

      // Decide to show or hide the graph
      let sum = 0;
      for (let i = 0; i < data.length; i++) {
        sum += data[i][1];
      }
      if (sum > 0) {
        // The first chart with data shows the box
        $('#status-overview-box').show();
        $('#' + graph.id).parent().show();

        // Generate graph
        const chart = c3.generate({
          bindto: '#' + graph.id,
          size: donutSize,
          data: {
            order: null,
            columns: [],
            colors: colorPalette,
            type: 'donut',
            onclick: graph.onclick
          },
          donut: {
            title: graph.title,
            label: {
              format: function(value, id, ratio) {
                return value;
              }
            }
          },
          onrendered: normalizeDonutTops
        });
        chart.load({
          columns: columns,
          done: resolve
        });

        // Add graph tooltip
        const description = graph.description || graph.title || '';
        if (description !== '') {
          d3.select('#' + graph.id)
            .select('.c3-chart')
            .append('svg:title')
            .text(description);
        }
      } else {
        resolve();
      }
      },
      error: resolve
    });
  });
};

const render = function(options) {
  const graphs = options.graphs;
  if (graphs === undefined || graphs === null || graphs.length === 0) {
    return;
  }
  if (options.parentContainer === undefined || graphs.parentContainer === null) {
    return;
  }
  if ($(options.parentContainer) === undefined) {
    return;
  }

  const promises = [];
  for (let i = 0; i < graphs.length; i++) {
    // Gather options to draw graph
    const graph = graphs[i];

    // Skip the entry when any of the required fields are missing
    if (graph.id === undefined || graph.id === null || graph.id === '') {
      return;
    }
    if (graph.url === undefined || graph.url === null || graph.url === '') {
      return;
    }

    // create container for graph if it does not exist yet
    if ($('#' + graph.id).length === 0) {
      const graphContainer = $('<div/>', {
        class: 'mx-auto col-xs-12 col-sm-6 col-md-6 col-lg-4'
      });
      graphContainer.append($('<div></div>', {
        id: graph.id
      }));
      graphContainer.hide();
      $(options.parentContainer).append(graphContainer);
    }

    // load data and populate graph
    promises.push(loadChartData(graph));
  }
  Promise.all(promises).then(normalizeDonutTops);
};

// all supported graphs
const graphDefinitions = {
  'business-services': {
    id: 'businessServiceProblemChart',
    title: 'Business Services',
    description: 'Business Services Status Overview',
    url: 'api/v2/status/summary/business-services',
    onclick: function(e) {
      window.location = 'status/index.jsp?title=Business Service List&type=business-services&severityFilter=' + e.id;
    }
  },

  applications: {
    id: 'applicationProblemChart',
    title: 'Applications',
    description: 'Applications Status Overview',
    url: 'api/v2/status/summary/applications',
    onclick: function(e) {
      window.location = 'status/index.jsp?title=Application List&type=applications&severityFilter=' + e.id;
    }
  },

  'nodes-by-alarms': {
    id: 'nodeProblemChartsByAlarms',
    title: 'Alarms',
    description: 'Nodes grouped by unacknowledged Alarms',
    url: 'api/v2/status/summary/nodes/alarms',
    onclick: function(e) {
      window.location = 'status/index.jsp?title=Node List&type=nodes&strategy=alarms&severityFilter=' + e.id;
    }
  },
  'nodes-by-outages': {
    id: 'nodeProblemChartByOutages',
    title: 'Outages',
    description: 'Nodes grouped by current Outages',
    url: 'api/v2/status/summary/nodes/outages',
    onclick: function(e) {
      window.location = 'status/index.jsp?title=Node List&type=nodes&strategy=outages&severityFilter=' + e.id;
    }
  }
};

const doRender = function(graphKeys) {
  //console.log('doRender:',graphKeys);
  const graphs = [];
  for (let i = 0; i < graphKeys.length; i++) {
    const graphKey = graphKeys[i];
    if (graphKey !== undefined && graphKey in graphDefinitions) {
      graphs.push(graphDefinitions[graphKey]);
    }
  }

  // only render if something is configured to show
  if (graphs.length !== 0) {
    //console.log('rendering ' + graphs.length + ' graphs');
    render({
      parentContainer: '#chart-content',
      graphs: graphs
    })
  } else {
    //console.log('no graphs found');
  }
};

window.renderStatusGraphs = doRender;
module.exports = doRender;