/**
 * Converts an HSL color value to RGB. Conversion formula
 * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
 * Assumes h, s, and l are contained in the set [0, 1] and
 * returns r, g, and b in the set [0, 255].
 *
 * @param   {number}  h       The hue
 * @param   {number}  s       The saturation
 * @param   {number}  l       The lightness
 * @return  {Array}           The RGB representation
 */
function hslToRgb(h, s, l) {
    var r, g, b;

    if (s == 0) {
        r = g = b = l; // achromatic
    } else {
        var hue2rgb = function hue2rgb(p, q, t) {
            if (t < 0) t += 1;
            if (t > 1) t -= 1;
            if (t < 1 / 6) return p + (q - p) * 6 * t;
            if (t < 1 / 2) return q;
            if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
            return p;
        }

        var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
        var p = 2 * l - q;
        r = hue2rgb(p, q, h + 1 / 3);
        g = hue2rgb(p, q, h);
        b = hue2rgb(p, q, h - 1 / 3);
    }

    return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
}
function calculateColorScale(i) {
    let hue = (100 - i) * 1.2 / 360;
    let rgb = hslToRgb(hue, 1, .5);
    let ret = 'rgb(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ')';
    return ret;
}

export class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
}

export class Connection {
    constructor(startNode, endNode) {
        this.startNode = startNode;
        this.endNode = endNode;
        this.deliveredFileCount = 0;
        this.crcError = 0;
        this.droppedError = 0;
        this.wrongId = 0;

        this.broken = false;
        this.crcErrorBound = 10;
        this.wrongIdBound = 10;

        this.isHorizontal = false;

        // check if horizontal
        let firstNodes = startNode.split("-");
        let secondNodes = endNode.split("-");
        if (parseInt(firstNodes[0]) != parseInt(secondNodes[0])) {
            this.isHorizontal == true;
        }
    }

    print() {
        console.log("Output: " + this.startNode + ", Input: " + this.endNode + "\nDelivered: " + this.deliveredFileCount + ", CRC_Error: " + this.crcError + ", Dropped: " + this.dropped + ", Wrong_ID: " + this.wrongId);
    }

    isEquals(element) {
        let nodes = element.split(" ");
        nodes[0] = nodes[0].replace("c-", "");
        nodes[1] = nodes[1].replace("c-", "");
        if (nodes[0] === this.startNode && nodes[1] === this.endNode) {
            return true;
        }
        return false;
    }

    getSelector() {
        return ".c-" + this.startNode + ".c-" + this.endNode;
    }

    set delivered(value) {
        if (!Number.isInteger(value)) {
            throw "Value is not a Integer";
        }
        if (value != this.deliveredFileCount) {
            this.deliveredFileCount = value;
        }
    }

    set dropped(drop) {
        this.droppedError = drop;
    }

    set CRC_Error(err) {
        if ((this.crcError < this.crcErrorBound && err >= this.crcErrorBound) || (this.crcError >= this.crcErrorBound && err < this.crcErrorBound)) {
            this.crcError = err;
            this.checkErrorRate();
            return;
        }
        this.crcError = err;
    }

    set wrongID(wrong) {
        if ((this.wrongId < this.wrongIdBound && wrong >= this.wrongIdBound) || (this.wrongId >= this.wrongIdBound && wrong < this.wrongIdBound)) {
            this.wrongId = wrong;
            this.checkErrorRate();
            return;
        }
        this.wrongId = wrong;
    }

    checkErrorRate() {
        if (this.crcError >= this.crcErrorBound || this.wrongId >= this.wrongIdBound) {
            let connections = document.querySelectorAll(this.getSelector());
            for (let connection of connections) {
                connection.style.zIndex = String(101);
                if (connection.getAttribute("class").split(" ")[1] === "c-" + this.startNode) {
                    connection.style.borderStyle = "dashed";
                }
            }
        } else {
            let connections = document.querySelectorAll(this.getSelector());
            for (let connection of connections) {
                connection.style.zIndex = String(Math.floor(this.deliveredFileCount / 1300 * 100));
                if (connection.getAttribute("class").split(" ")[1] === "c-" + this.startNode) {
                    connection.style.borderStyle = "solid"
                }
            }
        }
    }

    updateColor(simulationTime) {
        let connections = document.querySelectorAll(this.getSelector());
        let utilization = this.deliveredFileCount * 128 / (simulationTime / 10);
        if (this.isHorizontal) {
            utilization = utilization / 12000000;
        } else {
            utilization = utilization / 6000000;
        }
        for (let connection of connections) {
            connection.style.zIndex = String(Math.floor(utilization));
            if (connection.getAttribute("class").split(" ")[1] === "c-" + this.startNode) {
                connection.style.borderColor = calculateColorScale(utilization);
            }
        }
    }
}

export class ConnectionNetwork {
    constructor() {
        this.connectionList = [];
    }
    addElement(conn) {
        if (!(conn instanceof Connection)) {
            throw "Not a valid instance of Connection";
        }
        this.connectionList.push(conn);
    }

    getConnection(startend) {
        for (let element of this.connectionList) {
            if (element.isEquals(startend)) {
                return element;
            }
        }
        return -1;
    }

    updateConnection(startend, delivered, crcError, dropped, wrongId, simulationTime) {
        let connection = this.getConnection(startend);
        if (connection == "-1") {
            console.log("Connection " + startend + " wurde nicht gefunden!");
            return false;
        } else {
            connection.delivered = delivered;
            connection.CRC_Error = crcError;
            connection.dropped = dropped;
            connection.wrongID = wrongId;
            connection.updateColor(simulationTime);
            return true;
        }
    }
}