/* eslint-disable indent, space-before-blocks, no-unused-vars*/
////////////////////////////
// misc utility functions //
////////////////////////////

function idInArray(id, arr) {
    // because sometimes ids are strings and sometimes they're integers
    for (var index in arr) {
        if (id.toString() === arr[index].toString()) {
            return true;
        }
    }
    return false;
}

function assert(outcome, description) {
    if (!outcome) {
        console.log(description);
    }
}

function templateFromId(id) {
    return _.template($(id).html());
}

function firstCharUpper(s) {
    return s.charAt(0).toUpperCase() + s.slice(1);
}

function toTitleCase(str) {
    return str.replace(/\w\S*/g, function(txt) {
        return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
    });
}

function fieldNameToHumanReadable(str) {
    str = str.replace('_', ' ');
    return toTitleCase(str);
}

// http://stackoverflow.com/questions/2117320/set-maximum-displayed-rows-count-for-html-table
function limitTableRows(tableSelector, maxRows) {
    var table = $(tableSelector)[0]; //document.getElementById(tableId);
    var wrapper = table.parentNode;
    var rowsInTable = table.rows.length;

    try {
        var border = getComputedStyle(table.rows[0].cells[0], '').getPropertyValue('border-top-width');

        border = border.replace('px', '') * 1;
    }
    catch (e) {
        var border = table.rows[0].cells[0].currentStyle.borderWidth;

        border = border.replace('px', '') * 1 / 2;
    }
    var height = 0;

    if (rowsInTable > maxRows) {
        for (var i = 0; i < maxRows; i++) {
            height += table.rows[i].clientHeight + border;
            //console.log('XXX ' + height + ' ' + table.rows[i].clientHeight + ' ' + border);
        }
        wrapper.style.height = height + 'px';
    }
}

function validateField(validatorName, value, obj) {
    if (validatorName === 'notBlank') {
        if (value === undefined || value === null || value === '') {
            return 'can not be blank';
        }
    }

    // if notBlank wasn't set, and the field is blank, then we can return
    if (value === undefined || value === null || value === '') {
        return true;
    }
    /* eslint-disable default-case */
    switch (validatorName) {
    case 'url':
        /* eslint-disable max-len*/
        if (! /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(value)) {
        /* eslint-enable max-len*/
            return 'must be a valid url';
        }
        break;

    case 'portspec':
        if (! $.trim(value).match(portlist_regexp())) {
            return 'must be a valid portspec (example: \'tcp 123, udp 456-789\')';
        }
        break;
    // TODO test this before add
    // default:
    //     return true;
    //     break;
    /* eslint-enable default-case */
    }

    return true;
}

function array_diff(a1, a2) {
    var a = [], diff = [];

    for(var i = 0;i < a1.length;i++) {
        a[a1[i]] = true;
    }

    for(var i = 0;i < a2.length;i++) {
        if(a[a2[i]]) {
            delete a[a2[i]];
        }
        else {
            a[a2[i]] = true;
        }
    }

    for(var k in a) {
        // check that the property in not inherithed
        if ({}.hasOwnProperty.call(a, k)) {
            diff.push(k);
        }
    }

    return diff;
}

function array_subtract(a1, a2) {
    var result = [];

    // why not for ... of, move to lodash instead
    for (var index in a1) {
        if ({}.hasOwnProperty.call(a1, index)) {
            var value = a1[index];

            if (!$.inArray(value, a2) >= 0) {
                result.push(value);
            }
        }

    }
    return result;
}

function array_same_elements(arr1, arr2) {
    // return true if arrays have same elements, even if order is different
    return $(arr1).not(arr2).length === 0 && $(arr2).not(arr1).length === 0;
}

function array_pair_lookup(x, names, values) {
    for (var index in values) {
        if (values[index] === x) {
            return names[index];
        }
    }
    return 'object #' + x;
}

function all_options(selector) {
    var el = $(selector);
    var result = [];

    _.each(el.find('option'), function(option) {
        result.push($(option).val());
    });
    return result;
}

function make_same_width(containerSelector, itemSelector) {
    var maxWidth = 0;

    $(containerSelector).find(itemSelector).each(function() {
        maxWidth = Math.max(maxWidth, $(this).width());
    });

    $(containerSelector).find(itemSelector).each(function() {
        $(this).width(maxWidth);
    });
}

function strip_scripts(s) {
    var div = document.createElement('div');

    div.innerHTML = s;
    var scripts = div.getElementsByTagName('script');
    var i = scripts.length;

    while (i--) {
        scripts[i].parentNode.removeChild(scripts[i]);
    }
    return div.innerHTML;
}

