"use strict";

var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };

/*
 * *******************************************************************************
 *  * Copyright (c) 2018 Edgeworx, Inc.
 *  *
 *  * This program and the accompanying materials are made available under the
 *  * terms of the Eclipse Public License v. 2.0 which is available at
 *  * http://www.eclipse.org/legal/epl-2.0
 *  *
 *  * SPDX-License-Identifier: EPL-2.0
 *  *******************************************************************************
 *
 */

var toastDelay = 5000;
var instancesLayout = [];
var accTok = apwGetAccessToken();
var externalPorts = [];

var selectedTrackId = -1;
var selectedTrackName = "";
var selectedTrackIsActivated = -1;

var drawingConnections = false;

var addingPort = false;

var inputEndpoint = {
    //    connector: ["Straight"],
    anchor: "Left",
    endpoint: ["Image", { src: "/images/build/piping-connection-arrow.png?" + new Date().getTime() }],
    isSource: false,
    isTarget: true,
    maxConnections: -1,
    paintStyle: { fillStyle: "#039be5", outlineColor: "#039be5", outlineWidth: 1 },
    hoverPaintStyle: { fillStyle: "#039be5" },
    connectorStyle: { strokeStyle: "#039be5", lineWidth: 5 },
    connectorHoverStyle: { lineWidth: 5 },
    connectorOverlays: [["Arrow", { location: 0.5 }]]
};

var outputEndpoint = {
    //    connector: ["Straight"],
    anchor: "Right",
    endpoint: ["Image", { src: "/images/build/piping-connection-arrow.png?" + new Date().getTime() }],
    isSource: true,
    isTarget: false,
    maxConnections: -1,
    paintStyle: { fillStyle: "#039be5", outlineColor: "#039be5", outlineWidth: 1 },
    hoverPaintStyle: { fillStyle: "#039be5" },
    connectorStyle: { strokeStyle: "#039be5", lineWidth: 5 },
    connectorHoverStyle: { lineWidth: 5 },
    connectorOverlays: [["Arrow", { location: 0.5 }]]
};

var gridSize = 20;

function activateElementInstanceDraggable(elementId) {
    var div = $("div[data-element-instance-id='" + elementId + "']");
    div.draggable({
        helper: "original",
        opacity: 0.8,
        delay: 200,
        revert: "invalid",
        revertDuration: 400,
        scroll: true,
        containment: 'parent',
        zIndex: 99999,
        start: function start(event, ui) {
            $(".build-toolbox").css({ "overflow-x": "", "overflow-y": "" });
        },
        stop: function stop(event, ui) {
            $(".build-toolbox").css({ "overflow-x": "scroll", "overflow-y": "scroll" });
        },
        drag: function drag(event, ui) {
            var topRemainder = ui.position.top % gridSize;
            var leftRemainder = ui.position.left % gridSize;

            if (ui.position.left > 0) {
                ui.position.top = ui.position.top - topRemainder;
                ui.position.left = ui.position.left - leftRemainder;
            }

            var id = $(this).attr('data-element-instance-id');
            jsPlumb.repaint('instance-id-' + id, ui.position);
        }
    });

    $(".build-canvas").css({ "overflow-x": "scroll", "overflow-y": "scroll" });
}

function updateInstanceLayoutDictionary(x, y, id) {
    for (var i = 0; i < instancesLayout.length; i++) {
        var item = instancesLayout[i];
        if (item.id == id) {
            item.x = x;
            item.y = y;
            instancesLayout[i] = item;
        }
    }
}

function activateHandlersForBuildInterface() {
    //Set up the handlers and immediate actions
    $(".build-element-container").draggable({
        helper: "clone",
        opacity: 0.8,
        delay: 200,
        revert: "invalid",
        revertDuration: 400,
        scroll: true,
        zIndex: 99999,
        start: function start(event, ui) {
            $(".build-toolbox").css({ "overflow-x": "", "overflow-y": "" });
        },
        stop: function stop(event, ui) {
            $(".build-toolbox").css({ "overflow-x": "scroll", "overflow-y": "scroll" });
        },
        drag: function drag(event, ui) {
            var canvasX = $(".build-canvas").offset().left;

            var topRemainder = ui.position.top % gridSize;
            var leftRemainder = ui.position.left % gridSize;

            if (ui.position.left > canvasX) {
                ui.position.top = ui.position.top - topRemainder;
                ui.position.left = ui.position.left - leftRemainder;
            }
        }
    });

    $('#element-instance-properties-name').blur(validateElementName);
    $('#element-instance-properties-logsize').blur(updateElementInstance);
    $('#element-instance-properties-config').blur(updateElementInstance);
    $('#element-instance-properties-volumes').blur(updateElementInstance);
    $('#element-instance-properties-rootaccess').change(updateElementInstance);
    $('#element-instance-properties-fog-instance').change(updateElementInstance);

    $('#element-instance-properties-strace').change(enableElementStrace);

    $(".build-canvas").droppable({
        drop: handleElementInstanceTypeDropOnCanvas
    });

    //This is a hack to work around a scrolling issue with draggable. We will make the toolbar on the left become
    //scrollable AFTER the draggable handler has been added to the toolbar items.
    $(".build-toolbox").css({ "overflow-x": "scroll", "overflow-y": "scroll" });

    //Set up the click handler for the create track cancel button
    $(document).apwClickAction({
        'clickablesSelector': '#auth-create-track-button-cancel',
        'actionFunction': 'clearCreateTrackForm'
    });

    //Set up the click handler for the track activate/deactivate button
    $(document).apwClickAction({
        'clickablesSelector': '#track-deactivate-button',
        'actionFunction': 'changeTrackActiveStatus'
    });

    //Set up the click handler for the create track confirm button
    $(document).apwClickAction({
        'clickablesSelector': '#auth-create-track-button-confirm',
        'actionFunction': 'createNewTrack'
    });

    //Set up the click handler for the update track buttons
    $(document).apwClickAction({
        'clickablesSelector': '#track-update-button',
        'actionFunction': 'clickUpdateTrack'
    });

    //Set up the click handler for the update track save button
    $(document).apwClickAction({
        'clickablesSelector': '#auth-update-track-button-confirm',
        'actionFunction': 'saveUpdateTrack'
    });

    $(document).apwClickAction({
        'clickablesSelector': '#element-instance-port-button',
        'actionFunction': 'clickPortElement'
    });

    $(document).apwClickAction({
        'clickablesSelector': '#element-instance-download-image-button',
        'actionFunction': 'clickDownloadImage'
    });

    $(document).apwClickAction({
        'clickablesSelector': '#element-instance-create-image-button',
        'actionFunction': 'clickCreateImage'
    });

    $(document).apwClickAction({
        'clickablesSelector': '#element-instance-rebuild-button',
        'actionFunction': 'clickRebuild'
    });

    $(document).apwClickAction({
        'clickablesSelector': '#element-instance-strace-button',
        'actionFunction': 'clickDownloadStraceFile'
    });

    //Set up the click handler for the delete element port X buttons
    $(document).apwClickAction({
        'clickablesSelector': '.auth-port-element-remove-port',
        'actionFunction': 'clickPortRemove',
        'loadDataFields': '{"portID":"portid","networkID":"networkid"}'
    });

    //Set up the click handler for the add element port button
    $(document).apwClickAction({
        'clickablesSelector': '#auth-port-element-add-button',
        'actionFunction': 'clickPortAdd'
    });

    $('#track-delete-confirm-modal-yes-button').click(function () {
        deleteTrack();
    });

    $('#delete-confirm-modal-yes-button').click(function () {
        var withCleanUp = $('#deleteElementWithCleanUp')[0].checked;

        deleteSelectedInstance(withCleanUp);
    });
}

