function buildServiceStatus(service) {
    var serviceStatus = {};
    for (var key in service['instances']) {
        var instance = service['instances'][key];
        var state = nodeState(instance.checks);

        serviceStatus[state] = (serviceStatus[state] || 0) + 1;
        serviceStatus['total'] = (serviceStatus['total'] || 0) + 1;
    }
    service["status"] = serviceStatus
    return serviceStatus;
}

var timeouts = {};

function debounce(name, func, wait) {
    return function() {
      var context = this,
          args = arguments;
      var later = function () {
          timeouts[name] = null;
          func.apply(context, args);
      };
      if (timeouts[name] != null) {
        //console.log("Clearing timeout", name);
        clearTimeout(timeouts[name]);
      }
      timeouts[name] = setTimeout(later, wait);
    };
};

function padDateUnit(x) {
    return x > 9 ? x : '0' + x;
}

function formatDate(date) {
    return padDateUnit(date.getMonth() + 1) + "/" + padDateUnit(date.getDate()) + " " + padDateUnit(date.getHours()) + ':' + padDateUnit(date.getMinutes()) + ':' + padDateUnit(date.getSeconds());
}

function indexOfTimelineEvent(e) {
    if (e == null) {
        return 'k/0000000000/0000/0000';
    }
    return 'k/' + e.idx.toString().padStart(10, '0') + '/' + e.service + '/' + e.instance;
}

function nodeState(checks) {
    status = 'passing';
    for (var checkKey in checks) {
        switch (checks[checkKey]['status']) {
        case 'passing':
            break;
        case 'warning':
            status = 'warning';
            break;
        case 'critical':
            return 'critical';
            break;
        }
    }
    return status;
}

supported_protocols = ['https', 'http', 'sftp', 'ftp', 'ssh', 'telnet']

function serviceTitleGenerator(instance) {
    var protocol = null;
    for (i in supported_protocols) {
        var protoc = supported_protocols[i]
        if (instance.tags.includes(protoc)) {
            protocol = protoc + '://';
            break;
        }
    }

    var htmlTitle = document.createElement('h5');
    htmlTitle.setAttribute('title', 'Node Name: ' + instance.name +
        '\nAddress: ' + instance.addr +
        '\nService ID: ' + instance.id +
        '\nService Port: ' + instance.port);

    htmlTitle.setAttribute('class', 'instance-name');
    var instanceLink = document.createElement('a');
    var appendPort = "";
    if (instance.port > 0) {
        appendPort = ':' + instance.port;
    }
    if (protocol != null) {
        instanceLink.setAttribute('href', protocol + instance.addr + appendPort);
        instanceLink.setAttribute('target', '_blank');
    }

    instanceLink.appendChild(document.createTextNode(instance.name + appendPort));
    htmlTitle.appendChild(instanceLink);
    htmlTitle.appendChild(document.createTextNode(' '));
    htmlTitle.appendChild(document.createTextNode(instance.addr));

    return htmlTitle;
}

function nodeNameGenator(nodename, nodeaddr) {
    var protocol = 'ssh://'

    var htmlTitle = document.createElement('h5');

    var instanceLink = document.createElement('a');
    instanceLink.setAttribute('class', 'instance-name');
    if (protocol != null) {
        instanceLink.setAttribute('href', protocol + nodeaddr);
        instanceLink.setAttribute('target', '_blank');
    }
    instanceLink.appendChild(document.createTextNode(nodename));
    htmlTitle.appendChild(instanceLink);

    return htmlTitle;
}

function nodeAddressGenator(nodeaddr) {
    var htmlAddress = document.createElement('h5');
    htmlAddress.className = 'instance-addr lookup';
    htmlAddress.appendChild(document.createTextNode(nodeaddr));
    return htmlAddress;
}