function parse_portlist(ports) {
    /* Support a list of ports in the format 'protocol:port, protocol:port, ...'
        examples:
            tcp 123
            tcp 123:133
            tcp 123, tcp 124, tcp 125, udp 201, udp 202

        User can put either a '/' or a ' ' between protocol and ports
        Port ranges can be specified with '-' or ':'

        This is a straightforward port of the code in core/models/network.py
    */

    var nats = [];

    if (ports) {
        var parts = ports.split(',');
        var parts2, protocol, first, last, portStr;

        $.each(parts, function(index, part) {
            part = $.trim(part);
            if (part.indexOf('/') >= 0) {
                parts2 = part.split('/',2);

                protocol = parts2[0];
                ports = parts2[1];
            }
            else if (part.indexOf(' ') >= 0) {
                parts2 = part.split(' ',2);
                protocol = parts2[0];
                ports = parts2[1];
            }
            else {
                throw 'malformed port specifier ' +
                    part + ', format example: "tcp 123, tcp 201:206, udp 333"';
            }

            protocol = $.trim(protocol);
            ports = $.trim(ports);

            if (protocol !== 'tcp' && protocol !== 'udp') {
                throw 'unknown protocol ' + protocol;
            }

            if (ports.indexOf('-') >= 0) {
                parts2 = ports.split('-');
                first = parseInt($.trim(parts2[0]));
                last = parseInt($.trim(parts2[1]));
                portStr = first + ':' + last;
            }
            else if (ports.indexOf(':') >= 0) {
                parts2 = ports.split(':');
                first = parseInt($.trim(parts2[0]));
                last = parseInt($.trim(parts2[1]));
                portStr = first + ':' + last;
            }
            else {
                portStr = parseInt(ports).toString();
            }

            nats.push({l4_protocol: protocol, l4_port: portStr});
        }); /* end $.each(ports) */
    }
    return nats;
}

function portlist_regexp() {
    /* this constructs the big complicated regexp that validates port
       specifiers. Saved here in long form, in case we need to change it
       in the future.
    */
    var paren, whitespace, protocol,
        protocolSlash, numbers, range,
        protoPorts, protoPortsCommas,
        numbersOrRange, portSpec, multiProtoPorts;

    paren = function(x) {
        return '(?:' + x + ')';
    };

    whitespace = ' *';
    protocol = paren('tcp|udp');
    protocolSlash = protocol + paren(whitespace + '|\/');
    numbers = paren('[0-9]+');
    range = paren(numbers + paren('-|:') + numbers);
    numbersOrRange = paren(numbers + '|' + range);
    protoPorts = paren(protocolSlash + numbersOrRange);
    protoPortsCommas = paren(paren(protoPorts + ',' + whitespace) + '+');
    multiProtoPorts = paren(protoPortsCommas + protoPorts);
    portSpec = '^' + paren(protoPorts + '|' + multiProtoPorts) + '$';
    return RegExp(portSpec);
}

function portlist_selftest() {
    var r = portlist_regexp();

/* eslint-disable quotes, max-len*/
    assert(! "tcp".match(r), 'should not have matched: "tcp"');
    assert("tcp 1".match(r), 'should have matched: "tcp 1"');
    assert("tcp 123".match(r), 'should have matched: "tcp 123"');
    assert("tcp  123".match(r), 'should have matched: "tcp 123"');
    assert("tcp 123-456".match(r), 'should have matched: "tcp 123-456"');
    assert("tcp 123:456".match(r), 'should have matched: "tcp 123:456"');
    assert(! "tcp 123-".match(r), 'should have matched: "tcp 123-"');
    assert(! "tcp 123:".match(r), 'should have matched: "tcp 123:"');
    assert(! "foo 123".match(r), 'should not have matched "foo 123"');
    assert("udp 123".match(r), 'should have matched: "udp 123"');
    assert("tcp 123,udp 456".match(r), 'should have matched: "tcp 123,udp 456"');
    assert("tcp 123, udp 456".match(r), 'should have matched: "tcp 123, udp 456"');
    assert("tcp 123,  udp 456".match(r), 'should have matched: "tcp 123,  udp 456"');
    assert("tcp 123-45, udp 456".match(r), 'should have matched: "tcp 123-45, udp 456"');
    assert("tcp 123-45, udp 456, tcp 11, tcp 22:45, udp 76, udp 47:49, udp 60-61".match(r), 'should have matched: "tcp 123-45, udp 456, tcp 11, tcp 22:45, udp 76, udp 47:49, udp 60-61"');
    /* eslint-enable quotes, max-len*/
    return 'done';
}

//portlist_selftest();