function handleElementInstanceTypeDropOnCanvas(event, ui) {
    var canvasX = $(".build-canvas").offset().left;

    var fogX = ui.position.left;
    var fogY = ui.position.top;

    var posX = Math.floor((fogX - canvasX) / gridSize) * gridSize;
    var posY = Math.floor(fogY / gridSize) * gridSize;

    if (ui.draggable.attr('data-element-type-id') != undefined) {
        // element
        if (selectedTrackId == -1 || selectedTrackId == undefined) {
            Materialize.toast('No tracks exist!', toastDelay);
            return;
        }

        var canvas = $('.build-canvas');
        var canvasTop = canvas.scrollTop();
        var canvasLeft = canvas.scrollLeft();

        posX += canvasLeft;
        posY += canvasTop;

        var typeId = ui.draggable.attr('data-element-type-id');

        var postFields = "trackId=" + selectedTrackId + "&elementKey=" + typeId + "&name=NEW ELEMENT&layoutX=" + posX + "&layoutY=" + posY;

        $(document).apwManualPostUp({
            'postingDisplayFunction': 'apwManualPostUpPostingDisplayBlank',
            'postingFinishDisplayFunction': 'apwManualPostUpPostingDisplayFinishBlank',
            'postingResponseHandlerFunction': 'createElementInstanceLayoutAPI',
            'passToken': 'Y',
            'postURL': fogControllerUrl + ioAuthoring.urls.elementInstanceCreate,
            'postFailedFunction': 'apwManualPostUpFailedBlank',
            'postData': postFields
        });
    } else {
        // instance
        var id = ui.draggable.attr('data-element-instance-id');
        var div = $("div[data-element-instance-id='" + id + "']");

        // var oldLayout = findInLayoutDictionary(id);
        // console.log(oldLayout);
        // var isOverlapping = true;
        // if (oldLayout != undefined) {
        //     var widthx = 190;
        //     var widthy = 150;
        //     if (fogX + widthx < oldLayout.x || oldLayout.x + widthx < fogX)
        //         isOverlapping = false;
        //     if (fogY + widthy < oldLayout.y || oldLayout.y + widthy < fogY)
        //         isOverlapping = false;
        //
        //     if (isOverlapping) {
        //         fogX = oldLayout.x;
        //         fogY = oldLayout.y;
        //     }
        // }
        // updateInstanceLayoutDictionary(fogX, fogY, id);

        updateElementInstanceLayoutInfo(id, fogX, fogY, true);

        div.attr('data-element-instance-layout-x', fogX);
        div.attr('data-element-instance-layout-y', fogY);
        div.css('top', fogY);
        div.css('left', fogX);
        selectElementInstance(id);

        jsPlumb.repaint('instance-id-' + id, { 'left': fogX, 'top': fogY });
    }
}

var createElementInstanceLayoutAPI = function createElementInstanceLayoutAPI(response) {
    var result = $.parseJSON(response);
    if (result.status == 'ok') {
        var layputPostFields = "&layoutX=" + result.elementInstance.layoutX + "&layoutY=" + result.elementInstance.layoutY + "&elementId=" + result.elementInstance.id;

        $(document).apwManualPostUp({
            'postingDisplayFunction': 'apwManualPostUpPostingDisplayBlank',
            'postingFinishDisplayFunction': 'apwManualPostUpPostingDisplayFinishBlank',
            'postingResponseHandlerFunction': 'createElementInstanceAPI',
            'passToken': 'N',
            'postURL': ioAuthoring.urls.elementInstanceLayoutCreate,
            'postFailedFunction': 'apwManualPostUpFailedBlank',
            'postData': layputPostFields
        });
    }
};

function createElementInstanceAPI(response) {
    var result = $.parseJSON(response);
    if (result.status == 'ok') {
        var elementInstanceId = result.id;

        var apiEndpointGetElementInstanceDetails = fogControllerUrl + ioAuthoring.urls.getElementInstanceProperties;
        var postFields = "instanceId=" + elementInstanceId;

        $(document).apwManualPostUp({
            'postingDisplayFunction': 'apwManualPostUpPostingDisplayBlank',
            'postingFinishDisplayFunction': 'apwManualPostUpPostingDisplayFinishBlank',
            'postingResponseHandlerFunction': 'getCreatedElementInstanceLayout',
            'passToken': 'Y',
            'postURL': apiEndpointGetElementInstanceDetails,
            'postFailedFunction': 'apwManualPostUpFailedBlank',
            'postData': postFields
        });
    }
}

function getCreatedElementInstanceLayout(response) {
    if (response) {
        var result = $.parseJSON(response);
        var postFields = result;

        $(document).apwManualPostUp({
            'postingDisplayFunction': 'apwManualPostUpPostingDisplayBlank',
            'postingFinishDisplayFunction': 'apwManualPostUpPostingDisplayFinishBlank',
            'postingResponseHandlerFunction': 'getCreatedElementInstanceAPI',
            'passToken': 'N',
            'postURL': ioAuthoring.urls.getElementInstancesLayouts,
            'postFailedFunction': 'deleteConnectionAPIFail',
            'postData': postFields
        });
    }
}

function getCreatedElementInstanceAPI(response) {
    var result = $.parseJSON(response);

    if (result.status == 'ok') {
        var elementInstance = result.instance[0];
        var tmp = $('.build-element-instance-selected');
        if (tmp != undefined) {
            tmp.attr('data-element-instance-selected', '0');
            tmp.removeClass('build-element-instance-selected');
            updateElementInstanceLayoutInfo(tmp.attr('data-element-instance-id'), tmp.attr('data-element-instance-layout-x'), tmp.attr('data-element-instance-layout-y'), false);
        }

        if (elementInstance.ports == undefined || elementInstance.ports == "") elementInstance.ports = JSON.parse("[]");
        createNewElementInstanceOnCanvas(elementInstance.id, elementInstance.elementInstanceName, elementInstance.elementName, elementInstance.picture, 1, elementInstance.layoutX, elementInstance.layoutY, elementInstance.fogInstanceId, elementInstance.config, elementInstance.rootHostAccess, elementInstance.strace, elementInstance.ports, elementInstance.configstring, '-', elementInstance.isX86, elementInstance.isArm, elementInstance.logSize, '{"volumemappings":[]}');
        activateElementInstanceDraggable(elementInstance.id);
        activateElementInstanceClickHandler(elementInstance.id);
    }
}

function setObjectName(id, name) {
    var i = 0;
    var displayName = name;
    var tmp = document.getElementById('name-strong-' + id);
    if (tmp != undefined) $(tmp).remove();
    while (true) {
        var p = document.getElementById('display-name-' + id);
        var t = document.createTextNode(displayName);
        var s = document.createElement('strong');
        var sp = document.createElement('span');
        $(sp).attr('id', 'name-span-' + id);
        $(s).attr('id', 'name-strong-' + id);
        s.appendChild(t);
        p.appendChild(s);
        p.appendChild(sp);
        var pHeigth = $(p).outerHeight();
        var pLeft = $(p).offset().left;
        var sLeft = $(sp).offset().left;
        i++;
        displayName = name.substring(0, name.length - i) + '...';
        if (sLeft - pLeft <= 148 && pHeigth < 25) {
            $(sp).remove();
            break;
        }
        $(s).remove();
        $(sp).remove();
    }
    p.style.display = '';
}

function setObjectSubtitle(id, elementName) {
    var i = 0;
    var displayName = elementName;
    while (true) {
        var p = document.getElementById('subtitle-' + id);
        var t = document.createTextNode(displayName);
        var sp = document.createElement('span');
        $(sp).attr('id', 'subtitle-span-' + id);
        p.appendChild(t);
        p.appendChild(sp);
        var pLeft = $(p).offset().left;
        var pHeight = $(p).outerHeight();
        var sLeft = $(sp).offset().left;
        i++;
        displayName = elementName.substring(0, elementName.length - i) + '...';
        if (sLeft - pLeft <= 148 && pHeight < 20) {
            $(sp).remove();
            break;
        }
        $(t).remove();
        $(sp).remove();
    }
    p.style.display = '';
}

function createNewElementInstanceOnCanvas(id, name, elementName, picture, isSelected, layoutX, layoutY, fogInstanceId, config, rootAccess, strace, ports, configString, status, isX86, isArm, logSize, volumes) {
    instancesLayout.push({ x: layoutX, y: layoutY, id: id });

    if (configString == null || configString == "") configString = "{}";

    if (volumes == null || volumes == "") volumes = '{"volumemappings":[]}';

    if (status == undefined) status = "UNKNOWN";

    $('#build-canvas-layout-grid').append('<div class="build-element-instance-container ' + (isSelected == 1 ? 'build-element-instance-selected' : '') + '" style="top:' + layoutY + 'px;left:' + layoutX + 'px;" data-element-instance-id="' + id + '" data-element-instance-x86="' + isX86 + '" data-element-instance-arm="' + isArm + '" data-element-instance-log-size="' + logSize + '" data-element-instance-name="' + name + '" data-element-instance-layout-x="' + layoutX + '" data-element-instance-layout-y="' + layoutY + '" data-element-instance-selected="' + isSelected + '" id="instance-id-' + id + '" data-element-instance-fog="' + fogInstanceId + '" data-element-instance-config="' + escape(config) + '" data-element-instance-status="' + status + '" data-element-instance-root="' + rootAccess + '" data-element-instance-strace="' + strace + '" data-element-instance-volumes="' + escape(volumes) + '" data-element-instance-config-string="' + escape(configString) + '"> <img class="element-instance-icon" src="/' + picture + '?' + +new Date().getTime() + '"> ' + ' <p id="display-name-' + id + '"></p> ' + ' <p id="subtitle-' + id + '" class="build-element-instance-container-subtitle"></p> ' + '   <div style="display:none;" class="build-element-instance-loading-spinner"> ' + '       <div class="sk-child sk-bounce1"></div> ' + '       <div class="sk-child sk-bounce2"></div> ' + '       <div class="sk-child sk-bounce3"></div> ' + '   </div> ' + '</div>');

    setObjectName(id, name);
    setObjectSubtitle(id, elementName);

    $('#instance-id-' + id).data('ports', ports);

    if (isSelected == 1) {
        checkPortsManagementButtonAccess();
        elementInstanceClickHandler(id);
    }

    //    jsPlumb.draggable('instance-id-' + id);
    jsPlumb.addEndpoint('instance-id-' + id, { uuid: 'instance-id-' + id + "-in" }, inputEndpoint);
    jsPlumb.addEndpoint('instance-id-' + id, { uuid: 'instance-id-' + id + "-out" }, outputEndpoint);
}

