(function () {
  'use strict'

  var app = angular.module('app');

  app.controller('graphCtrl', function($scope, $http, appConfig, restClient) {

    var client = restClient;
    var paper = null;
    var graph = null;
    var graphData = null;

    var canvas = $('#graph');

    var resize = function() {
      return;
      paper.setDimensions(canvas.width(), canvas.height());
    };

    var drawGraph = function() {

      if(!graphData) return;

      canvas.html('');

      jsPlumb.ready(function () {

        var j = jsPlumb.getInstance({Container:canvas, Connector:"StateMachine", Endpoint:["Dot", {radius:3}], Anchor:"Center"});

        var templates = {};
        $http({
            url: "/views/templates.html"
        }).then(function (response) {

          let { data, status, headers, config } = response;

          /* map of elements to render */
          var components = {};
          /* graph margins */
          var marginLeft = 20;
          var marginTop = 20;

          data = $.parseHTML(data);
          $(data).children().each(function(i,c) {
            var id = $(c).attr('id');
            $(c).attr('id', null);
            var src = $('<div>').append($(c).clone()).html();
            templates[id] = src;
          });

          function render(type, params, nondraggable) {
            if(!params['type']) {
              params['type'] = type;
            }
            var src = templates[type];
            if(!src) {
              console.log("ERROR: Unable to find template:", type)
            }
            for(var key in params) {
              src = src.replace('{{' + key + '}}', params[key]);
            }
            var el = $.parseHTML(src)[0];
            el.attrs = params;
            if(params['parent']) {
              var parent = components[params['parent']];
              $(parent).find('.children').append(el);
            } else {
              canvas.append(el);
            }
            if(!nondraggable) {
              j.draggable(el);
            }
            return el;
          }

          function connect(el1, el2, invisible) {
            j.connect({
              source: el1, target: el2,
              anchor:[ "Continuous", {
                faces:["top", "bottom", "left", "right"]
              }],
              overlays: [
                  [ "PlainArrow", { location: 1 }, { cssClass: invisible ? "invisible" : "" } ],
                  [ "Label", { cssClass: "TODO" } ]
              ],
              cssClass: invisible ? "invisible" : ""
            });
          }

          function layout() {

            // construct dagre graph from JsPlumb graph
            var g = new dagre.graphlib.Graph();
            g.setGraph({
              'rankdir': 'LR',
              'nodesep': 30,
              'ranksep': 70
            });
            g.setDefaultEdgeLabel(function() { return {}; });
            var nodes = $(".plumb");
            nodes.each(function(i,n) {
                var n = nodes[i];
                var width = $(n).width();
                var height = $(n).height();
                g.setNode(n.id, { width: width, height: height });
            });
            var edges = j.getAllConnections();
            for (var i = 0; i < edges.length; i++) {
                var c = edges[i];
                g.setEdge(c.source.id, c.target.id );
            }
            dagre.layout(g);
            // Applying the calculated layout
            g.nodes().forEach(function(v) {
              $("#" + v).css("left", marginLeft + (g.node(v).x - ($("#" + v).width() / 2)) + "px");
              $("#" + v).css("top", marginTop + (g.node(v).y - ($("#" + v).height() / 2)) + "px");
            });
          }

          function isConnected(node, edges) {
            for(var i = 0; i < edges.length; i ++) {
              var edge = edges[i];
              if(edge.target == node.id || edge.source == node.id) {
                return true;
              }
            }
            return false;
          }

          var hideDisconnected = $scope.settings.hideDisconnected;
          graphData.nodes.forEach(function(node) {
            if(!hideDisconnected || node.parent || isConnected(node, graphData.edges)) {
              var el = render(node.type, node);
              components[node.id] = el;
            }
          });
          graphData.edges.forEach(function(edge) {
            var src = components[edge.source];
            var tgt = components[edge.target];
            connect(src, tgt);
          });

          function repaint () {
            /* repainting a single time does not seem to work */
            setTimeout(function(){ for(var i = 0; i < 5; i ++) { j.repaintEverything(); } });
          }

          layout();

          $scope.selection.obj = null;
          $(".selectnode").mousedown(function(e) {
            $(".selected").removeClass("selected");
            var node = $(e.target).closest('.layoutnode').get(0);
            $(node).addClass("selected");
            var selectionNode = $(e.target).closest('.selectnode').get(0);
            $(selectionNode).addClass("selected");
            $scope.selection.obj = selectionNode;
            $scope.$parent.$parent.$apply();
          });

          $("#graph").mousedown(function(e) {
            $(".selected").removeClass("selected");
            $scope.selection.obj = null;
            $scope.$parent.$parent.$apply();
          });

          $(".show_hide").click(function(e){
            $(e.target).closest(".layoutnode").find(".children").toggle();
            var val = $(e.target).text();
            if(val.indexOf('show') >= 0) {
              val = val.replace('show', 'hide');
            } else {
              val = val.replace('hide', 'show');
            }
            $(e.target).text(val);
            repaint();
          });

          repaint();

        });

      });

      return;
    };

    $scope.actions.loadGraph = function() {
      graphData = null;
      client.then(function(client) {
        $scope.loading = true;
        $scope.status = null;

        var params = {
          nameFilter: $scope.settings.nameFilter,
          awsEnvironment: $scope.settings.localEndpoints ? 'dev' : 'prod'
        };
        client.default.getGraph({request: params}).then(function(obj) {
          $scope.loading = false;
          graphData = obj.obj
          drawGraph();
          $scope.$apply();
        }, function(err) {
          $scope.loading = false;
          $scope.status = "An error has occurred, could not load data from the service.";
          $scope.$apply();
        });

        $scope.$apply();
      });
    };

    /* re-draw graph on settings change */
    $scope.$watch('settings.hideDisconnected', function(newValue) {
      $scope.selection.obj = null;
      drawGraph();
    });

    $scope.actions.loadGraph();

  });

})();
