{% extends "workflow/viewport-element.html" %}
{% block extrahead %}
<link href="/static/css/graph_common.css" rel="stylesheet">
<title>Pod Definition Prototype</title>

<!-- Loads and initializes the library -->
<script>
    var mxLoadStylesheets = false;
</script>
<script type="text/javascript" src="/static/js/mxClient.min.js" ></script>
<style>
p {
    word-break: normal;
    white-space: normal;
}
</style>
<script type="text/javascript">
var currentWindow;
var currentGraph;
var netCount = 0;
var netColors = ['red', 'blue', 'purple', 'green', 'orange', '#8CCDF5', '#1E9BAC'];
var hostCount = 0;
var lastHostBottom = 100;
var networks = new Set([]);
var has_public_net = false;

function main(graphContainer, overviewContainer, toolbarContainer) {
    //check if the browser is supported
    if (!mxClient.isBrowserSupported()) {
        mxUtils.error('Browser is not supported', 200, false);
        return null;
    }

    // Workaround for Internet Explorer ignoring certain styles
    if (mxClient.IS_QUIRKS) {
        document.body.style.overflow = 'hidden';
        new mxDivResizer(graphContainer);
    }
    var editor = new mxEditor();
    var graph = editor.graph;
    var model = graph.getModel();
    editor.setGraphContainer(graphContainer);

    doGlobalConfig(graph);
    currentGraph = graph;

    {% if xml %}
        restoreFromXml('{{xml|safe}}', editor);
    {% elif hosts %}
        {% for host in hosts %}
        var host = {{host|safe}};
        makeHost(host);
        {% endfor %}
    {% endif %}
    {% if added_hosts %}
        {% for host in added_hosts %}
        var host = {{host|safe}}
        makeHost(host);
        {% endfor %}
        updateHosts([]);
    {% endif %}

    addToolbarButton(editor, toolbarContainer, 'zoomIn', '', "/static/img/mxgraph/zoom_in.png", true);
    addToolbarButton(editor, toolbarContainer, 'zoomOut', '', "/static/img/mxgraph/zoom_out.png", true);

    {% if debug %}
    editor.addAction('printXML', function(editor, cell) {
        mxLog.write(encodeGraph(graph));
        mxLog.show();
    });
    addToolbarButton(editor, toolbarContainer, 'printXML', '', '/static/img/mxgraph/fit_to_size.png', true);
    {% endif %}

    var outline = new mxOutline(graph, overviewContainer);

    var checkAllowed = function(edge, terminal, source) {
        //check if other terminal is null, and that they are different
        otherTerminal = edge.getTerminal(!source);
        if(terminal != null && otherTerminal != null) {
            if( terminal.getParent().getId().split('_')[0] == //'host' or 'network'
                otherTerminal.getParent().getId().split('_')[0] ) {
                    //not allowed
                    graph.removeCells([edge]);
                    return false;
                }
        }
        return true;
    };

    var colorEdge = function(edge, terminal, source) {
        if(terminal.getParent().getId().indexOf('network') >= 0) {
            styles = terminal.getParent().getStyle().split(';');
            color = 'black';
            for(var i=0; i<styles.length; i++){
                kvp = styles[i].split('=');
                if(kvp[0] == "fillColor"){
                    color = kvp[1];
                }
            }
            edge.setStyle('strokeColor=' + color);
        }
    };

    var alertVlan = function(edge, terminal, source) {
        if( terminal == null || edge.getTerminal(!source) == null) {
            return;
        }
        var vlanHTML = '<form> <input type="radio" name="tagged" value="True" checked> Tagged<br>'
        vlanHTML += '<input type="radio" name="tagged" value="False"> Untagged </form>'
        vlanHTML += '<button onclick=parseVlanWindow(' + edge.getId() + ');>Okay</button>'
        vlanHTML += '<button onclick=deleteVlanWindow(' + edge.getId() + ');>Cancel</button>'
        content = document.createElement('div');
        content.innerHTML = vlanHTML;
        showWindow(graph, "Vlan Selection", content, 200, 200);
    }

    //sets the edge color to be the same as the network
    graph.addListener(mxEvent.CELL_CONNECTED, function(sender, event){
        edge = event.getProperty('edge');
        terminal = event.getProperty('terminal')
        source = event.getProperty('source');
        if(checkAllowed(edge, terminal, source)) {
            colorEdge(edge, terminal, source);
            alertVlan(edge, terminal, source);
        }
    });

    createDeleteDialog = function(id) {
        var content = document.createElement('div');
        var innerHTML = "<button style='width: 46%;' onclick=deleteCell('" + id + "');>Remove</button>"
        innerHTML += "<button style='width: 46%;' onclick='currentWindow.destroy();'>Cancel</button>"
        content.innerHTML = innerHTML;
        showWindow(currentGraph, 'Do you want to delete this network?', content, 200, 62);
    }

    graph.dblClick = function(evt, cell) {

        if( cell != null ){
            if( cell.getParent() != null && cell.getParent().getId().indexOf("network") > -1) {
                cell = cell.getParent();
            }
            if( cell.isEdge() || cell.getId().indexOf("network") > -1 ) {
                createDeleteDialog(cell.getId());
            }
            else {
            showDetailWindow(cell);
           }
        }
    };

    updateHosts({{ removed_hosts|default:"[]"|safe }});
    if(!has_public_net){
        addPublicNetwork();
    }
}