function activateElementInstanceClickHandler(id) {
    var div = $("div[data-element-instance-id='" + id + "']");
    div.click(function () {
        var instanceId = $(this).attr('data-element-instance-id');
        selectElementInstance(instanceId);
    });
}

function selectElementInstance(instanceId) {
    var tmp = $('.build-element-instance-selected');
    if (tmp != undefined) {
        tmp.attr('data-element-instance-selected', '0');
        tmp.removeClass('build-element-instance-selected');
        updateElementInstanceLayoutInfo(tmp.attr('data-element-instance-id'), tmp.attr('data-element-instance-layout-x'), tmp.attr('data-element-instance-layout-y'), false);
    }
    var tmp = $("div[data-element-instance-id='" + instanceId + "']");
    tmp.attr('data-element-instance-selected', '1');
    tmp.addClass('build-element-instance-selected');

    elementInstanceClickHandler(instanceId);
    updateElementInstanceLayoutInfo(tmp.attr('data-element-instance-id'), tmp.attr('data-element-instance-layout-x'), tmp.attr('data-element-instance-layout-y'), true);

    checkPortsManagementButtonAccess();
    Materialize.updateTextFields();
}

function updateElementInstanceLayoutInfo(instanceId, layoutX, layoutY, isSelected) {
    $.ajax({
        type: "POST",
        url: ioAuthoring.urls.updateElementInstancesLayouts,
        dataType: "json",
        data: { ElementID: instanceId, LayoutX: layoutX, LayoutY: layoutY, Selected: isSelected == true ? 1 : 0 },
        success: function success(result) {}
    });
}

function elementInstanceClickHandler(instanceId) {
    var overlay = $('.build-properties-loading-overlay');
    if (overlay != undefined) overlay.css('display', 'visible');

    var instanceObject = $("#instance-id-" + instanceId);
    var name = instanceObject.attr('data-element-instance-name');
    var config = unescape(instanceObject.attr('data-element-instance-config'));
    var rootAccess = instanceObject.attr('data-element-instance-root');
    var strace = instanceObject.attr('data-element-instance-strace');
    var fogInstance = instanceObject.attr('data-element-instance-fog');
    var configString = instanceObject.attr('data-element-instance-config-string');
    var status = instanceObject.attr('data-element-instance-status');
    var logSize = instanceObject.attr('data-element-instance-log-size');
    var volumes = unescape(instanceObject.attr('data-element-instance-volumes'));

    if (status == undefined || status == 'undefined') status = 'UNKNOWN';

    $('#element-instance-properties-name').val(name);
    $('#element-instance-properties-config').val(config);
    $('#element-instance-properties-rootaccess').prop('checked', rootAccess == 1);
    $('#element-instance-properties-strace').prop('checked', strace == 1);
    $('#element-instance-properties-fog-instance').val(fogInstance);
    $('#element-instance-properties-config-example').text(unescape(configString));
    $('#element-instance-properties-element-status').text(status);
    $('#element-instance-properties-logsize').val(logSize);
    $('#element-instance-properties-volumes').val(volumes);

    if (overlay != undefined) overlay.css('display', 'none');

    $('#element-instance-properties-config').trigger('autoresize');
    $('#element-instance-properties-volumes').trigger('autoresize');
    refreshSelectDropDowns();
    Materialize.updateTextFields();
}

function saveUpdateTrack() {
    var trackName = $("#auth-update-track-trackname").val();
    if (trackName.trim() == '') {
        Materialize.toast('Track name is empty!', toastDelay, 'tracks-toast-container');
        return;
    }

    var existingTrackNames = $("#track-select-dropdown option");
    for (var i = 0; i < existingTrackNames.length; i++) {
        if (existingTrackNames[i].text == trackName.trim()) {
            Materialize.toast('Track name already exists!', toastDelay, 'tracks-toast-container');
            return;
        }
    }

    // if (trackName.length > 24) {
    //     Materialize.toast('Track name must be less than 25 characters!', toastDelay, 'tracks-toast-container');
    //     return;
    // }
    //

    var updateFields = "trackId=" + $('#track-select-dropdown').val() + "&trackName=" + trackName + "&isSelected=-1&isActivated=-1";

    $(document).apwManualPostUp({
        'postingDisplayFunction': 'apwManualPostUpPostingDisplayBlank',
        'postingFinishDisplayFunction': 'apwManualPostUpPostingDisplayFinishBlank',
        'postingResponseHandlerFunction': 'updateTrackAPI',
        'passToken': 'Y',
        'postURL': fogControllerUrl + ioAuthoring.urls.updateTrack,
        'postFailedFunction': 'updateTrackAPIFail',
        'postData': updateFields
    });
}

function updateTrackAPI(response) {
    var result = $.parseJSON(response);

    if (result.status == "ok") {
        var name = $("#auth-update-track-trackname").val();
        var displayName = name;
        if (name.length > 15) displayName = name.substr(0, 15) + '...';
        $("#track-select-dropdown option:selected").text(displayName);
        $("#track-select-dropdown option:selected").attr('completename', name);
        refreshSelectDropDowns();
    }
}

function clickUpdateTrack() {
    var trackID = $("#track-select-dropdown").val();
    var curName = $("#track-select-dropdown option:selected").attr('completename');

    //Put the information into the update integrator modal window
    $("#auth-update-track-trackname").val(curName);
}

function createNewTrack() {
    var trackName = $("#auth-create-track-trackname").val();
    if (trackName.trim() == '') {
        Materialize.toast('Track name is empty!', toastDelay, 'tracks-toast-container');
        return;
    }

    var existingTrackNames = $("#track-select-dropdown option");
    for (var i = 0; i < existingTrackNames.length; i++) {
        if (existingTrackNames[i].text == trackName.trim()) {
            Materialize.toast('Track name already exists!', toastDelay, 'tracks-toast-container');
            return;
        }
    }

    var apiEndpointCreateTrack = fogControllerUrl + ioAuthoring.urls.createNewTrack;

    var createFields = "fogInstanceId=" + $('#element-instance-properties-fog-instance').val() + "&trackName=" + trackName;

    $(document).apwManualPostUp({
        'postingDisplayFunction': 'apwManualPostUpPostingDisplayBlank',
        'postingFinishDisplayFunction': 'apwManualPostUpPostingDisplayFinishBlank',
        'postingResponseHandlerFunction': 'createTrackAPI',
        'passToken': 'Y',
        'postURL': apiEndpointCreateTrack,
        'postFailedFunction': 'createTrackAPIFail',
        'postData': createFields
    });
}

function createTrackAPIFail() {}

function createTrackAPI(response) {
    var result = $.parseJSON(response);

    if (result.status == "ok") {
        resetPropertiesPanel();

        var trackId = result.trackId;
        var name = $("#auth-create-track-trackname").val();
        var displayName = name;
        if (name.length > 15) displayName = name.substr(0, 15) + '...';

        selectedTrackIsActivated = 0;
        $('#track-select-dropdown').append('<option value="' + trackId + '" isactivated="0" completename="' + name + '">' + displayName + '</option>');
        $('#track-select-dropdown').trigger('contentChanged');

        $('#track-select-dropdown').val(trackId);
        $('#track-select-dropdown').material_select();
        tracksDropDownChanged(trackId);
        $('#track-deactivate-button').text('Activate Track');
        $('#track-deactivate-button').removeClass("red");
        $('#track-deactivate-button').removeClass("grey");
        $('#track-deactivate-button').addClass("light-green");

        refreshSelectDropDowns();
    }
}

function clearCreateTrackForm() {
    $("#auth-create-track-trackname").val("");
}

