var colors=["green", "red", "sienna", "orange", "black", "purple", "chocolate", "olivedrab", "darkred", "darkslategrey", "midnightblue", "maroon", "teal", "goldenrod", "gray", "darkolivegreen", "darkcyan", "brown", "peru", "mediumorchild", "navy", "saddlebrown", "coral"];

function line_color(d)
{
    return colors[d];
}

var aliases = { };

function classToAlias(c)
{
    var alias = aliases[c];

    if (alias == undefined)
    {
        alias = c;
    }

    return alias;
}

function receiver(d, considerports)
{
    var rc = d.dst_ip;

    if (considerports)
    {
        rc += ":" + d.dst_port;
    }

    return classToAlias(rc);
}

function sender(d, considerports)
{
    var rc = d.src_ip;

    if (considerports)
    {
        rc += ":" + d.src_port;
    }

    return classToAlias(rc);
}

function prepareData(data, considerports)
{
    console.log("prepare data");

    data.frames.forEach(function(m, i) {
            m.hidden = false;
            m.drawable = true;
        });

        data.frames.forEach(function(m, i) {

        var i = (sender(m, considerports) == receiver(m, considerports))

        m.internal = i;
        m.drawable = !i;
    });

    return data;
}


function hostPortToClass(c)
{
    return "class-" + classToAlias(c).replace(/\./g, "_").replace(":", "_");
}

function toggleSVG(node)
{
    console.log(node);
    var x = document.getElementsByClassName(node);

    console.log(x.length);
    var i; for (i = 0; i < x.length; i++)
    {
        x[i].style.display = (x[i].style.display == 'none') ? 'block' : 'none';
    }
}

function createSvg(container)
{
    var width = parseInt(d3.select(container).style("width"), 10);

    return d3.select(container).append("svg").attr("width", width );
}

function redraw(data, considerports)
{
    var svg = d3.selectAll("svg");
    svg.remove();
    draw_sequence_diagram(data, considerports);
}

function hidePackets(rawdata, pattern, considerports)
{
    console.log("hide packets", pattern);

    rawdata.frames.forEach(function(item, i)
    {
        if (sender(item, considerports) == pattern || receiver(item, considerports) == pattern)
        {
            item.hidden = !item.hidden;
        }
    });

    redraw(rawdata, considerports);
}

function getNodes(data, considerports)
{
    var senders = d3.set(data.map(function(d){ return sender(d, considerports); })).values();
    var receivers = d3.set(data.map(function(d){ return receiver(d, considerports); })).values();
    return _.union(senders, receivers);
}

function considerPortsClick(rawdata, considerports)
{
    console.log("consider ports click");
    prepareData(rawdata, !considerports);
    redraw(rawdata, !considerports);
}