function nodeMetaGenerator(nodeMetaTags) {
    var metaTags = document.createElement('div');
    metaTags.setAttribute('title', 'Node Meta')
    metaTags.className = 'node-meta';
    for (var tagKey in nodeMetaTags) {
        if (!nodeMetaTags[tagKey]) {
            continue;
        }
        var metaTag = document.createElement('span');
        metaTag.setAttribute('class', 'badge badge-primary mx-1 lookup');
        metaTag.appendChild(document.createTextNode(tagKey + ':' + nodeMetaTags[tagKey]));
        metaTags.appendChild(metaTag);
    }
    return metaTags;
}

function tagsGenerator(instanceTags) {
    var tags = document.createElement('div');

    tags.className = 'instance-tags';
    tags.setAttribute('title', 'Tags of Service');
    for (var tagKey in instanceTags) {
        var tag = document.createElement('span');
        tag.setAttribute('class', 'badge badge-secondary mx-1 lookup');
        tag.appendChild(document.createTextNode(instanceTags[tagKey]));
        tags.appendChild(tag);
    }
    return tags;
}

function connectGenerator(instance) {
    var connectItem = document.createElement('div');
    var connectValue = instance['connect']
    if (connectValue != null && connectValue["Proxy"]) {
        connectItem.setAttribute('class', 'connect-enabled');
        var badge = document.createElement("span");
        badge.setAttribute("class", "badge badge-primary");
        badge.appendChild(document.createTextNode("Consul Connect Enabled"));
        connectItem.appendChild(badge);
        var content = document.createElement("pre");
        content.setAttribute("class", "connect-data");
        var code = document.createElement("code");
        code.setAttribute("class", "connect-source");
        code.appendChild(document.createTextNode(JSON.stringify(connectValue)));
        content.appendChild(code);
        connectItem.appendChild(content);
        return connectItem;
    } else {
        connectItem.setAttribute('class', 'connect-disabled');
    }
    return connectItem
}

function serviceMetaGenerator(instance, serviceName) {
    var instanceMeta = instance.sMeta;
    var top = document.createElement('div');
    top.className = 'instance-meta';
    if (instanceMeta) {
        var container = document.createElement('dl');
        top.appendChild(container);
        container.className = 'row';
        for (var meta in instanceMeta) {
            var metaH = document.createElement('dt');
            metaH.className = 'col-sm-4 lookup';
            metaH.appendChild(document.createTextNode(meta));
            container.appendChild(metaH);
            var metaVH = document.createElement('dd');
            metaVH.className = 'col-sm-8 lookup';
            metaVH.appendChild(serviceMetaDecorator(instance, meta, instanceMeta[meta], serviceName));
            container.appendChild(metaVH);
        }
    }
    return top;
}

function weightsGenerator(instanceWeights, instanceStatus) {
    var weights = document.createElement('div');
    if (instanceWeights != null) {
        weights.setAttribute('class', 'weights weight-passing badge badge-' + toCSSClass(instanceStatus));
        weights.appendChild(document.createTextNode("Weights: ")); {
            var passing = document.createElement("span");
            passing.setAttribute("class", "weight-passing badge badge-success");
            passing.appendChild(document.createTextNode(instanceWeights['Passing']));
            passing.setAttribute('title', "DNS Weight when in warning state")
            weights.appendChild(passing);
        }
        weights.appendChild(document.createTextNode(' ')); {
            var warning = document.createElement("span");
            warning.setAttribute("class", "weight-warning badge badge-warning");
            warning.setAttribute('title', "DNS Weight when in warning state")
            warning.appendChild(document.createTextNode(instanceWeights['Warning']));
            weights.appendChild(warning);
        }
        return weights;
    } else {
        weights.setAttribute('class', 'weights-absent');
    }
    return weights
}

function toCSSClass(state) {
    switch (state) {
    case 'passing':
        return 'success';
    case 'critical':
        return 'danger';
    }
    return state;
}