function clickPortAdd() {
    if (addingPort) {
        return;
    }
    addingPort = true;
    var elementID = $("#auth-port-element-button-cancel").data("elementid");
    var internalPort = $("#auth-port-element-internalport").val();
    var externalPort = $("#auth-port-element-externalport").val();
    var isPublicChecked = $("#auth-port-element-publicaccess").is(":checked");
    var publicAccess = 0;

    if (isPublicChecked) {
        publicAccess = 1;
    }

    //Check to see that both the internal and external ports are real numbers
    var internalPort = parseInt(internalPort);
    var externalPort = parseInt(externalPort);

    var fogId = $('#instance-id-' + elementID).attr('data-element-instance-fog');
    var duplicatePort = externalPort == 60400 || externalPort == 10500 || externalPort == 60401 || externalPort == 54321 || externalPort == 55555;

    if (!duplicatePort) {
        for (var i = 0; i < externalPorts.length; i++) {
            var port = externalPorts[i];
            if (port.fog == fogId && port.port == externalPort) {
                duplicatePort = true;
                break;
            }
        }
    }

    if (duplicatePort) {
        Materialize.toast("Port " + externalPort + " is already in use on this fog instance!", toastDelay);
        addingPort = false;
        return;
    }
    if (internalPort > 0 && externalPort > 0 && internalPort < 65534 && externalPort < 65534) {

        var updateFields = "elementId=" + elementID + "&internalPort=" + internalPort + "&externalPort=" + externalPort + "&publicAccess=" + publicAccess + "&trackId=" + selectedTrackId;

        $(document).apwManualPostUp({
            'postingDisplayFunction': 'apwManualPostUpPostingDisplayBlank',
            'postingFinishDisplayFunction': 'apwManualPostUpPostingDisplayFinishBlank',
            'postingResponseHandlerFunction': 'addPortAPI',
            'passToken': 'Y',
            'postURL': fogControllerUrl + ioAuthoring.urls.createElementInstancePort,
            'postFailedFunction': 'addPortAPIFail',
            'postData': updateFields
        });
    } else {
        Materialize.toast("You must enter a valid number for both the internal and external ports", toastDelay);
        addingPort = false;
    }
}

function addPortAPIFail() {
    Materialize.toast("Adding port failed!", toastDelay);
    addingPort = false;
}

function addPortAPI(response) {
    addingPort = false;
    var respObj = $.parseJSON(response);

    if (respObj.status == 'ok') {
        var curPort = respObj.port;

        //Get the elementID behind all this and update its data
        var curElementID = $("#auth-port-element-button-cancel").data("elementid");
        var lookString2 = ".build-element-instance-container.build-element-instance-selected";
        var curElement = $(lookString2);

        var fogId = curElement.attr('data-element-instance-fog');
        externalPorts.push({ fog: fogId, port: curPort.externalPort });

        var portsCopy = curElement.data("ports");
        portsCopy.push(curPort);

        $(curElement).data("ports", portsCopy);

        //Clear the input fields
        $("#auth-port-element-internalport").val("");
        $("#auth-port-element-externalport").val("");
        $("#auth-port-element-publicaccess").attr('checked', false);

        //Create the new screen object and add it to the UI
        var portAnchor = $("#element-ports-anchor");
        var finalAccessString = "None (restricted to edge)";

        if (typeof curPort.accessUrl != 'undefined') {
            if (curPort.accessUrl != null && curPort.accessUrl.length > 0) {
                finalAccessString = curPort.accessUrl;
            }
        }

        var aNewPortItem = $('<div class="input-field col s12 element-ports-item" id="element-ports-item-' + curPort.portId + '"><span class="element-ports-figure">Port inside element: ' + curPort.internalPort + '</span><span class="element-ports-figure">Port exposed on edge: ' + curPort.externalPort + '</span><span class="element-ports-figure">Public access URL: ' + finalAccessString + '</span><a class="materialize-red-text element-ports-figure auth-port-element-remove-port" href="#" data-portid="' + curPort.portId + '" data-networkid="' + curPort.networkPairingId + '"><i class="fa fa-remove"></i></a></div>');

        portAnchor.after(aNewPortItem);
    }
}

function clickPortRemove(data) {
    var portID = data.portID;
    var networkID = data.networkID;

    if (networkID > 0) {
        //Do nothing here - all good
    } else {
        networkID = 0;
    }

    var updateFields = "portId=" + portID + "&networkPairingId=" + networkID;

    $(document).apwManualPostUp({
        'postingDisplayFunction': 'apwManualPostUpPostingDisplayBlank',
        'postingFinishDisplayFunction': 'apwManualPostUpPostingDisplayFinishBlank',
        'postingResponseHandlerFunction': 'removePortAPI',
        'passToken': 'Y',
        'postURL': fogControllerUrl + ioAuthoring.urls.deleteElementInstancePort,
        'postFailedFunction': 'removePortAPIFail',
        'postData': updateFields
    });
}

function removePortAPI(response) {
    var respObj = $.parseJSON(response);

    if (respObj.status == "ok") {
        var thePortID = respObj.portId;

        //Fetch the screen object and update its fields accordingly
        var lookString = "#element-ports-item-" + thePortID;
        var curItemFromDOM = $(lookString);

        //Get the elementID behind all this
        var curElementID = $("#auth-port-element-button-cancel").data("elementid");
        var lookString2 = ".build-element-instance-container.build-element-instance-selected";
        var curElement = $(lookString2);
        var externalPort = 0;

        var portsCopy = curElement.data("ports");

        //Loop through the copy of the ports and remove the matching item
        for (var i = 0; i < portsCopy.length; i++) {
            if (portsCopy[i].portId == thePortID) {
                externalPort = parseInt(portsCopy[i].externalPort);
                portsCopy.splice(i, 1);
            }
        }

        var fogId = curElement.attr('data-element-instance-fog');
        removePortFromList(fogId, externalPort);

        $(curElement).data("ports", portsCopy);
        curItemFromDOM.remove();
    }
}

function removePortFromList(fogId, externalPort) {
    for (var i = 0; i < externalPorts.length; i++) {
        var port = externalPorts[i];

        if (port.fog == fogId && port.port == externalPort) externalPorts.splice(i, 1);
    }
}

function clickPortElement() {
    var instanceObject = $(".build-element-instance-container.build-element-instance-selected");
    var elementID = instanceObject.attr('data-element-instance-id');
    var fogId = instanceObject.attr('data-element-instance-fog');

    if (fogId == 'NONE') {
        Materialize.toast('This element is not assigned to any ioFog instance!', toastDelay);
        $('#portelement').closeModal();
    }

    //Clear the prior list of ports
    var portContainer = $("#element-ports-container");
    var portAnchor = $("#element-ports-anchor");
    portContainer.find("div.element-ports-item").remove();

    //Clear the input fields
    $("#auth-port-element-internalport").val("");
    $("#auth-port-element-externalport").val("");
    $("#auth-port-element-publicaccess").attr('checked', false);

    var ports;

    //If this item exists, get the data and then the configuration string
    if (instanceObject.length) {
        ports = instanceObject.data('ports');
    }

    for (var i = 0; i < ports.length; i++) {
        var curPort = ports[i];

        var finalAccessString = "None (restricted to edge)";

        if (curPort.accessUrl != null && curPort.accessUrl.length > 0) {
            finalAccessString = curPort.accessUrl;
        }

        var aNewPortItem = $('<div class="input-field col s12 element-ports-item" id="element-ports-item-' + curPort.portId + '"><span class="element-ports-figure">Port inside element: ' + curPort.internalPort + '</span><span class="element-ports-figure">Port exposed on edge: ' + curPort.externalPort + '</span><span class="element-ports-figure">Public access URL: ' + finalAccessString + '</span><a class="materialize-red-text element-ports-figure auth-port-element-remove-port" href="#" data-portid="' + curPort.portId + '" data-networkid="' + curPort.networkPairingId + '"><i class="fa fa-remove"></i></a></div>');

        portAnchor.after(aNewPortItem);
    }

    //Put the information into the update element modal window
    //TBD
    $("#auth-port-element-button-cancel").data("elementid", elementID);
}