function draw_sequence_diagram(rawdata, considerports)
{
    var chartName = rawdata.call;
    var data = rawdata.frames;

    function considerPorts()
    {
        considerPortsClick(rawdata, considerports);
    }

    var message_frame = d3.select("#message-frame");

    function showMessageBody(m)
    {
       message_frame.html("<textarea>" + "packet# " + m.id + "\n" + m.payload + "</textarea>");
    }

    function listClasses(m, considerports)
    {
        return hostPortToClass(sender(m, considerports)) + " " +
               hostPortToClass(receiver(m, considerports)) + " " +
               "class-message";
    }

    function classClick(node)
    {
        console.log("class click", node);

        // FIXME reimplement with d3
        // toggleSVG(hostPortToClass(node));
        hidePackets(rawdata, node, considerports);

    }

    function initAliases(nodes, preAliases)
    {
        if (_.isEmpty(aliases))
        {
            var nodes1 = nodes.slice();

            if (typeof preAliases !== 'undefined')
            {
                for (var key in preAliases)
                {
                    var index = nodes1.indexOf(key);
                   if (index != -1)
                   {
                       nodes1[index] = preAliases[key];
                   }
                }
            }

            dictedit.init("nodeEditor", null, nodes, nodes1);
            aliases = dictedit.value("nodeEditor");
            applyNodeEdit();
        }
    }

    var button1 = d3.select("#considerPortsButton").on("click", considerPorts);
    var expand = d3.select("#expandEditor").on("click", expandEditor);
    var apply = d3.select("#applyNodeEdit").on("click", applyNodeEdit);

    function applyNodeEdit() {
        aliases = dictedit.value("nodeEditor");
        redraw(rawdata, considerports);
    };

    function expandEditor() {
        $("#editorDiv").slideToggle(200);
    };

    var header = createSvg("#diagram_header");
    var svg = createSvg("#diagram");

    var margin = { top: 20, right: 20, bottom: 20, left: 20 },
        width = +svg.attr('width') - margin.left - margin.right,
        height = +svg.attr('height') - margin.top - margin.bottom,
        g = header.append('g').attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

     // Graph title
     g.append('text')
        .attr('x', (width / 2))
        .attr('y', 0 - (margin.top / 4))
        .attr('class', 'class-title')
        .text(chartName);

     var nodes = getNodes(data, considerports);

     initAliases(nodes, rawdata.aliases);

     var XPAD = 200; // horizontal padding for vertical lines/messages/labels
     var YPAD = 3;
     var VERT_SPACE = parseInt(width/nodes.length);

     var MESSAGE_SPACE = 25;
     svg.attr("height", (data.length+2)*MESSAGE_SPACE);

     var MESSAGE_LABEL_X_OFFSET = -50;
     var MESSAGE_LABEL_Y_OFFSET = 10;
     var MESSAGE_ARROW_Y_OFFSET = 15;

     var CLASS_WIDTH = 120;
     var CLASS_LABEL_X_OFFSET = -30;
     var CLASS_LABEL_Y_OFFSET = 25;

     // Draw vertical lines
     nodes.forEach(function(c, i) {
          var line = svg.append("line")
          .attr("class", "class-vertical-line")
          .attr("x1", XPAD + i * VERT_SPACE)
          .attr("y1", 0) // YPAD + MESSAGE_SPACE)
          .attr("x2", XPAD + i * VERT_SPACE)
          .attr("y2", YPAD + data.length * (MESSAGE_SPACE + 5))
     });

     // Draw class labels
     nodes.forEach(function(c, i) {
        var x = XPAD + i * VERT_SPACE;
        var g1 = header.append("g")
          .attr("transform", "translate(" + x + "," + YPAD + ")")
          .attr("class", "class-rect")
          .append("rect")
          .attr("x", -CLASS_WIDTH/2)
          .attr("y", "0")
          .attr("width", CLASS_WIDTH)
          .attr("height", "24px")
          .on("click", function() { classClick(c) })

        var g2 = header.append("g")
          .attr("transform", "translate(" + x + "," + YPAD + ")")
          .append("text")
          .attr("class", "class-label")
          .attr("text-anchor", "middle")
          .text(function (d) { return classToAlias(c); })
          .attr("dy", "16px")
          .on("click", function() { classClick(c) })
      });

      var i = -1;
      var message_number = 0;

      data.forEach(function(m, c) {

      // FIXME var lcolor = line_color(m.session_id);
      var lcolor = line_color(0);
      var tcolor = "black";

      if (!m.drawable)
      {
          return;
      }

      i++;

      if (m.hidden)
      {
          lcolor = "lightgrey";
          tcolor = "lightgrey";
      }

      // draw packet number
      var xPos = 0;
      var yPos = MESSAGE_LABEL_Y_OFFSET + i * MESSAGE_SPACE;
      var classes = listClasses(m, considerports);

      var g1 = svg.append("g")
          .attr("transform", "translate(" + xPos + "," + yPos + ")")
          .attr("text-anchor", "left")
          .attr("class", classes)
          .attr("class", "message-number")
          .append("text")
          .style("fill", tcolor)
          .text(message_number++);


      // draw timestamp
      var xPos = XPAD/2;
      var yPos = MESSAGE_LABEL_Y_OFFSET + i * MESSAGE_SPACE;

      g1 = svg.append("g")
          .attr("transform", "translate(" + xPos + "," + yPos + ")")
          .attr("text-anchor", "middle")
          .attr("class", classes)
          .append("text")
          .style("fill", tcolor)
          .style("font-size", "10px")
          .text(m.timestamp);

      // draw message labels
      xPos = XPAD + MESSAGE_LABEL_X_OFFSET + (((nodes.indexOf(receiver(m, considerports)) - nodes.indexOf(sender(m, considerports))) * VERT_SPACE) / 2) + (nodes.indexOf(sender(m, considerports))  * VERT_SPACE);
      yPos = MESSAGE_LABEL_Y_OFFSET + i * MESSAGE_SPACE;

      // draw message labels
      g1 = svg.append("g")
          .attr("transform", "translate(" + xPos + "," + yPos + ")")
          .append("text")
          .attr("dx", "5px")
          .attr("dy", "-2px")
          .attr("text-anchor", "begin")
          .style("fill", tcolor)
          .attr("class", classes)
          .on("click", function() { showMessageBody(m) })
          .text(m.method + " " + m.request_uri);

      // draw line
      var y = MESSAGE_ARROW_Y_OFFSET + (i) * MESSAGE_SPACE;
      var line = svg.append("line")
          .style("stroke", lcolor)
          .attr("x1", XPAD + nodes.indexOf(sender(m, considerports)) * VERT_SPACE)
          .attr("y1", y)
          .attr("x2", XPAD + nodes.indexOf(receiver(m, considerports)) * VERT_SPACE)
          .attr("y2", y)
          .attr("marker-end", "url(#end)")
          .on("click", function() { showMessageBody(m) })

          if (m.hidden)
          {
            line.attr("marker-end", "url(#hidden)");
          }
          else
          {
            line.attr("marker-end", "url(#end)");
          }
      });

      // Arrow style
      svg.append("svg:defs").selectAll("marker")
          .data(["end"])
        .enter().append("svg:marker")
          .attr("id", String)
          .attr("viewBox", "0 -5 10 10")
          .attr("refX", 10)
          .attr("refY", 0)
          .attr("markerWidth", 10)
          .attr("markerHeight", 10)
          .attr("orient", "auto")
          .attr("fill", "blue")
        .append("svg:path")
          .style("stroke", "blue")
          .attr("d", "M0,-3L10,0L0,3");

      // Arrow style
      svg.append("svg:defs").selectAll("marker")
          .data(["hidden"])
        .enter().append("svg:marker")
          .attr("id", String)
          .attr("viewBox", "0 -5 10 10")
          .attr("refX", 10)
          .attr("refY", 0)
          .attr("markerWidth", 10)
          .attr("markerHeight", 10)
          .attr("orient", "auto")
          .attr("fill", "lightgrey")
        .append("svg:path")
          .style("stroke", "lightgrey")
          .attr("d", "M0,-3L10,0L0,3");

    showMessageBody(data[0]);
}
