/**
    (C) 2022 - ntop.org    
*/

<template>
  <div v-if="no_data" class="alert alert-info" id="empty-message">{{ _i18n('flows_page.no_data') }}</div>
  <div class="d-flex justify-content-center align-items-center" v-bind:id="id"></div>
</template>

<script setup>
import { ref, onMounted } from "vue";
import sankeyUtils from "../utilities/map/sankey_utils"

const no_data = ref(false)
const props = defineProps({
  id: String,
  page_csrf: String,
  url: String,
  url_params: Array,
  extra_settings: Object,
})

function SankeyChart(data) {
  /* Get default settings */
  let settings = {
    ...sankeyUtils.getDefaultSankeySettings(),
    ...props.extra_settings
  };

  /* Get the links and nodes formatted list */
  const link_source = d3v7.map(data, settings.linkSource).map(intern);
  const link_target = d3v7.map(data, settings.linkTarget).map(intern);
  const link_source_node = d3v7.map(data, settings.linkSourceNode).map(intern);
  const link_target_node = d3v7.map(data, settings.linkTargetNode).map(intern);
  const link_link = d3v7.map(data, settings.linkLink).map(intern);
  const link_value = d3v7.map(data, settings.linkValue);
  const link_color = d3v7.map(data, settings.linkColor);
  const source_color = d3v7.map(data, settings.sourceColor);
  const target_color = d3v7.map(data, settings.targetColor);
  const source_link = d3v7.map(data, settings.sourceLink);
  const target_link = d3v7.map(data, settings.targetLink);

  let links = data;
  let nodes = Array.from(d3v7.union(link_source, link_target), id => ({ id }));

  const node_id_list = d3v7.map(nodes, settings.nodeId).map(intern);
  settings.nodeGroups = d3v7.map(nodes, settings.nodeGroup).map(intern);

  nodes = d3v7.map(nodes, (_, i) => ({ id: node_id_list[i] }));
  links = d3v7.map(links, (_, i) => ({ 
    source: link_source[i], 
    target: link_target[i], 
    value: link_value[i] ,
    source_node: link_source_node[i],
    target_node: link_target_node[i],
    link: link_link[i],
    color: link_color[i],
    source_color: source_color[i],
    target_color: target_color[i],
    source_link: source_link[i],
    target_link: target_link[i],
  }));

  /* Colors/Label/Titles arrays */
  const color = d3v7.scaleOrdinal(settings.nodeGroups, settings.colors);
  const node_label_list = d3v7.map(nodes, settings.nodeLabel);
  const node_title_list = d3v7.map(nodes, settings.nodeTitle);
  const link_title_list = d3v7.map(links, settings.linkTitle);

  /* Compute the Sankey layout. */
  let sankey = d3v7.sankey()
    .nodeId(({index: i}) => node_id_list[i])
    .nodeAlign(settings.nodeAlign)
    .nodeWidth(settings.nodeWidth)
    .nodePadding(settings.nodePadding)
    .extent([[settings.marginLeft, settings.marginTop], [settings.width - settings.marginRight, settings.height - settings.marginBottom]])
    ({nodes, links});

  const svg = d3v7.create("svg")
    .attr("viewBox", [0, 0, settings.width, settings.height])
    .attr("style", "max-width: 100%; height: 60vh; height: intrinsic;");

  let deltaX, deltaY;
    
  const width = settings.width;
  const link = svg.append("g")
    .attr("fill", "none")
    .attr("stroke-opacity", settings.linkStrokeOpacity)
    .selectAll("g")
    .data(links)
    .join("g")
    .style("mix-blend-mode", settings.linkMixBlendMode)
    .append("path")
    .attr("d", settings.linkPath)
    .attr("stroke", ({ color }) => color )
    .attr("stroke-width", ({ width }) => Math.max(1, width))
    .call(link_title_list ? path => path.append("title").text(({index: i}) => link_title_list[i]) : () => {});


  const drag = d3v7.drag()
    .on("start", function (event, d) {
        const current = d3v7.select(this);
        deltaX = current.attr("x") - event.x;
        deltaY = current.attr("y") - event.y;
    })
    .on("drag", function (event, d) {
      d3v7.select(this)
            .attr("x", event.x + deltaX)
            .attr("y", event.y + deltaY);

      //debugger;
      sankey = d3v7.sankey().update(sankey)
        link.selectAll("path")
        .data(sankey.links, function(d) { return d; });
    });
    
  const node = svg.append("g")
    .attr("stroke", settings.nodeStroke)
    .attr("stroke-width", settings.nodeStrokeWidth)
    .attr("stroke-opacity", settings.nodeStrokeOpacity)
    .attr("stroke-linejoin", settings.nodeStrokeLinejoin)
    .selectAll("rect")
    .data(nodes)    
    .join("rect")
    .call(drag)
	  .on("dblclick", function(data) { 
      data = data.currentTarget.__data__
      const sourceLink = data.sourceLinks;
      const targetLink = data.targetLinks;
      const link = (sourceLink && sourceLink[0]) ? sourceLink[0] : targetLink[0];

      if(link) {
        /* Get the node link from the rest */
        if(link.source.id === data.id) {
          if(link.source_link && link.source_link !== '')
            window.open(link.source_link, '_blank');
        } else if(link.target.id === data.id) {
          if(link.target_link && link.target_link !== '')
            window.open(link.target_link, '_blank');
        } 
      } 
    })
    /*.on("drag", sankeyUtils.dragNodeEvent) */
    .attr("x", d => d.x0)
    .attr("y", d => d.y0)
    .attr("height", d => d.y1 - d.y0)
    .attr("width", d => d.x1 - d.x0)
    .attr("cursor", "pointer")
    .attr("fill", (data) => { 
      const sourceLink = data.sourceLinks;
      let node_color = color(settings.nodeGroups[data.index]) 

      if(sourceLink && sourceLink[0]) {
        /* Get the node color from the rest */
        if(sourceLink[0].source.id === data.id) {
          if(sourceLink[0].source_color && sourceLink[0].source_color !== '') 
            node_color = sourceLink[0].source_color;
        } else if(sourceLink[0].target.id === data.id) {
          if(sourceLink[0].target_color && sourceLink[0].target_color !== '') 
            node_color = sourceLink[0].target_color;
        }
      } 
      
      return node_color;
    })
    .append("title").text(({index: i}) => node_title_list[i]);

  svg.append("g")
    .attr("font-family", "sans-serif")
    .attr("font-size", 10)
    .selectAll("text")
    .data(nodes)
    .join("text")
    .attr("x", d => d.x0 < width / 2 ? d.x1 + settings.nodeLabelPadding : d.x0 - settings.nodeLabelPadding)
    .attr("y", d => (d.y1 + d.y0) / 2)
    .attr("dy", "0.35em")
    .attr("text-anchor", d => d.x0 < settings.width / 2 ? "start" : "end")
    .text(({index: i}) => node_label_list[i]);

  function intern(value) {
    return value !== null && typeof value === "object" ? value.valueOf() : value;
  }

  return Object.assign(svg.node(), {scales: {color}});
}


const _i18n = (t) => i18n(t);
const format_request = function() {
  let params = {}
  props.url_params.forEach((name) => {
    params[name] = ntopng_url_manager.get_url_entry(name);
  });

  return NtopUtils.buildURL(props.url, params); 
}

const updateData = async function(data) {
  /* Show the loading overlay */
  NtopUtils.showOverlays();  

  /* Update the URL using the params needed */
  const url = format_request()
  /* Do the request and update the sankey */
  await $.get(url, function(rsp, status){
    const data = rsp.rsp;
    if(data.length > 0) {
      let chart = SankeyChart(data)
      no_data.value = false
      $(`#${props.id}`).empty();
      $(`#${props.id}`).append(chart);
    } else {
      $(`#${props.id}`).empty();
      no_data.value = true
    }  
  });

  NtopUtils.hideOverlays();
};

onMounted(() => { })

defineExpose({ updateData })
</script>