function connectionMade(info, ev) {
    if (drawingConnections) return;
    var con = info.connection;

    if (con.sourceId == con.targetId) {
        jsPlumb.detach(con);
        return;
    }

    var sourceObject = $('#' + con.sourceId);
    var destinationObject = $('#' + con.targetId);

    var sourceElementId = sourceObject.attr('data-element-instance-id');
    var destinationElementId = destinationObject.attr('data-element-instance-id');

    var sourceFog = sourceObject.attr('data-element-instance-fog');
    var destinationFog = destinationObject.attr('data-element-instance-fog');

    if (sourceFog != "NONE" && destinationFog != "NONE") createRoute(sourceFog, sourceElementId, destinationFog, destinationElementId, selectedTrackId);

    var updateFields = "sourceElementId=" + sourceElementId + "&destinationElementId=" + destinationElementId;

    $(document).apwManualPostUp({
        'postingDisplayFunction': 'apwManualPostUpPostingDisplayBlank',
        'postingFinishDisplayFunction': 'apwManualPostUpPostingDisplayFinishBlank',
        'postingResponseHandlerFunction': 'createConnectionAPI',
        'passToken': 'Y',
        'postURL': fogControllerUrl + ioAuthoring.urls.createElementInstanceConnection,
        'postFailedFunction': 'createConnectionAPIFail',
        'postData': updateFields
    });
}

function createConnectionAPI() {}

function createConnectionAPIFail() {
    alert('save connection failed');
}

function connect(sourceElementInstance, destinationElementInstance) {
    jsPlumb.connect({ uuids: ["instance-id-" + sourceElementInstance + "-out", "instance-id-" + destinationElementInstance + "-in"] });
}

function connectionDetached(info, originalEvent) {
    if (drawingConnections) return;
    var con = info.connection;
    removeConnection(con);
}

function deleteConnectionAPI() {}

function deleteConnectionAPIFail() {
    alert('delete connection failed');
}

function removeConnection(connection) {
    var sourceObject = $('#' + connection.sourceId);
    var destinationObject = $('#' + connection.targetId);

    var sourceElementId = sourceObject.attr('data-element-instance-id');
    var destinationElementId = destinationObject.attr('data-element-instance-id');

    var sourceFog = sourceObject.attr('data-element-instance-fog');
    var destinationFog = destinationObject.attr('data-element-instance-fog');

    if (sourceFog != "NONE" && destinationFog != "NONE") deleteRoute(sourceFog, sourceElementId, destinationFog, destinationElementId, selectedTrackId);

    var updateFields = "sourceElementId=" + sourceElementId + "&destinationElementId=" + destinationElementId;

    $(document).apwManualPostUp({
        'postingDisplayFunction': 'apwManualPostUpPostingDisplayBlank',
        'postingFinishDisplayFunction': 'apwManualPostUpPostingDisplayFinishBlank',
        'postingResponseHandlerFunction': 'deleteConnectionAPI',
        'passToken': 'Y',
        'postURL': fogControllerUrl + ioAuthoring.urls.deleteElementInstanceConnection,
        'postFailedFunction': 'deleteConnectionAPIFail',
        'postData': updateFields
    });
}

function fogInstanceDropDownClickHandler(instanceId) {}

function tracksDropDownChanged(trackId) {
    if (trackId == selectedTrackId) return;

    var apiEndpointUpdateTrack = fogControllerUrl + ioAuthoring.urls.updateTrack;

    var updateFields = "trackId=" + selectedTrackId + "&trackName=" + "&isSelected=0&isActivated=-1";

    $(document).apwManualPostUp({
        'postingDisplayFunction': 'apwManualPostUpPostingDisplayBlank',
        'postingFinishDisplayFunction': 'apwManualPostUpPostingDisplayFinishBlank',
        'postingResponseHandlerFunction': 'apwManualPostUpResponseHandlerBlank',
        'passToken': 'Y',
        'postURL': apiEndpointUpdateTrack,
        'postFailedFunction': 'updateTrackAPIFail',
        'postData': updateFields
    });

    //remove all jsPlumb stuff
    drawingConnections = true;
    jsPlumb.detachEveryConnection();
    jsPlumb.deleteEveryEndpoint();
    drawingConnections = false;

    //hide all element instances for previous track
    $('.build-element-instance-container').remove();

    if (trackId == null) {
        selectedTrackId = -1;
        $('#track-deactivate-button').text('Activate Track');
        $('#track-deactivate-button').removeClass("light-green");
        $('#track-deactivate-button').removeClass("red");
        $('#track-deactivate-button').addClass("grey");
        return;
    }

    setSelectedTrackProperties();

    var updateFields = "trackId=" + selectedTrackId + "&trackName=" + "&isSelected=1&isActivated=-1";

    $(document).apwManualPostUp({
        'postingDisplayFunction': 'apwManualPostUpPostingDisplayBlank',
        'postingFinishDisplayFunction': 'apwManualPostUpPostingDisplayFinishBlank',
        'postingResponseHandlerFunction': 'apwManualPostUpResponseHandlerBlank',
        'passToken': 'Y',
        'postURL': apiEndpointUpdateTrack,
        'postFailedFunction': 'updateTrackAPIFail',
        'postData': updateFields
    });

    //display element instances for current track
    getElementInstances(trackId);
}

function getElementInstances(trackId) {
    var postFields = "";

    $(document).apwManualPostUp({
        'postingDisplayFunction': 'apwManualPostUpPostingDisplayBlank',
        'postingFinishDisplayFunction': 'apwManualPostUpPostingDisplayFinishBlank',
        'postingResponseHandlerFunction': 'getElementInstancesLayouts',
        'passToken': 'Y',
        'postURL': fogControllerUrl + ioAuthoring.urls.getElementInstances + trackId,
        'postFailedFunction': 'deleteConnectionAPIFail',
        'postData': postFields
    });
}

function getElementInstancesLayouts(response) {
    if (response) {
        var result = $.parseJSON(response);
        var postFields = result;

        $(document).apwManualPostUp({
            'postingDisplayFunction': 'apwManualPostUpPostingDisplayBlank',
            'postingFinishDisplayFunction': 'apwManualPostUpPostingDisplayFinishBlank',
            'postingResponseHandlerFunction': 'getElementInstancesAPI',
            'passToken': 'N',
            'postURL': ioAuthoring.urls.getElementInstancesLayouts,
            'postFailedFunction': 'deleteConnectionAPIFail',
            'postData': postFields
        });
    }
}

function getElementInstancesAPI(response) {
    var result = $.parseJSON(response);
    if (result.status != 'ok') return;

    var elementInstances = result.instance;
    $.each(elementInstances, function (index, elementInstance) {
        if (elementInstance.ports == undefined || elementInstance.ports == "") elementInstance.ports = [];else {
            $.each(elementInstance.ports, function (idx, port) {
                externalPorts.push({
                    fog: elementInstance.fogInstanceId,
                    port: parseInt(port.externalport)
                });
            });
        }
        createNewElementInstanceOnCanvas(elementInstance.id, elementInstance.elementInstanceName, elementInstance.elementName, elementInstance.picture, elementInstance.isSelected == null ? 0 : elementInstance.isSelected, elementInstance.layoutX, elementInstance.layoutY, elementInstance.fogInstanceId, elementInstance.config, elementInstance.rootHostAccess, elementInstance.strace, elementInstance.ports, elementInstance.configstring, elementInstance.status, elementInstance.isX86, elementInstance.isArm, elementInstance.logSize, elementInstance.volumeMappings);
        activateElementInstanceDraggable(elementInstance.id);
        activateElementInstanceClickHandler(elementInstance.id);
    });

    drawingConnections = true;
    $.each(elementInstances, function (index, elementInstance) {
        var connections = elementInstance.connections;
        if (connections) {
            $.each(connections, function (index, connection) {
                connect(elementInstance.id, connection);
            });
        }
    });
    drawingConnections = false;

    var instance = $(".build-element-instance-container.build-element-instance-selected");
    var instanceId = instance.attr('data-element-instance-id');
    if (instanceId != undefined) {
        scrollToInstance(instance);
        selectElementInstance(instanceId);
    }
}

function changeTrackActiveStatus() {
    if (selectedTrackId == -1 || selectedTrackId == undefined) {
        Materialize.toast('No tracks exist!', toastDelay);
        return;
    }

    selectedTrackIsActivated = Math.abs(selectedTrackIsActivated - 1);
    $('#track-select-dropdown option:selected').attr('isactivated', selectedTrackIsActivated);

    selectedTrackName = $('#track-select-dropdown option:selected').attr('completename');

    var updateFields = "trackId=" + selectedTrackId + "&trackName=" + selectedTrackName + "&isSelected=-1&isActivated=" + selectedTrackIsActivated;

    $(document).apwManualPostUp({
        'postingDisplayFunction': 'apwManualPostUpPostingDisplayBlank',
        'postingFinishDisplayFunction': 'apwManualPostUpPostingDisplayFinishBlank',
        'postingResponseHandlerFunction': 'apwManualPostUpResponseHandlerBlank',
        'passToken': 'Y',
        'postURL': fogControllerUrl + ioAuthoring.urls.updateTrack,
        'postFailedFunction': 'updateTrackAPIFail',
        'postData': updateFields
    });

    setSelectedTrackProperties();
}

