<template>
  <div class="column">
    <div id="FeatureGraph1"></div>
  </div>
</template>

<script>
import { EventBus } from '../main.js'
import * as greadability from '../greadability.js'
import * as d3Base from 'd3'

// attach all d3 plugins to the d3 library
const d3 = Object.assign(d3Base)

export default {
  name: 'FeatureSpace1',
  data () {
    return {
      dataFS1: [],
      dataFS1Trans: [],
      jsonData: [],
      corrMatrixComb: [],
      corrMatrixCombTotal: [],
      VIFRemaining: [],
      MIRemaining: [],
    }
  },
  methods: {
      initializeNetwork () {   
        
        var listofNodes = this.dataFS1[0]
        var dataLoc = JSON.parse(this.dataFS1[2])
        var pushEachFinal = []
        var pushEach
        var oldVal
        var newVal
        var outcome
        var countLoc

        var corrMatrixCombLoc =[]
        var corrMatrixCombTotalLoc = []
        var VIFRemainingLoc = []
        var MIRemainingLoc = []

        this.dataFS1Trans.forEach(function(element, index) {
          var round = element.round
          corrMatrixCombLoc.push(Object.values(JSON.parse(round[4])))
          corrMatrixCombTotalLoc.push(Object.values(JSON.parse(round[8]))[0] * 100)
          VIFRemainingLoc.push(Object.values(JSON.parse(round[12]))[0])
          MIRemainingLoc.push(JSON.parse(round[16]))
          round = JSON.parse(round[0])
          oldVal = 0
          newVal = 0
          outcome = 0
          countLoc = 0
          pushEach = []
          Object.entries(round).forEach(
          function ([feature, value]) {
            var key = Object.keys(value)
            var search = Object.values(dataLoc[key])
            oldVal = Math.abs(search[countLoc]) + oldVal
            newVal = Math.abs(Object.values(value)[0]) + newVal
            countLoc++
          })
          oldVal = oldVal / listofNodes.length
          newVal = newVal / listofNodes.length
          outcome = oldVal - newVal
          pushEach.push({keyIns: "round", valueIns: outcome})

          var roundLogE = element.roundLogE
          corrMatrixCombLoc.push(Object.values(JSON.parse(roundLogE[4])))
          corrMatrixCombTotalLoc.push(Object.values(JSON.parse(roundLogE[8]))[0] * 100)
          VIFRemainingLoc.push(Object.values(JSON.parse(roundLogE[12]))[0])
          MIRemainingLoc.push(JSON.parse(roundLogE[16]))
          roundLogE = JSON.parse(roundLogE[0])
          oldVal = 0
          newVal = 0
          outcome = 0
          countLoc = 0
          Object.entries(roundLogE).forEach(
          function ([feature, value]) {
            var key = Object.keys(value)
            var search = Object.values(dataLoc[key])
            oldVal = Math.abs(search[countLoc]) + oldVal
            newVal = Math.abs(Object.values(value)[0]) + newVal
            countLoc++
          })
          oldVal = oldVal / listofNodes.length
          newVal = newVal / listofNodes.length
          outcome = oldVal - newVal
          pushEach.push({keyIns: "roundLogE", valueIns: outcome})

          var roundLog2 = element.roundLog2
          corrMatrixCombLoc.push(Object.values(JSON.parse(roundLog2[4])))
          corrMatrixCombTotalLoc.push(Object.values(JSON.parse(roundLog2[8]))[0] * 100)
          VIFRemainingLoc.push(Object.values(JSON.parse(roundLog2[12]))[0])
          MIRemainingLoc.push(JSON.parse(roundLog2[16]))
          roundLog2 = JSON.parse(roundLog2[0])
          oldVal = 0
          newVal = 0
          outcome = 0
          countLoc = 0
          Object.entries(roundLog2).forEach(
          function ([feature, value]) {
            var key = Object.keys(value)
            var search = Object.values(dataLoc[key])
            oldVal = Math.abs(search[countLoc]) + oldVal
            newVal = Math.abs(Object.values(value)[0]) + newVal
            countLoc++
          })
          oldVal = oldVal / listofNodes.length
          newVal = newVal / listofNodes.length
          outcome = oldVal - newVal
          pushEach.push({keyIns: "roundLog2", valueIns: outcome})

          var roundLog10 = element.roundLog10
          corrMatrixCombLoc.push(Object.values(JSON.parse(roundLog10[4])))
          corrMatrixCombTotalLoc.push(Object.values(JSON.parse(roundLog10[8]))[0] * 100)
          VIFRemainingLoc.push(Object.values(JSON.parse(roundLog10[12]))[0])
          MIRemainingLoc.push(JSON.parse(roundLog10[16]))
          roundLog10 = JSON.parse(roundLog10[0])
          oldVal = 0
          newVal = 0
          outcome = 0
          countLoc = 0
          Object.entries(roundLog10).forEach(
          function ([feature, value]) {
            var key = Object.keys(value)
            var search = Object.values(dataLoc[key])
            oldVal = Math.abs(search[countLoc]) + oldVal
            newVal = Math.abs(Object.values(value)[0]) + newVal
            countLoc++
          })
          oldVal = oldVal / listofNodes.length
          newVal = newVal / listofNodes.length
          outcome = oldVal - newVal
          pushEach.push({keyIns: "roundLog10", valueIns: outcome})
          pushEachFinal.push({key: listofNodes[index], value: pushEach})
          })

        this.corrMatrixComb = [...corrMatrixCombLoc]
        this.corrMatrixCombTotal = [...corrMatrixCombTotalLoc]
        this.VIFRemaining = [...VIFRemainingLoc]
        this.MIRemaining = [...MIRemainingLoc]

        var nodes = []
        var groupID = 0
        listofNodes.forEach( function(element) {
          groupID++
          nodes.push({"name": element, "group": groupID})
        })

        var links = []

        Object.entries(dataLoc).forEach(
            function ([feature, value]) {
              Object.entries(value).forEach( function ([featureInside, value]) {
                if (feature != featureInside) { 
                  if (value >= 0) {
                    links.push({"source": listofNodes.indexOf(feature), "target": listofNodes.indexOf(featureInside), "value": Math.abs(value) * 30, "lin_color": "#D3D3D3"})
                  }
                }   
              })
        })

        groupID = 0
        listofNodes.forEach( function(element) {
          groupID++
          nodes.push(
            {"name": element+"_r", "group": groupID},
            {"name": element+"_r_E", "group": groupID},
            {"name": element+"_r_2", "group": groupID},
            {"name": element+"_r_10", "group": groupID},
          )
        })

        Object.entries(pushEachFinal).forEach(
            function ([index, feature]) {
              feature.value.forEach(function (element, indexIns) {
                if (element.valueIns > 0) {
                  links.push({"source": index, "target": index*feature.value.length+feature.value.length+indexIns, "value": Math.abs(element.valueIns) * 30, "lin_color": "#4393c3"})
                }
                else {
                  links.push({"source": index, "target": index*feature.value.length+feature.value.length+indexIns, "value": Math.abs(element.valueIns) * 30, "lin_color": "#d6604d"})
                }
              })
        })

        this.jsonData = {"nodes": nodes, "links": links};

        this.graphVizualization()

      },
      graphVizualization () {

        var listofNodes = this.dataFS1[0]
        var uniqueTarget = JSON.parse(this.dataFS1[14])
        var corrTarget = JSON.parse(this.dataFS1[6])
        var corrGlob = JSON.parse(this.dataFS1[10])
        var VIFVar = JSON.parse(this.dataFS1[18])
        var MIVar = JSON.parse(this.dataFS1[22])
        MIVar = MIVar.concat(this.MIRemaining)
        var colorCateg = d3.scaleOrdinal(d3.schemeAccent)

        var corrTargetFormatted = []
        for (let i = 0; i < Object.keys(corrTarget).length; i++) {
          var corrTargetFormattedLoc = []
          for (let j = 0; j < Object.keys(corrTarget[i]).length; j++) {
            if (j > uniqueTarget.length - 1) {
              corrTargetFormattedLoc.push(Math.round(Object.values(corrTarget[i])[j] * 100))
            }
          }
          corrTargetFormatted.push(corrTargetFormattedLoc) 
        }

        for (let j = 0; j < uniqueTarget.length; j++) {
          for (let i = 0; i < Object.keys(this.corrMatrixComb).length; i++) {
            if (Math.round(Object.values((Object.values(this.corrMatrixComb[i])[j]))[0] * 100) == null) {
              corrTargetFormatted[j].push(0)
            } else {
              corrTargetFormatted[j].push(Math.round(Object.values((Object.values(this.corrMatrixComb[i])[j]))[0] * 100))
            }
          }
        }

        var corrGlobFormatted = []
        for (let i = 0; i < Object.keys(corrGlob).length; i++) {
          if (i != 0) {
            corrGlobFormatted.push(Math.round(Object.values(corrGlob)[i]['0'] * 100))
          }
        }

        for (let i = 0; i < this.corrMatrixCombTotal.length; i++) {
            corrGlobFormatted.push(Math.round(this.corrMatrixCombTotal[i]))
        }

        var VIFVarFormatted = []
        for (let i = 0; i < Object.keys(VIFVar).length; i++) {
          if (i != 0) {
            if (Object.values(VIFVar)[i] > 10) {
              VIFVarFormatted.push(4)
            } else if (Object.values(VIFVar)[i] > 5) {
              VIFVarFormatted.push(2)
            } else {
              VIFVarFormatted.push(0)
            }
          }
        }

        for (let i = 0; i < this.VIFRemaining.length; i++) {
            if (this.VIFRemaining[i] > 10) {
              VIFVarFormatted.push(4)
            } else if (this.VIFRemaining[i] > 5) {
              VIFVarFormatted.push(2)
            } else {
              VIFVarFormatted.push(0)
            }
          }

        function min(input) {
            if (toString.call(input) !== "[object Array]")  
              return false;
          return Math.min.apply(null, input);
        }

        function max(input) {
            if (toString.call(input) !== "[object Array]")  
              return false;
          return Math.max.apply(null, input);
        }

        function normalize(min, max) {
            var delta = max - min;
            return function (val) {
                return (val - min) / delta;
            };
        }

        var MIMin = min(MIVar)
        var MIMax = max(MIVar)

        MIVar = MIVar.map(normalize(MIMin, MIMax))

        var colorsScaleNodes = d3.scaleOrdinal()
          .domain(d3.ticks([MIMin, MIMax, 10]))
          .range(['#67001f','#b2182b','#d6604d','#f4a582','#fddbc7','#d1e5f0','#92c5de','#4393c3','#2166ac','#053061']);

        var svg = d3.select("#FeatureGraph1");
        svg.selectAll("*").remove();

        var width = 1000;
        var height = 500;

        var numTicks = 200;

        var selectedParams;
        var bestParams;

        var dispatch = d3.dispatch('layoutend');

        svg = d3.select("#FeatureGraph1").append("svg")
          .attr("width", width)
          .attr("height", height);

        var graph = this.jsonData

        var link = svg.append('g')
          .attr('class', 'links')
          .selectAll('line')
          .data(graph.links)
          .enter().append('line')
          .attr("stroke", function(d) { return d.lin_color; })
          .attr("stroke-width", function(d) { return Math.sqrt(d.value); });
          

        var node = svg.append('g')
          .attr('class', 'nodes')
          .selectAll('g')
          .data(graph.nodes)
          .enter().append('g')

        var paramGroups = [
          {name: 'chargeStrength', values: [-30, -80]},
          {name: 'linkDistance', values: [30, -80]},
          {name: 'linkStrength', values: [0, 1]},
          {name: 'gravity', values: [0, 0.5]},
          {name: 'iterations', values: [1, 2]},
          {name: 'alphaDecay', values: [0, 0.0228, 0.05]},
          {name: 'velocityDecay', values: [0.4, 0.8]}
        ];

        var paramList = generateParams(paramGroups);

        var bestSoFar = d3.select('.best').selectAll('li')
          .data(paramGroups.map(function (d) { return d.name; }))
          .enter().append('li')
          .text(function (d) { return d; });

        dispatch.on('layoutend', function (params, i) {
          if (!bestParams || params.graphReadability > bestParams.graphReadability) {
            bestParams = params;
            selectedParams = bestParams;

            bestSoFar
              .data(d3.map(bestParams).keys().filter(function (d) { return d !== 'positions' && d !== 'graphReadability'; }))
              .text(function (d) { return d + ' = ' + bestParams[d]; });
          }

        });

        var i = 0;
        var stepper = d3.timer(function () {
          var p = paramList[i];
          var forceSim = getForceSimFromParams(p);

          // Reset node attributes.
          graph.nodes.forEach(function (n) {
            n.x = n.y = n.vx = n.vy = 0;
          });

          forceSim.nodes(graph.nodes)
            .stop();

          forceSim.force('link')
            .links(graph.links);

          for (var t = 0; t < numTicks; ++t) {
            forceSim.tick();
          }

          p.graphReadability = greadability.greadability(graph.nodes, graph.links);
          p.graphReadability = (p.graphReadability.crossing + p.graphReadability.crossingAngle +
          p.graphReadability.angularResolutionMin + p.graphReadability.angularResolutionDev) / 4

          p.positions = graph.nodes.map(function (n) { return {x: n.x, y: n.y}; });

          dispatch.call('layoutend', forceSim, p, i);

          ++i;
          if (i >= paramList.length) {
            
            var widthLoc = 100;
            var arcSize = (6 * widthLoc / 100);
            var innerRadius = arcSize * 2; 

            var svgLoc = node.append('svg').attr('width', widthLoc).attr('height', widthLoc)
              .attr("id", function(d, i) { return "svg" + (i); })

            graph.nodes.forEach(function(itemVal, indexNode) {
              
              var data = []

              for(let k = 0; k < uniqueTarget.length; k++) {
                data.push({value: corrTargetFormatted[k][indexNode], label: '', color: colorCateg(uniqueTarget[k]), lcolor: ''})
              }

              var length = data.length
              data.push({value: 100, label: corrGlobFormatted[indexNode], color: '#000000', lcolor: colorsScaleNodes(MIVar[indexNode])})


              var border = VIFVarFormatted[indexNode]

                  var arcs = data.map(function (obj, i) {
                    if (i == length) {
                      return d3.arc().innerRadius(i * arcSize + innerRadius).outerRadius((i + 1) * arcSize - (widthLoc / 100) + innerRadius + border);
                    } else {
                      return d3.arc().innerRadius(i * arcSize + innerRadius).outerRadius((i + 1) * arcSize - (widthLoc / 100) + innerRadius);
                    }
                  });
                  
                  var arcsGrey = data.map(function (obj, i) {
                      return d3.arc().innerRadius(i * arcSize + (innerRadius + ((arcSize / 2) - 2))).outerRadius((i + 1) * arcSize - ((arcSize / 2)) + (innerRadius));
                  });

                  var pieData = data.map(function (obj, i) {
                      return [
                          {value: obj.value, arc: arcs[i], object: obj},
                          {value: (100 - obj.value), arc: arcsGrey[i], object: obj},
                          {value: 0, arc: arcs[i], object: obj}];
                  });

                  var pie = d3.pie().sort(null).value(function (d) {
                      return d.value;
                  });
                  var id = indexNode
                  var g = d3.select('#svg'+id).selectAll('g').data(pieData).enter()
                      .append('g')
                      .attr('transform', 'translate(' + widthLoc / 2 + ',' + widthLoc / 2 + ') rotate(180)');
                  var gText = d3.select('#svg'+id).selectAll('g.textClass').data([{}]).enter()
                      .append('g')
                      .classed('textClass', true)
                      .attr('transform', 'translate(' + widthLoc / 2 + ',' + widthLoc / 2 + ') rotate(180)');

                  g.selectAll('path').data(function (d) {
                      return pie(d);
                  }).enter().append('path')
                      .attr('id', function (d, i) {
                          if (i == 1) {
                              return "Text" + d.data.object.label
                          }
                      })
                      .attr('d', function (d) {
                          return d.data.arc(d);
                      }).attr('fill', function (d, i) {
                      return i == 0 ? d.data.object.color : i == 1 ? '#D3D3D3' : 'none';
                  });

                  g.each(function (d, index) {

                      var el = d3.select(this);
                      var path = el.selectAll('path').each(function (r, i) {
                          if (i === 1) {
                              var centroid = r.data.arc.centroid({
                                  startAngle: r.startAngle + 0.05,
                                  endAngle: r.startAngle + 0.001 + 0.05
                              });
                              var lableObj = r.data.object;
                              g.append('text')
                                  .attr('font-size', ((2 * width) / 100))
                                  .attr('dominant-baseline', 'central')
                                  .append("textPath")
                                  .attr("textLength", function (d, i) {
                                      return 0;
                                  })
                                  .attr("startOffset", '5')
                                  .attr("dy", '-3em')
                                  .text(lableObj.value + '%');
                          }
                          if (i === 0) {
                              var centroidText = r.data.arc.centroid({
                                  startAngle: r.startAngle,
                                  endAngle: r.startAngle
                              });
                              var lableObj = r.data.object;
                              gText.append('text')
                                  .attr('font-size', ((2 * width) / 100))
                                  .text(lableObj.label)
                                  .style('fill', lableObj.lcolor)
                                  .attr('transform', "translate(" + (10) + "," + (0 + ") rotate(" + (180) + ")"))
                                  .attr('dominant-baseline', 'central');
                          }
                      });

                  });

                })

                var drag_handler = d3.drag()
                  .on("start", drag_start)
                  .on("drag", drag_drag)
                  .on("end", drag_end);	
                  
                drag_handler(node);

                var labels = node.append("text")
                  .text(function(d) {
                    return d.name;
                  })
                  .attr('x', 20)
                  .attr('y', 8);
            
                node.append('title').text(function (d) { return d.name; });

                //add zoom capabilities 
                var zoom_handler = d3.zoom()
                    .on("zoom", zoom_actions);

                zoom_handler(svg); 

                drawGraph();

                //Zoom functions 
                function zoom_actions(){
                    svg.attr("transform", d3.event.transform)
                }

                function drag_start(d) {
                  if (!d3.event.active) forceSim.alphaTarget(0.3).restart();
                      d.fx = d.x;
                      d.fy = d.y;
                  }

                  //make sure you can't drag the circle outside the box
                  function drag_drag(d) {
                    d.fx = d3.event.x;
                    d.fy = d3.event.y;
                    tickActions();
                  }

                  function drag_end(d) {
                    if (!d3.event.active) forceSim.alphaTarget(0);
                    d.fx = null;
                    d.fy = null;
                  }

                stepper.stop();
          }

        });

        function tickActions() {
          link
            .attr('x1', function (d) { return d.source.x; })
            .attr('x2', function (d) { return d.target.x; })
            .attr('y1', function (d) { return d.source.y; })
            .attr('y2', function (d) { return d.target.y; });

          node
            .attr("id", function(d, i) { return "g" + (i); })
            .attr("transform", function(d, i) {
              d.x = d.x - 50
              d.y = d.y - 50
              return "translate(" + d.x + "," + d.y + ")";
            })
        };

        function drawGraph () {
          graph.nodes.forEach(function (n, i) {
            n.x = selectedParams.positions[i].x;
            n.y = selectedParams.positions[i].y;
          });

          var xDistance = d3.extent(graph.nodes, function (n) { return n.x; });
          var xMin = xDistance[0];
          xDistance = xDistance[1] - xDistance[0];

          var yDistance = d3.extent(graph.nodes, function (n) { return n.y; });
          var yMin = yDistance[0];
          yDistance = yDistance[1] - yDistance[0];

          graph.nodes.forEach(function (n, i) {
            n.x = (height - 10) * (n.x - xMin) / Math.max(xDistance, yDistance);
            n.y = (height - 10) * (n.y - yMin) / Math.max(xDistance, yDistance);
          });

          xDistance = d3.extent(graph.nodes, function (n) { return n.x; });
          var xMid = (xDistance[1] + xDistance[0]) / 2;
          yDistance = d3.extent(graph.nodes, function (n) { return n.y; });
          var yMid = (yDistance[1] - yDistance[0]) / 2;

          graph.nodes.forEach(function (n, i) {
            n.x = n.x + width/2 - xMid;
            n.y = n.y + height/2 - yMid;
          });

          tickActions();
        }

        function generateParams (paramGroups, paramList, currParam) {
          var p = paramGroups[0];

          if (!paramList) paramList = [];

          if (!currParam) currParam = {};

          p.values.forEach(function (v) {
            var setting = {};
            setting[p.name] = v;
            if (paramGroups.length === 1) {
              paramList.push(Object.assign(setting, currParam));
            } else {
              generateParams(paramGroups.slice(1), paramList, Object.assign(setting, currParam));
            }
          });

          return paramList;
        }

        function getForceSimFromParams (params) {
          var forceSim = d3.forceSimulation()
            .force('link', d3.forceLink().iterations(params.iterations))
            .force('charge', d3.forceManyBody().strength(params.chargeStrength))
            .force('x', d3.forceX(0).strength(params.gravity))
            .force('y', d3.forceY(0).strength(params.gravity))
            .force('center', d3.forceCenter(0, 0))
            .alphaDecay(params.alphaDecay)
            .velocityDecay(params.velocityDecay);

          if (params.linkStrength !== null) {
            forceSim.force('link').strength(params.linkStrength);
          }

          return forceSim;
        }

      },
      reset () {
        var svg = d3.select("#FeatureGraph1");
        svg.selectAll("*").remove();
      },
    },
  mounted () {
    EventBus.$on('quadTrans1', data => { this.dataFS1Trans = data })
    EventBus.$on('quad1', data => { this.dataFS1 = data })
    EventBus.$on('quad1', this.initializeNetwork)
  }
}
</script>

<style>

text {
  font-family: sans-serif;
}

svg {
  display: block;
}

.links line {
  stroke-opacity: 0.6;
}

.column {
  float: left;
  margin: 0 10px;
}

</style>