function servicesGenerator(instanceServices) {
    var services = document.createElement('div');
    services.className = 'instance-services';
    services.appendChild(document.createTextNode("Services: "));
    services.appendChild(document.createElement('br'));
    for (var serviceKey in instanceServices) {
        var service = document.createElement('a');
        var serviceName = instanceServices[serviceKey]['Service']['Service'];
        var servicePort = instanceServices[serviceKey]['Service']['Port'];
        service.setAttribute('class', 'btn btn-sm m-1 lookup');
        service.setAttribute('target', '_blank');
        nodeAddr = instanceServices[serviceKey]['Service']['Address'];
        service.setAttribute('href', 'http://' + nodeAddr + ':' + servicePort);
        service.classList.add('btn-outline-' + toCSSClass(nodeState(instanceServices[serviceKey]['Checks'])))
        service.appendChild(document.createTextNode(serviceName + ':' + servicePort));
        services.appendChild(service);
    }
    return services;
}

function checksStatusGenerator(instance, prefix) {
    var instanceChecks = instance.checks;
    var checks = document.createElement('div');
    checks.className = 'checks';
    checks.setAttribute('title', 'Checks of Node/Service')

    for (var checkKey in instanceChecks) {
        var checkInstance = instanceChecks[checkKey];
        var checkId = prefix + '::' + checkInstance.checkid;
        var btn = 'btn-' + toCSSClass(instanceChecks[checkKey]['status'])
        var check = document.createElement('div');

        var btnCheck = document.createElement('button');
        btnCheck.setAttribute('class', 'btn ' + btn + ' btn-sm m-1');
        btnCheck.setAttribute('type', 'button');
        btnCheck.setAttribute('data-toggle', 'collapse');
        btnCheck.setAttribute('data-target', '#' + checkId);
        btnCheck.setAttribute('aria-expanded', 'false');
        btnCheck.setAttribute('title', checkInstance.checkid);

        btnCheck.appendChild(document.createTextNode(instanceChecks[checkKey]['name']));

        check.appendChild(btnCheck);

        var collapseCheck = document.createElement('div');
        collapseCheck.setAttribute('class', 'collapse')
        collapseCheck.setAttribute('id', checkId)

        var cardCheck = document.createElement('div');
        cardCheck.setAttribute('class', 'card card-body p-3 m-1 mb-2');

        var notes = document.createElement('table');
        notes.setAttribute('class', 'table table-hover mb-0');

        var dataToDisplay = ['notes', 'output'];
        for (var index in dataToDisplay) {
            var tr = document.createElement('tr');
            var td1 = document.createElement('td');
            var td2 = document.createElement('td');
            var fieldName = dataToDisplay[index].replace(/\b\w/g, l => l.toUpperCase());
            td1.appendChild(document.createTextNode(fieldName + ': '));
            var target = td2
            if (index != 1) {
                target = document.createElement('div');
                target.setAttribute("class", "check-notes")
            } else {
                // Notes are rendered as plain text
                target = document.createElement('pre');
                target.setAttribute("class", "check-output")
            }
            target.appendChild(document.createTextNode(instanceChecks[checkKey][dataToDisplay[index]]));
            td2.appendChild(target)
            tr.appendChild(td1);
            tr.appendChild(td2);
            notes.appendChild(tr);
        }

        cardCheck.appendChild(notes);
        collapseCheck.appendChild(cardCheck);
        check.appendChild(collapseCheck);
        checks.appendChild(check)
    }

    return checks;
}

function resizeAll() {
    resizeWrapper('service-wrapper', 'service-list');
    resizeWrapper('instances-wrapper', 'instances-list');
}

function resizeInstances() {
    resizeWrapper('instances-wrapper', 'instances-list');
}

function resizeData() {
    resizeWrapper('keys-wrapper', 'keys-list');
    resizeWrapper('data-wrapper', 'kv-data');
}

function resizeWrapper(wrapperId, wrapped) {
    var size = $(window).height() - $('#' + wrapperId).offset()["top"] - 20;
    $('#' + wrapperId).css("height", size);
}