function setSelectedTrackProperties() {
    selectedTrackId = $('#track-select-dropdown option:selected').val();
    if (selectedTrackId == undefined || selectedTrackId == -1) {
        selectedTrackId = -1;
        $('#track-deactivate-button').text('Activate Track');
        $('#track-deactivate-button').removeClass("light-green");
        $('#track-deactivate-button').removeClass("red");
        $('#track-deactivate-button').addClass("grey");
        return;
    }

    selectedTrackIsActivated = $('#track-select-dropdown option:selected').attr('isactivated');
    selectedTrackName = $('#track-select-dropdown option:selected').attr('completename');
    if (selectedTrackIsActivated == 1) {
        $('#track-deactivate-button').text('Deactivate Track');
        $('#track-deactivate-button').addClass("red");
        $('#track-deactivate-button').removeClass("grey");
        $('#track-deactivate-button').removeClass("light-green");
    } else if (selectedTrackIsActivated == 0) {
        $('#track-deactivate-button').text('Activate Track');
        $('#track-deactivate-button').removeClass("red");
        $('#track-deactivate-button').removeClass("grey");
        $('#track-deactivate-button').addClass("light-green");
    }
}

function deleteTrack() {
    var postFields = "trackId=" + selectedTrackId;

    $(document).apwManualPostUp({
        'postingDisplayFunction': 'apwManualPostUpPostingDisplayBlank',
        'postingFinishDisplayFunction': 'apwManualPostUpPostingDisplayFinishBlank',
        'postingResponseHandlerFunction': 'deleteElementInstanceLayouts',
        'passToken': 'Y',
        'postURL': fogControllerUrl + ioAuthoring.urls.deleteUserTrack,
        'postFailedFunction': 'apwManualPostUpFailedBlank',
        'postData': postFields
    });
}

var deleteElementInstanceLayouts = function deleteElementInstanceLayouts(response) {
    var result = $.parseJSON(response);
    var postFields = result;

    $(document).apwManualPostUp({
        'postingDisplayFunction': 'apwManualPostUpPostingDisplayBlank',
        'postingFinishDisplayFunction': 'apwManualPostUpPostingDisplayFinishBlank',
        'postingResponseHandlerFunction': 'deleteTrackAPI',
        'passToken': 'N',
        'postURL': ioAuthoring.urls.deleteElementInstanceLayouts,
        'postFailedFunction': 'apwManualPostUpFailedBlank',
        'postData': postFields
    });
};

function deleteTrackAPI(response) {
    var result = $.parseJSON(response);
    if (result.status != 'ok') return;

    //remove all jsPlumb stuff
    drawingConnections = true;
    jsPlumb.detachEveryConnection();
    jsPlumb.deleteEveryEndpoint();
    drawingConnections = false;

    //hide all element instances for previous track
    $('.build-element-instance-container').remove();

    $('#track-select-dropdown option[value="' + selectedTrackId + '"]').remove();
    tracksDropDownChanged($("#track-select-dropdown").val());
    refreshSelectDropDowns();
    var instance = $(".build-element-instance-container");
    var instanceId = instance.attr('data-element-instance-id');
    if (instanceId != undefined) {
        scrollToInstance(instance);
        selectElementInstance(instanceId);
    } else {
        resetPropertiesPanel();
        refreshSelectDropDowns();
    }
}

function refreshSelectDropDowns() {
    $('#track-select-dropdown').material_select('destroy');
    $('#track-select-dropdown').parent().find('.caret').remove();
    $('#track-select-dropdown').material_select();

    $('#element-instance-properties-fog-instance').material_select('destroy');
    $('#element-instance-properties-fog-instance').parent().find('.caret').remove();
    $('#element-instance-properties-fog-instance').material_select();
}

function validateElementName() {
    var name = $('#element-instance-properties-name').val();
    if (name.trim() == '') {
        Materialize.toast('Element name is empty!', toastDelay);
        $('#element-instance-properties-name').focus();
        return;
    }

    updateElementInstance(name);
}

function portMappingHasConflict(ports, fog) {
    for (var j = 0; j < ports.length; j++) {
        for (var i = 0; i < externalPorts.length; i++) {
            var port = externalPorts[i];
            if (port.fog == fog && port.port == ports[j].externalport) {
                return true;
            }
        }
    }

    return false;
}

function updateElementInstance(name) {
    if (name instanceof Object) {
        name = $('#element-instance-properties-name').val();
        if (name.trim() == '') {
            return;
        }
    }
    var instanceObject = $(".build-element-instance-container.build-element-instance-selected");
    var instanceId = instanceObject.attr('data-element-instance-id');
    var config = encodeURIComponent($('#element-instance-properties-config').val());
    var volumes = $('#element-instance-properties-volumes').val();
    var rootAccess = $('#element-instance-properties-rootaccess').is(':checked') ? "1" : "0";
    var fogInstance = $('#element-instance-properties-fog-instance').val();
    var logSize = $('#element-instance-properties-logsize').val();
    if (isNaN(logSize)) {
        logSize = 10;
        $('#element-instance-properties-logsize').val(logSize);
    }
    if (logSize <= 0) {
        Materialize.toast('Invalid Log File Size!', toastDelay);
        $('#element-instance-properties-logsize').focus();
        return;
    }

    var previousFog = instanceObject.attr('data-element-instance-fog');
    if (previousFog != fogInstance) {

        var isX86 = instanceObject.attr('data-element-instance-x86');
        var isArm = instanceObject.attr('data-element-instance-arm');
        var selectedFogType = $('#element-instance-properties-fog-instance  option:selected').attr('fogTypeId');

        if (fogInstance != 'NONE' && (selectedFogType == 1 && isX86 == 0 || selectedFogType == 2 && isArm == 0)) {
            Materialize.toast('Incompatible fog type!', toastDelay);
            $('#element-instance-properties-fog-instance').val(previousFog);
            refreshSelectDropDowns();
            return;
        }

        var instancePorts = instanceObject.data("ports");
        if (portMappingHasConflict(instancePorts, fogInstance)) {
            Materialize.toast('Port mapping has conflict!', toastDelay);
            $('#element-instance-properties-fog-instance').val(previousFog);
            refreshSelectDropDowns();
            return;
        } else {
            for (var j = 0; j < instancePorts.length; j++) {
                for (var i = 0; i < externalPorts.length; i++) {
                    var port = externalPorts[i];
                    if (port.fog == previousFog && port.port == instancePorts[j].externalport) {
                        externalPorts.splice(i, 1);
                        externalPorts.push({
                            fog: fogInstance,
                            port: instancePorts[j].externalport
                        });
                    }
                }
            }
        }
    }

    if (config.trim() == '') {
        config = '{}';
        $('#element-instance-properties-config').val('{}');
    }

    if (volumes.trim() == '') {
        volumes = '{}';
        $('#element-instance-properties-volumes').val('{}');
    } else if (!validateVolumes(volumes)) {
        Materialize.toast('Invalid volume mappings!', toastDelay);
        $('#element-instance-properties-volumes').focus();
        return;
    }

    var postFields = "instanceId=" + instanceId + "&name=" + name + "&fogInstanceId=" + fogInstance + "&config=" + config + "&rootAccess=" + rootAccess + "&logSize=" + logSize + "&volumes=" + encodeURIComponent(volumes);

    $(document).apwManualPostUp({
        'postingDisplayFunction': 'apwManualPostUpPostingDisplayBlank',
        'postingFinishDisplayFunction': 'apwManualPostUpPostingDisplayFinishBlank',
        'postingResponseHandlerFunction': 'updateElementInstanceAPI',
        'passToken': 'Y',
        'postURL': fogControllerUrl + ioAuthoring.urls.elementInstanceUpdate,
        'postFailedFunction': 'apwManualPostUpFailedBlank',
        'postData': postFields
    });
}

function validateVolumes(volumesStr) {
    try {
        var volumes = JSON.parse(volumesStr);

        if (!volumes.volumemappings || ["array", "object"].indexOf(_typeof(volumes.volumemappings)) === -1) {
            return false;
        }
        for (var i in volumes.volumemappings) {
            var mapping = volumes.volumemappings[i];
            if (!mapping.hostdestination || !mapping.containerdestination) {
                //|| !mapping.accessmode
                return false;
            }
        }
    } catch (e) {
        return false;
    }

    return true;
}

