/**
 * © OpenCORD
 *
 * Visit http://guide.xosproject.org/devguide/addview/ for more information
 *
 * Created by teone on 6/28/16.
 */

(function () {
  'use strict';
  angular.module('xos.ecordTopology')
  .directive('serviceMap', function(){
    return {
      restrict: 'E',
      scope: {
        unis: '=',
        services: '='
      },
      bindToController: true,
      controllerAs: 'vm',
      template: '',
      controller: function($element, $scope, $rootScope, $timeout, $log, cordIcons){
        const el = $element[0];
        const layout = d3.layout.tree();
        let duration = 500;
        let margin = 40;

        // count the mas depth of an object
        const depthOf = (obj) => {
          var depth = 0;
          if (obj.children) {
            obj.children.forEach(function (d) {
              var tmpDepth = depthOf(d);
              if (tmpDepth > depth) {
                depth = tmpDepth
              }
            })
          }
          return 1 + depth
        };

        // create svg elements
        const svg = d3.select(el)
          .append('svg')
          .style('width', `${el.clientWidth}px`)
          .style('height', `${el.clientHeight}px`)

        var diagonal = d3.svg.diagonal()
          .projection(function(d) { return [d.y, d.x]; });

        let i = 0;
        const update = (tree) => {

          let maxDepth = depthOf(tree);

          layout
            .size([el.clientHeight, el.clientWidth]);

          var nodes = layout.nodes(tree);
          var links = layout.links(nodes);

          const step = ((el.clientWidth - margin) / (maxDepth - 1));
          // Normalize for fixed-depth.
          nodes.forEach(function(d, i) {
            if(i === 0){
              d.y = margin;
            }
            else{
              d.y = d.depth * step;
            }
          });

          // Update the nodes…
          var node = svg.selectAll('g.node')
            .data(nodes, (d) =>  {
              return d.id || (d.id = ++i)
            });


          // Enter any new nodes at the parent's previous position.
          var nodeEnter = node.enter().append('g')
            .attr({
              'class': d => `node ${d.type}`,
              id: d => d.id
            })
            .attr('transform', () => `translate(${el.clientWidth / 2}, 50)`);

          nodeEnter.append('rect')
            .attr({
              class: d => d.type,
              width: 24,
              height: 24,
              x: -12,
              y: -12
            });

          // unis
          nodeEnter.filter('.uni')
            .append('path')
            .attr({
              d: cordIcons.cordLogo,
              transform: 'translate(-10, -10),scale(0.18)'
            });

          // services
          nodeEnter.filter('.service')
            .append('path')
            .attr({
              d: cordIcons.service,
              transform: 'translate(-12, -12)'
            });

          nodeEnter.append('text')
            .attr({
              'text-anchor': 'middle',
              x: 0,
              y: 25
            })
            .text(d => {
              return d.name
            });

          // Transition exiting nodes to the parent's new position.
          var nodeExit = node.exit().transition()
            .duration(duration)
            .remove();

          nodeExit.select('circle')
            .attr('r', 1e-6);

          nodeExit.select('text')
            .style('fill-opacity', 1e-6);

          var nodeUpdate = node.transition()
            .duration(duration)
            .attr('transform', (d) => `translate(${d.y},${d.x})`);

          // Update the links…
          var link = svg.selectAll('path.link')
            .data(links, function(d) { return d.target.id; });

          // Enter any new links at the parent's previous position.
          link.enter().insert('path', 'g')
            .attr('class', 'link')
            .attr('d', function(d) {
              var o = {x: d.source.x, y: d.source.y};
              return diagonal({source: o, target: o});
            });

          // Transition links to their new position.
          link.transition()
            .duration(duration)
            .attr('d', diagonal);

          // Transition exiting nodes to the parent's new position.
          link.exit().transition()
            .duration(duration)
            .attr('d', function(d) {
              var o = {x: d.source.x, y: d.source.y};
              return diagonal({source: o, target: o});
            })
            .remove();

          // Stash the old positions for transition.
          nodes.forEach(function(d) {
            d.x0 = d.x;
            d.y0 = d.y;
          });
        };

        // format uni in the tree layout shape
        this.formatUni = (unis) => {
          return unis.reduce((list, item, i) => {
            list.push({
              name: item.scaEthFppUniN.name,
              children: [],
              id: `uni-${i}`
            });
            return list;
          }, [])
          return unis;
        }

        // add active services
        this.addServices = (services, unis) => {

          let list = [unis[0], ...services, unis[1]];


          const addChildR = (base, list) => {

            if(list.length === 0){
              return [];
            }

            let el = list.shift();

            let n = {
              name: el.name || el.label,
              type: el.name ? 'uni':'service',
              children: addChildR(el, list),
              id: el.id
            };

            return [n];
          };

          let tree = addChildR({}, list);

          return tree[0];
        };

        $scope.$watch(() => this.services, (s) => {
          if(s && this.unis){
            update(this.addServices(s, this.formatUni(this.unis)))
          }
        }, true)
      }
    };
  });
})();