function showDetailWindow(cell) {
    var info = JSON.parse(cell.getValue());
    var content = document.createElement("div");
    var inner = "<pre>Name: " + info.name + "\n";
    inner += "Description:\n" + info.description + "</pre>";
    inner += '<button onclick="currentWindow.destroy();">Okay</button>'
    content.innerHTML = inner
    showWindow(currentGraph, 'Details', content, 400, 400);
}

function restoreFromXml(xml, editor) {
    var doc = mxUtils.parseXml(xml);
    var node = doc.documentElement;
    editor.readGraphModel(node);

    //Iterate over all children, and parse the networks to add them to the sidebar
    var root = currentGraph.getDefaultParent();
    for( var i=0; i<root.getChildCount(); i++) {
        var cell = root.getChildAt(i);
        if(cell.getId().indexOf("network") > -1) {
            var info = JSON.parse(cell.getValue());
            var name = info['name'];
            networks.add(name);
            var styles = cell.getStyle().split(";");
            var color = null;
            for(var j=0; j< styles.length; j++){
                var kvp = styles[j].split('=');
                if(kvp[0] == "fillColor") {
                    color = kvp[1];
                    break;
                }
            }
            if(info.public){
                has_public_net = true;
            }
            netCount++;
            makeSidebarNetwork(name, color, cell.getId());
        }
    }
}

function deleteCell(cellId) {
    var cell = currentGraph.getModel().getCell(cellId);
    if( cellId.indexOf("network") > -1 ) {
        elem = document.getElementById(cellId);
        elem.parentElement.removeChild(elem);
    }
    currentGraph.removeCells([cell]);
    currentWindow.destroy();
}

function newNetworkWindow() {
    var innerHtml = 'Name: <input type="text" name="net_name" id="net_name_input" style="margin:5px;"><br>';
    innerHtml += '<button style="width: 46%;" onclick="parseNetworkWindow()">Okay</button>';
    innerHtml += '<button style="width: 46%;" onclick="currentWindow.destroy();">Cancel</button><br>';
    innerHtml += '<div id="current_window_errors"/>';
    var content = document.createElement("div");
    content.innerHTML = innerHtml;

    showWindow(currentGraph, "Network Creation", content, 300, 300);
}

function parseNetworkWindow() {
    var net_name = document.getElementById("net_name_input").value
    var error_div = document.getElementById("current_window_errors");
    if( networks.has(net_name) ){
        error_div.innerHTML = "All network names must be unique";
        return;
    }
    addNetwork(net_name);
    currentWindow.destroy();
}

function addToolbarButton(editor, toolbar, action, label, image, isTransparent)
{
    var button = document.createElement('button');
    button.style.fontSize = '10';
    if (image != null)
    {
        var img = document.createElement('img');
        img.setAttribute('src', image);
        img.style.width = '16px';
        img.style.height = '16px';
        img.style.verticalAlign = 'middle';
        img.style.marginRight = '2px';
        button.appendChild(img);
    }
    if (isTransparent)
    {
        button.style.background = 'transparent';
        button.style.color = '#FFFFFF';
        button.style.border = 'none';
    }
    mxEvent.addListener(button, 'click', function(evt)
    {
        editor.execute(action);
    });
    mxUtils.write(button, label);
    toolbar.appendChild(button);
};