function updateElementInstanceAPI(response) {
    var instanceObject = $(".build-element-instance-container.build-element-instance-selected");
    var instanceId = instanceObject.attr('data-element-instance-id');
    var name = $('#element-instance-properties-name').val();
    var config = escape($('#element-instance-properties-config').val());
    var rootAccess = $('#element-instance-properties-rootaccess').is(':checked') ? "1" : "0";
    var strace = $('#element-instance-properties-strace').is(':checked') ? "1" : "0";
    var fogInstance = $('#element-instance-properties-fog-instance').val();
    var logSize = $('#element-instance-properties-logsize').val();
    if (isNaN(logSize)) {
        logSize = 10;
        $('#element-instance-properties-logsize').val(logSize);
    }

    var oldFogInstance = instanceObject.attr('data-element-instance-fog');

    instanceObject.attr('data-element-instance-name', name);
    instanceObject.attr('data-element-instance-config', config);
    instanceObject.attr('data-element-instance-root', rootAccess);
    instanceObject.attr('data-element-instance-strace', strace);
    instanceObject.attr('data-element-instance-fog', fogInstance);
    instanceObject.attr('data-element-instance-log-size', logSize);

    setObjectName(instanceId, name);

    checkPortsManagementButtonAccess();

    if (oldFogInstance != fogInstance) updateRoutes(instanceId, fogInstance, oldFogInstance);
}

function checkPortsManagementButtonAccess() {
    var instanceObject = $(".build-element-instance-container.build-element-instance-selected");
    var rootAccess = instanceObject.attr('data-element-instance-root');
    var fogInstance = instanceObject.attr('data-element-instance-fog');

    if (rootAccess == 1 || fogInstance == "NONE") {} else {}
}

function removeElementRoutes(thisId, thisFog) {
    jsPlumb.select({ source: "instance-id-" + thisId }).each(function (connection) {
        var other = connection.targetId;

        var otherObject = $('#' + other);

        var otherFog = otherObject.attr('data-element-instance-fog');
        var otherId = otherObject.attr('data-element-instance-id');

        deleteRoute(thisFog, thisId, otherFog, otherId, selectedTrackId);
    });
    jsPlumb.select({ target: "instance-id-" + thisId }).each(function (connection) {
        var other = connection.sourceId;

        var otherObject = $('#' + other);

        var otherFog = otherObject.attr('data-element-instance-fog');
        var otherId = otherObject.attr('data-element-instance-id');

        deleteRoute(otherFog, otherId, thisFog, thisId, selectedTrackId);
    });
}

function deleteRoute(publishingFog, publishingElement, destinationFog, destinationElement, trackId) {
    console.log('deleting route : ' + publishingFog + ' / ' + publishingElement + '  ===>  ' + destinationFog + ' / ' + destinationElement);

    var isNetworkConnection = publishingFog == destinationFog ? 0 : 1;

    var postFields = "publishingInstanceId=" + publishingFog + "&publishingTrackId=" + trackId + "&publishingElementId=" + publishingElement + "&destinationInstanceId=" + destinationFog + "&destinationTrackId=" + trackId + "&destinationElementId=" + destinationElement + "&isNetworkConnection=" + isNetworkConnection;

    $(document).apwManualPostUp({
        'postingDisplayFunction': 'apwManualPostUpPostingDisplayBlank',
        'postingFinishDisplayFunction': 'apwManualPostUpPostingDisplayFinishBlank',
        'postingResponseHandlerFunction': 'deleteRouteAPI',
        'passToken': 'Y',
        'postURL': fogControllerUrl + ioAuthoring.urls.deleteElementInstanceRoute,
        'postFailedFunction': 'apwManualPostUpFailedBlank',
        'postData': postFields
    });
}

function updateRoutes(thisId, thisFog, oldFogInstance) {
    if (thisFog == "NONE") {
        removeElementRoutes(thisId, oldFogInstance);
        return;
    }

    if (oldFogInstance != "NONE") removeElementRoutes(thisId, oldFogInstance);

    jsPlumb.select({ source: "instance-id-" + thisId }).each(function (connection) {
        var other = connection.targetId;

        var otherObject = $('#' + other);

        var otherFog = otherObject.attr('data-element-instance-fog');
        var otherId = otherObject.attr('data-element-instance-id');

        if (otherFog == "NONE") return;

        createRoute(thisFog, thisId, otherFog, otherId, selectedTrackId);
    });
    jsPlumb.select({ target: "instance-id-" + thisId }).each(function (connection) {
        var other = connection.sourceId;

        var otherObject = $('#' + other);

        var otherFog = otherObject.attr('data-element-instance-fog');
        var otherId = otherObject.attr('data-element-instance-id');

        if (otherFog == "NONE") return;

        createRoute(otherFog, otherId, thisFog, thisId, selectedTrackId);
    });
}

function createRoute(publishingFog, publishingElement, destinationFog, destinationElement, trackId) {
    console.log('creating route : ' + publishingFog + ' / ' + publishingElement + '  ===>  ' + destinationFog + ' / ' + destinationElement);

    var postFields = "publishingInstanceId=" + publishingFog + "&publishingElementId=" + publishingElement + "&destinationInstanceId=" + destinationFog + "&destinationElementId=" + destinationElement + "&publishingTrackId=" + trackId + "&destinationTrackId=" + trackId;

    $(document).apwManualPostUp({
        'postingDisplayFunction': 'apwManualPostUpPostingDisplayBlank',
        'postingFinishDisplayFunction': 'apwManualPostUpPostingDisplayFinishBlank',
        'postingResponseHandlerFunction': 'createRouteAPI',
        'passToken': 'Y',
        'postURL': fogControllerUrl + ioAuthoring.urls.createElementInstanceRoute,
        'postFailedFunction': 'apwManualPostUpFailedBlank',
        'postData': postFields
    });
}

function createRouteAPI(response) {
    var result = $.parseJSON(response);
    if (result.status == 'ok') console.log(result.route);else console.log(result.errormessage);
}

function deleteRouteAPI(response) {
    var result = $.parseJSON(response);
    if (result.status == 'ok') console.log('deleted successfully.');else console.log('error: ' + result.errormessage);
}

function deleteSelectedInstance(withCleanUp) {
    var instanceObject = $(".build-element-instance-container.build-element-instance-selected");
    var instanceId = instanceObject.attr('data-element-instance-id');
    var instanceFog = instanceObject.attr('data-element-instance-fog');

    var timer = instanceObject.data('timer');
    if (timer != undefined) {
        clearInterval(timer);
        instanceObject.removeData('timer');
    }

    jsPlumb.select({ source: "instance-id-" + instanceId }).each(function (connection) {
        var other = connection.targetId;

        var otherObject = $('#' + other);

        var otherFog = otherObject.attr('data-element-instance-fog');
        var otherId = otherObject.attr('data-element-instance-id');

        if (otherFog == "NONE") return;

        deleteRoute(instanceFog, instanceId, otherFog, otherId, selectedTrackId);
        jsPlumb.detach(connection);
    });
    jsPlumb.select({ target: "instance-id-" + instanceId }).each(function (connection) {
        var other = connection.sourceId;

        var otherObject = $('#' + other);

        var otherFog = otherObject.attr('data-element-instance-fog');
        var otherId = otherObject.attr('data-element-instance-id');

        if (otherFog == "NONE") return;

        deleteRoute(otherFog, otherId, instanceFog, instanceId, selectedTrackId);
        jsPlumb.detach(connection);
    });

    jsPlumb.remove('instance-id-' + instanceId);

    var postFields = "elementId=" + instanceId + "&withCleanUp=" + withCleanUp;

    $(document).apwManualPostUp({
        'postingDisplayFunction': 'apwManualPostUpPostingDisplayBlank',
        'postingFinishDisplayFunction': 'apwManualPostUpPostingDisplayFinishBlank',
        'postingResponseHandlerFunction': 'apwManualPostUpResponseHandlerBlank',
        'passToken': 'Y',
        'postURL': fogControllerUrl + ioAuthoring.urls.elementInstanceDelete,
        'postFailedFunction': 'apwManualPostUpFailedBlank',
        'postData': postFields
    });
    instanceObject.remove();

    var instance = $(".build-element-instance-container");
    var instanceId = instance.attr('data-element-instance-id');
    if (instanceId != undefined) {
        scrollToInstance(instance);
        selectElementInstance(instanceId);
    } else {
        resetPropertiesPanel();
        refreshSelectDropDowns();
    }
}

function scrollToInstance(instance) {
    var canvas = $('.build-canvas');

    var canvasTop = canvas.scrollTop();
    var canvasLeft = canvas.scrollLeft();

    var elementTop = instance.attr('data-element-instance-layout-y');
    var elementLeft = instance.attr('data-element-instance-layout-x');

    if ((elementTop < canvasTop || elementTop > canvas[0].offsetHeight) && (elementLeft < canvasLeft || elementLeft > canvas[0].offsetWidth)) canvas.animate({ scrollTop: Math.max(elementTop - 50, 0), scrollLeft: Math.max(elementLeft - 50, 0) }, 'slow');else if (elementTop < canvasTop || elementTop > canvas[0].offsetHeight) canvas.animate({ scrollTop: Math.max(elementTop - 50, 0) }, 'slow');else if (elementLeft < canvasLeft || elementLeft > canvas[0].offsetWidth) canvas.animate({ scrollLeft: Math.max(elementLeft - 50, 0) }, 'slow');
}

