import cytoscape from 'cytoscape';
import dagre from 'cytoscape-dagre';
import tippy from 'tippy.js';
import 'tippy.js/dist/tippy.css';
import Gradient from 'javascript-color-gradient'; // 多個色階產生器
import TimeLabel from '@/module/timeLabel.js'; // 時間格式轉換器

cytoscape.use( dagre );

/**
 * @param {object}  mapData processMapData | bpmnData
 * mapData:{
 *  startId: 0,
 *  endId: 1,
 *  nodes: [],
 *  edges: []
 * }
 * @param {string} dataLayerType DataLayer's type
 * @param {string} dataLayerOption DataLayer's options
 * @param {string} curve Curve's type
 * @param {string} graphId cytoscape's container
 */
export default function cytoscapeMap(mapData, dataLayerType, dataLayerOption, curveStyle, rank, graphId) {
  // create color Gradient
  // 設定每個 node, edges 的顏色與樣式
  let gradientArray = [];
  let activityArray = [];
  let edgeArray = [];
  let nodeOption = [];
  let edgeOption = [];
  let nodes = mapData.nodes;
  let edges = mapData.edges;

  // 設定除了 start, end 的 node 顏色
  // 找出 type activity's node
  activityArray = nodes.filter(i => i.data.type === 'activity');

  // 找出除了 start, end 以外所有的 node 的 option value
  activityArray.map(node => nodeOption.push(node.data[dataLayerType][dataLayerOption]));

  // 刪掉重複的元素，小到大排序(映對色階淺到深)
  nodeOption = [...new Set(nodeOption)].sort((a, b) => a - b);

  // 產生 node 色階
  gradientArray = new Gradient()
    .setColorGradient("#CCE5FF", "#66b2ff")
    .setMidpoint(nodeOption.length)
    .getColors();

  // 設定每個 node 的背景色
  activityArray.forEach(node => {
    // 透過 index 找對應位置
    let gradientIndex = nodeOption.indexOf(node.data[dataLayerType][dataLayerOption]);
    node.data.backgroundColor = gradientArray[gradientIndex];
  })

  // 設定除了 start, end 的 edges 粗細
  // 找出除了 start, end 以外所有的 edge
  edgeArray = edges.filter(i => i.data.source !== mapData.startId && i.data.target !== mapData.endId);

  // 找出所有 edge 的 option value
  edgeArray.map(edge => edgeOption.push(edge.data[dataLayerType][dataLayerOption]));

  // 刪掉重複的元素，小到大排序(映對色階淺到深)
  edgeOption = [...new Set(edgeOption)].sort((a, b) => a - b);

  // 設定每個 edge 的粗細
  edgeArray.forEach(edge => {
    let edgeIndex = edgeOption.indexOf(edge.data[dataLayerType][dataLayerOption])
    edge.data.lineWidth = (parseInt(edgeIndex) + 1) * 0.15
    edge.data.style = 'solid'
  })

  // create Cytoscape
  let cy = cytoscape({
    container: graphId,
    elements: {
      nodes: nodes, //nodes, // 節點的資料
      edges: edges, //edges, // 關係線的資料
    },
    layout: {
      name: 'dagre',
      rankDir: rank, // 直向 TB | 橫向 LR, 'cytoscape-dagre' 套件裡的變數
    },
    style: [
      // 點擊 node 後改變的樣式
      {
        selector:'node:selected',
        style:{
          'border-color':'red',
          'border-width':'3',
        },
      },
      // node 節點的樣式
      {
        selector: 'node',
        style: {
          'label':
          function(node) { // 節點要顯示的文字
            // node.data(this.dataLayerType+"."+this.dataLayerOption) 為原先陣列 node.data.key.value
            let optionValue = node.data(`${dataLayerType}.${dataLayerOption}`);
            let text = '';

            // 文字超過 10 字尾巴要加「...」，style 要換兩行(\n 換行符號)
            // 使用 data() 是因為在 cytoscape 中從陣列轉為 function
            text = node.data('label').length > 10 ? `${node.data('label').substr(0, 10)}...\n\n` : `${node.data('label')}\n\n`;

            // 在 element 中 activity 歸類在 default，所以要先判斷 node 是否為 activeity 才裝入文字。
            // 可使用 parseInt(整數) parseFloat(浮點數) 將字串轉為數字
            // Relative 要轉為百分比 %
            if(node.data('type') === 'activity') {
              switch(dataLayerType) {
                case 'freq': // Frequency
                  let textInt = dataLayerOption === 'rel_freq' ? text + optionValue * 100 + "%" : text + optionValue;
                  let textFloat = dataLayerOption === 'rel_freq'? text + (optionValue * 100).toFixed(2) + "%" : text + optionValue.toFixed(2);

                  // 判斷是否為整數，若非整數要取小數點後面兩個值。
                  text = Math.trunc(optionValue) === optionValue ? textInt : textFloat;
                  break;

                case 'duration': // Duration 除了 Relative 為百分比 % ，其他要轉變時間單位。
                  // Relative %
                  let textDurRel = text + (optionValue * 100).toFixed(2) + "%";
                  // Timelabel
                  let timeLabelInt = text + TimeLabel(optionValue);
                  let timeLabelFloat = text + TimeLabel(optionValue.toFixed(2));

                  // 判斷是否為整數，若非整數要取小數點後面兩個值。
                  let textTimeLabel = Math.trunc(optionValue) === optionValue ? timeLabelInt : timeLabelFloat;

                  text = dataLayerOption === 'rel_duration' ? textDurRel : textTimeLabel;
                  break;
              }
            }

            return text
          },
          'text-opacity':0.7,
          'background-color': 'data(backgroundColor)',
          'border-color':'data(bordercolor)',
          'border-width':
          function(node) {
            return node.data('type') === 'activity' ? '1' : '2';
          },
          //'border-radius': '5',
          'shape':'data(shape)',
          'text-wrap': 'wrap',
          'text-max-width':75,
          'text-halign': 'center',
          'text-valign': 'center',
          'height': 'data(height)',
          'width': 'data(width)',
          'color': '#001933',
          'font-size':
          function(node) {
            return node.data('type') === 'activity' ? 14 : 22;
          },
        },
      },
      // edge 關係線的樣式
      {
        selector: 'edge',
        style: {
          'content': function(edge) { // 關係線顯示的文字
            let optionValue = edge.data(`${dataLayerType}.${dataLayerOption}`);
            let result = '';

            if(optionValue === '') return optionValue;

            switch(dataLayerType) {
              case 'freq':
                let edgeInt = dataLayerOption === 'rel_freq' ? optionValue * 100 + "%" : optionValue;
                let edgeFloat = dataLayerOption === 'rel_freq' ? (optionValue * 100).toFixed(2) + "%" : optionValue.toFixed(2);

                // 判斷是否為整數，若非整數要取小數點後面兩個值。
                result = Math.trunc(optionValue) === optionValue ? edgeInt : edgeFloat;
                break;

              case 'duration': // Duration 除了 Relative 為百分比 % ，其他要轉變時間單位。
                // Relative %
                let edgeDurRel = (optionValue * 100).toFixed(2) + "%";
                // Timelabel
                let timeLabelInt = TimeLabel(optionValue);
                let timeLabelFloat = TimeLabel(optionValue.toFixed(2));
                let edgeTimeLabel = Math.trunc(optionValue) === optionValue ? timeLabelInt : timeLabelFloat;

                result = dataLayerOption === 'rel_duration' ? edgeDurRel : edgeTimeLabel;
                break;
            };
            return result;
          },
          'curve-style': curveStyle, // unbundled-bezier | taxi
          'target-arrow-shape': 'triangle', // 指向目標的箭頭形狀: 三角形
          'color': 'gray', //#0066cc
          //'control-point-step-size':100, // 從點到點的垂直線，指定貝茲取線邊緣間的距離
          'width':'data(lineWidth)',
          'line-style':'data(style)',
          "text-margin-y": "15rem",
          //"text-rotation": "autorotate",
        }
      },
    ],
  });

  // creat tippy.js
  let tip;
  cy.on('mouseover', 'node', function(event) {
      var node = event.target
      let ref = node.popperRef()
      let dummyDomEle = document.createElement('div');
      let content = document.createElement('div');
      content.innerHTML = node.data("label")
      tip = new tippy(dummyDomEle, { // tippy props:
          getReferenceClientRect: ref.getBoundingClientRect,
          trigger: 'manual',
          content:content
       });
      if(node.data("label").length > 10) tip.show();
  })
  cy.on('mouseout', 'node', function(event) {
      tip.hide();
  })
}