function encodeGraph(graph) {
    var encoder = new mxCodec();
    var xml = encoder.encode(graph.getModel());
    return mxUtils.getXml(xml);
}

function doGlobalConfig(graph) {
    //general graph stuff
    graph.setMultigraph(false);
    graph.setCellsSelectable(false);
    graph.setCellsMovable(false);

    //testing
    graph.vertexLabelIsMovable = true;


    //edge behavior
    graph.setConnectable(true);
    graph.setAllowDanglingEdges(false);
    mxEdgeHandler.prototype.snapToTerminals = true;
    mxConstants.MIN_HOTSPOT_SIZE = 16;
    mxConstants.DEFAULT_HOTSPOT = 1;
    //edge 'style' (still affects behavior greatly)
    style = graph.getStylesheet().getDefaultEdgeStyle();
    style[mxConstants.STYLE_EDGE] = mxConstants.EDGESTYLE_ELBOW;
    style[mxConstants.STYLE_ENDARROW] = mxConstants.NONE;
    style[mxConstants.STYLE_ROUNDED] = true;
    style[mxConstants.STYLE_FONTCOLOR] = 'black';
    style[mxConstants.STYLE_STROKECOLOR] = 'red';

    style[mxConstants.STYLE_LABEL_BACKGROUNDCOLOR] = '#FFFFFF';
    style[mxConstants.STYLE_STROKEWIDTH] = '3';
    style[mxConstants.STYLE_ROUNDED] = true;
    style[mxConstants.STYLE_EDGE] = mxEdgeStyle.EntityRelation;

    hostStyle = graph.getStylesheet().getDefaultVertexStyle();
    hostStyle[mxConstants.STYLE_ROUNDED] = 1;

    // TODO: Proper override
    graph.convertValueToString = function(cell) {
        try{
            //changes value for edges with xml value
            if(cell.isEdge()) {
                if(JSON.parse(cell.getValue())["tagged"]) {
                    return "tagged";
                }
                return "untagged";
            } else{
                    return JSON.parse(cell.getValue())['name'];
            }
        }
        catch(e){
                return cell.getValue();
        }
    };
}

function showWindow(graph, title, content, width, height) {
    //create transparent black background
    var background = document.createElement('div');
    background.style.position = 'absolute';
    background.style.left = '0px';
    background.style.top = '0px';
    background.style.right = '0px';
    background.style.bottom = '0px';
    background.style.background = 'black';
    mxUtils.setOpacity(background, 50);
    document.body.appendChild(background);

    //deal with IE quirk
    if (mxClient.IS_IE) {
        new mxDivResizer(background);
    }

    var x = Math.max(0, document.body.scrollWidth/2-width/2);
    var y = Math.max(10, (document.body.scrollHeight ||
                document.documentElement.scrollHeight)/2-height*2/3);

    var wnd = new mxWindow(title, content, x, y, width, height, false, true);
    wnd.setClosable(false);

    wnd.addListener(mxEvent.DESTROY, function(evt) {
        graph.setEnabled(true);
        mxEffects.fadeOut(background, 50, true, 10, 30, true);
    });
    currentWindow = wnd;

    graph.setEnabled(false);
    wnd.setVisible(true);
};

function closeWindow() {
    //allows the current window to be destroyed
    currentWindow.destroy();
};

function othersUntagged(edgeID) {
    var edge = currentGraph.getModel().getCell(edgeID);
    var end1 = edge.getTerminal(true);
    var end2 = edge.getTerminal(false);

    if( end1.getParent().getId().split('_')[0] == 'host' ){
        var netint = end1;
    } else {
        var netint = end2;
    }

    var edges = netint.edges;

    for( var i=0; i < edges.length; i++ ) {
        if( edges[i].getValue() ) {
            var tagged = JSON.parse(edges[i].getValue()).tagged;
        } else {
            var tagged = true;
        }
        if( !tagged ) {
            return true;
        }
    }
    return false;
};


function deleteVlanWindow(edgeID) {
    var cell = currentGraph.getModel().getCell(edgeID);
    currentGraph.removeCells([cell]);
    currentWindow.destroy();
}