function createBadge(classes, data) {
    var badge = document.createElement('span');
    badge.setAttribute('class', 'badge mx-1');
    classesArray = classes.split(' ');
    for (var i = 0; i < classesArray.length; i++) {
        badge.classList.add(classesArray[i]);
    }

    badge.appendChild(document.createTextNode(data));
    return badge;
}

function serviceMatcher(service, regex, showProxiesInList) {
    if (service.getElementsByClassName('service-name')[0].innerHTML.match(regex)) {
        if (!showProxiesInList && service.classList.contains('kind-connect-proxy')) {
            return false;
        }
        return true;
    }
    var tags = service.getElementsByClassName('service-tags')[0].getElementsByClassName('badge');

    for (var i = 0; i < tags.length; i++) {
        if (tags[i].innerHTML.match(regex)) {
            return true;
        }
    }

    var sstatus = service.getAttribute('status')
    if (sstatus && sstatus.match(regex)) {
        return true;
    }

    return false;
}

function keyMatcher(service, regex) {
    if (service.getElementsByClassName('key-name')[0].innerHTML.match(regex)) {
        return true;
    }
}

function hasMatches(instance, regex) {
    var toLookup = instance.getElementsByClassName('lookup');
    for (var i = 0; i < toLookup.length; i++) {
        if (toLookup[i].innerHTML.match(regex)) {
            return true;
        }
    }
    return false;
}

function instanceMatcher(instance, regex) {
    if (instance.getElementsByClassName('instance-name')[0].innerHTML.match(regex)) {
        return true;
    }

    return hasMatches(instance, regex);
}

function nodeMatcher(instance, regex) {
    instanceMatcher(instance, regex);
    if (instance.getElementsByClassName('instance-name')[0].innerHTML.match(regex)) {
        return true;
    }

    return hasMatches(instance, regex);
}

function getTagsNode(node) {
    tags = new Set([]);
    for (service in node['Service']) {
        for (tag in node['Service'][service]['Service']['Tags']) {
            tags.add(node['Service'][service]['Service']['Tags'][tag]);
        }
    }
    return Array.from(tags);
}

function getChecksNode(node) {
    checks = [];
    for (service in node['Service']) {
        for (check in node['Service'][service]['Checks']) {
            checks.push(node['Service'][service]['Checks'][check]);
        }
    }
    return checks;
}

function getGeneralNodeStatus(nodeService) {
    status = 'passing';

    for (var serviceKey in nodeService) {
        if (nodeService[serviceKey] != null && "Checks" in nodeService[serviceKey]) {
            switch (nodeState(nodeService[serviceKey]['Checks']))  {
            case 'passing':
                break;
            case 'warning':
                status = 'warning';
                break;
            case 'critical':
                return 'critical';
                break;
            }
        }
    }
    return status;
}

function updateStatusItem(statuses) {
    states = ['passing', 'warning', 'critical', 'total']
    for (state in states) {
        document.getElementById('service-status-' + states[state]).innerHTML = (statuses[states[state]] || 0);
    }
}

function updateFilterDisplay(filterStatus) {
    $('.progress-status').each(function () {
        var status = $(this).attr('status');
        if (filterStatus == null || filterStatus == status) {
            $(this).removeClass('progress-deactivated');
        } else {
            $(this).addClass('progress-deactivated');
        }
    })
    $('.service-status').each(function () {
        var status = $(this).attr('status');
        if (filterStatus == null || filterStatus == status) {
            $(this).removeClass('status-deactivated');
        } else {
            $(this).addClass('status-deactivated');
        }
    })

}

function updateURL(arg, value) {
    var newUrl = new URL(location.href);
    if (value == "" || value == null) {
        newUrl.searchParams.delete(arg);
    } else {
        newUrl.searchParams.set(arg, value);
    }

    window.history.pushState({}, "", newUrl);
}