function findInLayoutDictionary(id) {
    for (var i = 0; i < instancesLayout.length; i++) {
        var item = instancesLayout[i];
        if (item.id == id) return item;
    }

    return undefined;
}

function enableElementStrace() {
    var selectedElement = $('.build-element-instance-container.build-element-instance-selected');
    if (selectedElement == undefined) {
        Materialize.toast('No element selected!', toastDelay);
        return;
    }
    if (selectedElement.attr('data-element-instance-fog') == "NONE") {
        Materialize.toast('This element is not assigned to any ioFog instance!', toastDelay);
        return;
    }
    var elementId = selectedElement.attr('data-element-instance-id');
    var strace = this.checked;
    var postFields = "elementInstanceId=" + elementId + "&strace=" + strace;
    $(document).apwManualPostUp({
        'postingDisplayFunction': 'apwManualPostUpPostingDisplayBlank',
        'postingFinishDisplayFunction': 'apwManualPostUpPostingDisplayFinishBlank',
        'postingResponseHandlerFunction': 'updateElementInstanceAPI',
        'passToken': 'Y',
        'postURL': fogControllerUrl + ioAuthoring.urls.elementInstanceSwitchStrace,
        'postFailedFunction': 'apwManualPostUpFailedBlank',
        'postData': postFields
    });
}

var apwManualPostUpFailedBlank = function apwManualPostUpFailedBlank(response) {};

function resetPropertiesPanel() {
    $('#element-instance-properties-name').val('');
    $('#element-instance-properties-fog-instance').val('NONE');
    $('#element-instance-properties-config').val('');
    $('#element-instance-properties-config-example').text('{}');
    $('#element-instance-properties-element-status').text('UNKNOWN');
    $('#element-instance-properties-rootaccess').prop('checked', false);
    $('#element-instance-properties-strace').prop('checked', false);
}

function rebuildTimer1(elementId) {
    $.ajax({
        type: "GET",
        url: fogControllerUrl + ioAuthoring.urls.elementInstanceRebuildStatus + elementId + '?t=' + accTok,
        dataType: "json",
        success: function success(result) {
            if (result.rebuild == 0) {
                var element = $("div[data-element-instance-id='" + elementId + "']");
                clearInterval(element.data('timer1'));
                element.removeData('timer1');
                console.log('waiting for running!');
                var timer = setInterval(rebuildTimer2, 10000, elementId);
                element.data('timer2', timer);
            }
        }
    });
}

function rebuildTimer2(elementId) {
    $.ajax({
        type: "GET",
        url: fogControllerUrl + ioAuthoring.urls.getElementInstances + selectedTrackId + '?t=' + accTok,
        dataType: "json",
        success: function success(result) {
            var elements = result.instance;
            for (var i = 0; i < elements.length; i++) {
                if (elements[i].id == elementId) {
                    console.log(elements[i].status);
                    if (elements[i].status == 'RUNNING') {
                        var element = $("div[data-element-instance-id='" + elementId + "']");
                        Materialize.toast(element.attr('data-element-instance-name') + ' has been rebuilt!', toastDelay);
                        clearInterval(element.data('timer2'));
                        element.removeData('timer2');
                    }
                }
            }
        }
    });
}

function clickRebuild() {
    var selectedElement = $('.build-element-instance-container.build-element-instance-selected');
    if (selectedElement == undefined) {
        Materialize.toast('No element selected!', toastDelay);
        return;
    }

    if (selectedElement.attr('data-element-instance-fog') == "NONE") {
        Materialize.toast('This element is not assigned to any ioFog instance!', toastDelay);
        return;
    }

    var timer = selectedElement.data('timer1');
    if (timer != undefined) {
        Materialize.toast('The selected element is already being rebuilt!', toastDelay);
        return;
    }

    timer = selectedElement.data('timer2');
    if (timer != undefined) {
        Materialize.toast('The selected element is already being rebuilt!', toastDelay);
        return;
    }

    if (selectedTrackIsActivated != 1) {
        Materialize.toast('The selected track is deactivated!', toastDelay);
        return;
    }

    // var elementStatus = selectedElement.attr('data-element-instance-status');
    // if (elementStatus == undefined || elementStatus != "RUNNING") {
    //     Materialize.toast('The selected element is not running!', toastDelay);
    //     return;
    // }

    Materialize.toast('Rebuilding ' + selectedElement.attr('data-element-instance-name') + '...', toastDelay);
    var elementId = selectedElement.attr('data-element-instance-id');
    selectedElement.attr('data-element-instance-status', 'REBUILD');

    var postFields = "elementId=" + elementId;

    $(document).apwManualPostUp({
        'postingDisplayFunction': 'apwManualPostUpPostingDisplayBlank',
        'postingFinishDisplayFunction': 'apwManualPostUpPostingDisplayFinishBlank',
        'postingResponseHandlerFunction': 'apwManualPostUpResponseHandlerBlank',
        'passToken': 'Y',
        'postURL': fogControllerUrl + ioAuthoring.urls.elementInstanceRebuildUpdate,
        'postFailedFunction': 'apwManualPostUpFailedBlank',
        'postData': postFields
    });
    timer = setInterval(rebuildTimer1, 1000, elementId);
    selectedElement.data('timer1', timer);
}

function clickDownloadImage(instanceId) {
    var selectedElement = $('.build-element-instance-container.build-element-instance-selected');
    var elementId = instanceId || selectedElement.attr('data-element-instance-id');

    var initFetch = { method: 'POST',
        cache: 'default' };

    var urlReq = fogControllerUrl + ioAuthoring.urls.elementInstanceImageSnapshot + '?elementId=' + elementId + '&t=' + accTok;
    var myRequest = new Request(urlReq, initFetch);
    fetch(myRequest).then(function (response) {
        return response.blob();
    }).then(function (myBlob) {
        var objectURL = URL.createObjectURL(myBlob);
        var a = document.createElement('a');
        a.href = objectURL;
        a.download = 'snapshot.tar.gz';
        a.target = '_blank';
        a.click();
    });
}

function clickCreateImage(instanceId) {
    var selectedElement = $('.build-element-instance-container.build-element-instance-selected');
    var elementId = instanceId || selectedElement.attr('data-element-instance-id');

    $.ajax({
        type: "POST",
        url: fogControllerUrl + ioAuthoring.urls.elementInstanceImageSnapshotStatus,
        dataType: "json",
        data: {
            t: accTok,
            elementId: elementId
        },
        success: function success(result) {
            if (!result.status == "ok") {
                console.log('Image Snapshot can not be created.');
            }
        }
    });
}

function clickDownloadStraceFile() {
    var selectedElement = $('.build-element-instance-container.build-element-instance-selected');
    if (selectedElement == undefined) {
        Materialize.toast('No element selected!', toastDelay);
        return;
    }

    if (selectedElement.attr('data-element-instance-fog') == "NONE") {
        Materialize.toast('This element is not assigned to any ioFog instance!', toastDelay);
        return;
    }

    var elementId = selectedElement.attr('data-element-instance-id');
    var postFields = "elementInstanceId=" + elementId;

    $(document).apwManualPostUp({
        'postingDisplayFunction': 'apwManualPostUpPostingDisplayBlank',
        'postingFinishDisplayFunction': 'apwManualPostUpPostingDisplayFinishBlank',
        'postingResponseHandlerFunction': 'getStraceFile',
        'passToken': 'Y',
        'postURL': fogControllerUrl + ioAuthoring.urls.elementInstanceDownloadStraceFile,
        'postFailedFunction': 'apwManualPostUpFailedBlank',
        'postData': postFields
    });
}

function getStraceFile(inResponse) {
    var result = $.parseJSON(inResponse);
    if (result.status == 'ok' && result.convertedFile) {
        var file = atob(result.convertedFile);
        saveAsFile(file, "strace.txt", "text/plain;charset=utf-8");
    } else {
        Materialize.toast('There are no strace logs generated. Tick "Enable Strace" to generate more logs!', toastDelay);
    }
}

function saveAsFile(text, filename, type) {
    try {
        var blob = new Blob([text], { type: type });
        saveAs(blob, filename);
    } catch (e) {
        window.open("data:" + type + "," + encodeURIComponent(text), '_blank', '');
    }
}