function parseVlanWindow(edgeID) {
    //do parsing and data manipulation
    var radios = document.getElementsByName("tagged");
    edge = currentGraph.getModel().getCell(edgeID);

    for(var i=0; i<radios.length; i++) {
        if(radios[i].checked) {
            //set edge to be tagged or untagged
            //cellValue.setAttribute("tagged", radios[i].value);
            if( radios[i].value == "False")
            {
                if( othersUntagged(edgeID) )
                {
                    alert("Only one untagged VLAN is allowed per interface");
                    return;
                }
            }
            edgeVal = Object();
            edgeVal['tagged'] = radios[i].value == "True";
            edge.setValue(JSON.stringify(edgeVal));
            break;
        }
    }
    currentGraph.refresh(edge);
    closeWindow();
}

function makeMxNetwork(net_name, public = false) {
    model = currentGraph.getModel();
    width = 10;
    height = 1700;
    xoff = 400 + (30 * netCount);
    yoff = -10;
    var color = netColors[netCount];
    if( netCount > (netColors.length - 1)) {
        color = Math.floor(Math.random() * 16777215); //int in possible color space
        color = '#' + color.toString(16).toUpperCase(); //convert to hex
        //alert(color);
    }
    var net_val = Object();
    net_val['name'] = net_name;
    net_val['public'] = public;
    net = currentGraph.insertVertex(
        currentGraph.getDefaultParent(),
        'network_' + netCount,
        JSON.stringify(net_val),
        xoff,
        yoff,
        width,
        height,
        'fillColor=' + color,
        false
    );
    var num_ports = 45;
    for(var i=0; i<num_ports; i++){
        port = currentGraph.insertVertex(
            net,
            null,
            '',
            0,
            (1/num_ports) * i,
            10,
            height / num_ports,
            'fillColor=black;opacity=0',
            true
        );
    }

    var retVal = Object();
    retVal['color'] = color;
    retVal['element_id'] = "network_" + netCount;

    networks.add(net_name);

    netCount++;
    return retVal;
}

function addPublicNetwork() {
    var net = makeMxNetwork("public", true);
    makeSidebarNetwork("public", net['color'], net['element_id']);
    has_public_net = true;
}

function addNetwork(net_name) {
    var ret = makeMxNetwork(net_name);
    var color = ret['color'];
    var net_id = ret['element_id'];
    makeSidebarNetwork(net_name, color, net_id);
}

function updateHosts(removed) {
    for(var i=0; i < removed.length; i++)
    {
        var hoststring = removed[i];
        var hostid = "host_" + hoststring.split("*")[0];
        var cell = currentGraph.getModel().getCell(hostid);
        currentGraph.removeCells([cell]);
    }

    var hosts = currentGraph.getChildVertices(currentGraph.getDefaultParent());
    var topdist = 100;
    for(var i=0; i<hosts.length; i++) {
        var host = hosts[i];
        if(!host.id.startsWith("host_"))
        {
            continue;
        }
        var geometry = host.getGeometry();
        geometry.y = topdist + 50;
        topdist = geometry.y + geometry.height;
        host.setGeometry(geometry);
    }
}

function makeSidebarNetwork(net_name, color, net_id){
    var newNet = document.createElement("li");
    var colorBlob = document.createElement("div");
    colorBlob.className = "colorblob";
    var textContainer = document.createElement("p");
    textContainer.className = "network_innertext";
    newNet.id = net_id;
    var deletebutton = document.createElement("button");
    deletebutton.className = "btn btn-danger";
    deletebutton.style = "float: right; height: 20px; line-height: 8px; vertical-align: middle; width: 20px; padding-left: 5px;";
    deleteButtonText = document.createTextNode("X");
    deletebutton.appendChild(deleteButtonText);
    deletebutton.addEventListener("click", function() {
            createDeleteDialog(net_id);
    }, false);
    var text = net_name;
    var newNetValue = document.createTextNode(text);
    textContainer.appendChild(newNetValue);
    colorBlob.style['background'] = color;
    newNet.appendChild(colorBlob);
    newNet.appendChild(textContainer);
    if( net_name != "public" )
    {
        newNet.appendChild(deletebutton);
    }
    document.getElementById("network_list").appendChild(newNet);
}

function makeHost(hostInfo) {
    value = JSON.stringify(hostInfo['value']);
    interfaces = hostInfo['interfaces'];
    graph = currentGraph;
    width = 100;
    height = (25 * interfaces.length) + 25;
    xoff = 75;
    yoff = lastHostBottom + 50;
    lastHostBottom = yoff + height;
    host = graph.insertVertex(
        graph.getDefaultParent(),
        'host_' + hostInfo['id'],
        value,
        xoff,
        yoff,
        width,
        height,
        'editable=0',
        false
    );
    host.getGeometry().offset = new mxPoint(-50,0);
    host.setConnectable(false);
    hostCount++;

    for(var i=0; i<interfaces.length; i++) {
        port = graph.insertVertex(
            host,
            null,
            JSON.stringify(interfaces[i]),
            90,
            (i * 25) + 12,
            20,
            20,
            'fillColor=blue;editable=0',
            false
        );
        port.getGeometry().offset = new mxPoint(-4*interfaces[i].name.length -2,0);
        currentGraph.refresh(port);
    }
    currentGraph.refresh(host);
}

function submitForm() {
    var form = document.getElementById("xml_form");
    var input_elem = document.getElementById("hidden_xml_input");
    var s = encodeGraph(currentGraph);
    input_elem.value = s;
    req = new XMLHttpRequest();
    req.open("POST", "/wf/workflow/", false);
    req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    req.onerror = function() { alert("problem with form submission"); }
    var formData = $("#xml_form").serialize();
    req.send(formData);
}
</script>
{% endblock extrahead %}

<!-- Calls the main function after the page has loaded. Container is dynamically created. -->
{% block content %}
    <div id="graphParent"
         style="position:absolute;overflow:hidden;top:0px;bottom:0px;width:75%;left:0px;">
        <div id="graphContainer"
            style="position:relative;overflow:hidden;top:36px;bottom:0px;left:0px;right:0px;background-image:url('/static/img/mxgraph/grid.gif');cursor:default;">
        </div>


        <!-- Creates a container for the sidebar -->
        <div id="toolbarContainer"
            style="position:absolute;white-space:nowrap;overflow:hidden;top:0px;left:0px;right:0px;padding:6px;">
        </div>

        <!-- Creates a container for the outline -->
        <div id="outlineContainer"
            style="position:absolute;overflow:hidden;top:36px;right:0px;width:200px;height:140px;background:transparent;border-style:solid;border-color:black;">
        </div>
    </div>

    <style>
        #network_select {
                background: inherit;
                padding: 0px;
                padding-top: 0px;
        }
        #toolbarContainer {
                background: #DDDDDD;
                height: 36px;
        }
        #toolbar_extension {
                height: 36px;
                background: #DDDDDD;
        }
        #btn_add_network {
                width: 100%;
        }
        #vlan_notice {
                margin: 20px;
        }
        #network_list li {
                border-radius: 2px;
                margin: 5px;
                padding: 5px;
                vertical-align: middle;
                background: #DDDDDD;
        }
        #network_list {
                list-style-type: none;
                padding: 0;
        }
        .colorblob {
                width: 20px;
                height: 20px;
                border-radius: 50%;
                display: inline-block;
                vertical-align: middle;
        }
        .network_innertext {
                display: inline-block;
                padding-left: 10px;
                vertical-align: middle;
                padding-bottom: 0px;
                margin-bottom: 2px;
        }
        .mxWindow {
                background: #FFFFFF;
        }
    </style>

    <div id="network_select" style="position:absolute;top:0px;bottom:0px;width:25%;right:0px;left:auto;">
        <div id="toolbar_extension">
        <button id="btn_add_network" type="button" class="btn btn-primary" onclick="newNetworkWindow();">Add Network</button>
        </div>
        <ul id="network_list">
        </ul>
        <button type="button" style="display: none" onclick="submitForm();">Submit</button>
    </div>
    <form id="xml_form" method="post" action="/wf/workflow/">
        {% csrf_token %}
        <input type="hidden" id="hidden_xml_input" name="xml" />
    </form>

<script>
    main(
        document.getElementById('graphContainer'),
        document.getElementById('outlineContainer'),
        document.getElementById('toolbarContainer'),
        document.getElementById('sidebarContainer')
    )
</script>
{% endblock content %}
{% block onleave %}
submitForm();
{% endblock %}