// requires acanvas.js also
var THREE_STREAMS_COLORS = ["#ff0000", "#e0e000", "#000080"];
var FONT_ON_IMAGE = "15px sans-serif";

function Axes(context) {
    this.context = context;
    this.lineWidth = 1;
    this.arrowRight = null;
    this.arrowUp = null;
    this.color = "#000000";
    this.arrowShift = 0;

    this.drawAxisX = function (left, right, y) {
        this.context.fillStyle = this.color;
        var fn = this;
        var leftAndHalf = left - fn.lineWidth / 2;
        fn.context.fillRect(
            leftAndHalf, y - fn.lineWidth / 2,
            (right - fn.arrowShift) - leftAndHalf, fn.lineWidth);
        if (this.arrowRight != null) {
            var img = new Image();
            img.src = this.arrowRight;
            img.onload = function () {
                fn.context.drawImage(img, right - img.width, y - img.height / 2);
            }
        }
        return this;
    }

    this.drawAxisY = function (x, top, bottom) {
        this.context.fillStyle = this.color;
        var fn = this;
        var bottomAndHalf = bottom + fn.lineWidth / 2;
        fn.context.fillRect(
            x - fn.lineWidth / 2, top + fn.arrowShift,
            fn.lineWidth, bottomAndHalf - (top + fn.arrowShift));
        if (this.arrowUp != null) {
            var img = new Image();
            img.src = this.arrowUp;
            img.onload = function () {
                fn.context.drawImage(img, x - img.width / 2, top);
            }
        }
        return this;
    }
}

function Hexagram(context) {
    this.context = context;
    this.radius = 50;
    this.lineWidth = 1;
    this.color0 = "#000000";
    this.color1 = "#000000";
    this.colorCircle = "#000000";
    this.colorCross = "#000000";
    this.keyPointRadius = 10;
    this.keyPointYScale = 1.0;
    this.keyPointRadii = null;
    this.keyPointRargudaColor = "#000000";
    this.keyPointRomisakaColor = "#000000";
    this.keyPointRauraColor = "#000000";
    this.xy = [
        {x: 0.0, y: 1.0},
        {x: -Math.sqrt(3.0) / 2.0, y: 0.5},
        {x: -Math.sqrt(3.0) / 2.0, y: -0.5},
        {x: 0.0, y: -1.0},
        {x: Math.sqrt(3.0) / 2.0, y: -0.5},
        {x: Math.sqrt(3.0) / 2.0, y: 0.5}
    ];
    this.xyIntersections = [
        {x: Math.sqrt(3.0) / 3.0, y: 0.0},
        {x: Math.sqrt(3.0) / 6.0, y: 0.5},
        {x: -Math.sqrt(3.0) / 6.0, y: 0.5},
        {x: -Math.sqrt(3.0) / 3.0, y: 0.0},
        {x: -Math.sqrt(3.0) / 6.0, y: -0.5},
        {x: Math.sqrt(3.0) / 6.0, y: -0.5}
    ];
    this.rauraSize = 1.2 * Math.sqrt(3.0) / 6.0;
    this.xyCross = [
        {x: this.rauraSize, y: 0.0},
        {x: 0.0, y: this.rauraSize},
        {x: -this.rauraSize, y: 0.0},
        {x: 0.0, y: -this.rauraSize}
    ];

    this.drawHexagram = function (x, y) {
        this.context.lineCap = "round";
        this.context.lineJoin = "round";
        this.context.lineWidth = this.lineWidth;
        for (var k = 5; k >= 0; k--) {
            this.drawEdge(x, y, k);
        }
        this.drawEdge(x, y, 5, true);
        // - last call is necessary to make interleaving over previous edge
    }

    this.rotate = function(angle) {
        var cos = Math.cos(angle);
        var sin = Math.sin(angle);
        this.rotateAllPoints(this.xy, cos, sin);
        this.rotateAllPoints(this.xyIntersections, cos, sin);
        this.rotateAllPoints(this.xyCross, cos, sin);
    }

    this.drawCircle = function (x, y) {
        this.context.lineWidth = this.lineWidth;
        drawCircle(this.context, x, y, this.radius + this.lineWidth / 2, this.colorCircle);
    }

    this.drawCross = function (x, y, yDistanceForLastPoint) {
        this.context.lineCap = "round";
        this.context.lineWidth = this.lineWidth;
        this.context.strokeStyle = this.colorCross;
        drawLine(this.context,
            x + this.radius * this.xyCross[0].x, y + this.radius * this.xyCross[0].y,
            x + this.radius * this.xyCross[2].x, y + this.radius * this.xyCross[2].y);
        drawLine(this.context,
            x + this.radius * this.xyCross[1].x, y + this.radius * this.xyCross[1].y,
            x + this.radius * this.xyCross[3].x, y + this.radius * this.xyCross[3].y);
        if (yDistanceForLastPoint) {
            drawLine(this.context,
                x, y - this.radius * yDistanceForLastPoint,
                x, y + this.radius * yDistanceForLastPoint);
        }
    }

    this.drawKeyPointsRarguda = function (x, y) {
        for (var k = 0; k < 6; k++) {
            var r = this.keyPointRadii == null ? this.keyPointRadius : this.keyPointRadii[k];
            fillEllipse(
                this.context,
                x + this.radius * this.xy[k].x,
                y + this.radius * this.xy[k].y,
                r, r * this.keyPointYScale, 0.0,
                this.keyPointRargudaColor);
        }
    }

    this.drawKeyPointsRomisaka = function (x, y) {
        for (var k = 0; k < 6; k++) {
            fillEllipse(
                this.context,
                x + this.radius * this.xyIntersections[k].x,
                y + this.radius * this.xyIntersections[k].y,
                this.keyPointRadius, this.keyPointRadius * this.keyPointYScale, 0.0,
                this.keyPointRomisakaColor);
        }
    }

    this.drawKeyPointsRaura = function (x, y, yDistanceForLastPoint) {
        if (yDistanceForLastPoint == null) {
            yDistanceForLastPoint = 0;
        }
        var n = yDistanceForLastPoint == 0 ? 5 : 6;
        for (var k = 0; k < n; k++) {
            fillEllipse(
                this.context,
                x + (k >= 4 ? 0 : this.radius * this.xyCross[k].x),
                y + (k == 4 ? -this.radius * yDistanceForLastPoint :
                    k == 5 ? this.radius * yDistanceForLastPoint :
                    this.radius * this.xyCross[k].y),
                this.keyPointRadius, this.keyPointRadius * this.keyPointYScale, 0.0,
                this.keyPointRauraColor);
        }
    }

    this.drawEdge = function(x, y, index, onlyHalf) {
        // Order of drawing edges is important to make correct interleaving
        // of 2-color lines man/woman/man/woman/...
        this.context.strokeStyle = index % 2 == 0 ? this.color0 : this.color1;
        this.context.beginPath();
        var x1 = x + this.radius * this.xy[index].x;
        var y1 = y + this.radius * this.xy[index].y;
        this.context.moveTo(x1, y1);
        index = (index + 4) % 6;
        var x2 = x + this.radius * this.xy[index].x;
        var y2 = y + this.radius * this.xy[index].y;
        if (onlyHalf) {
            x2 = (x1 + x2) / 2;
            y2 = (y1 + y2) / 2;
        }
        this.context.lineTo(x2, y2);
        this.context.stroke();
    }

    // private
    this.rotateAllPoints = function(points, cos, sin) {
        for (var k = 0; k < points.length; k++) {
            this.rotatePoint(points[k], cos, sin);
        }
    }
    // private
    this.rotatePoint = function(point, cos, sin) {
        var x = point.x;
        var y = point.y;
        point.x = x * cos + y * sin;
        point.y = -x * sin + y * cos;
    }
}

function TriangularPrism(context) {
    this.context = context;
    this.sideLength = 10;
    this.height = 5;
    this.rotationAroundX = 0.0;
    this.zHorizontalSections = [];
    this.color = "#000000";
    this.sectionColor = "#FF0000";

    // Axis x is from left to right, axis z is from top to bottom, axis y is from viewer into picture
    this.drawPrism = function (x, y) {
        this.context.strokeStyle = this.color;
        this.context.lineCap = "round";
        this.context.lineJoin = "round";
        this.context.lineWidth = this.lineWidth;
        var xyzBottom = rotateAroundX(
            regularTriangleVertexes(this.sideLength, -0.5 * this.height), this.rotationAroundX);
        this.drawTriangle(x, y, xyzBottom, false);
        var xyzTop = rotateAroundX(
            regularTriangleVertexes(this.sideLength, 0.5 * this.height), this.rotationAroundX);
        this.drawTriangle(x, y, xyzTop, true);
        for (var k = 0; k < 3; k++) {
            drawLine(this.context, x + xyzBottom[k].x, y + xyzBottom[k].z, x + xyzTop[k].x, y + xyzTop[k].z, false);
        }
        this.context.strokeStyle = this.sectionColor;
        for (var i = 0; i < this.zHorizontalSections.length; i++) {
            var z = (this.zHorizontalSections[i] - 0.5) * this.height;
            var xyz = rotateAroundX(regularTriangleVertexes(this.sideLength, z), this.rotationAroundX);
            this.drawTriangle(x, y, xyz, true);
        }
    }
    // private
    this.drawTriangle = function (x, y, xyz, dashSecond) {
        drawLine(this.context, x + xyz[0].x, y + xyz[0].z, x + xyz[1].x, y + xyz[1].z, false);
        drawLine(this.context, x + xyz[1].x, y + xyz[1].z, x + xyz[2].x, y + xyz[2].z, dashSecond);
        drawLine(this.context, x + xyz[2].x, y + xyz[2].z, x + xyz[0].x, y + xyz[0].z, false);
    }
}

function ThreeStreamSectors(context) {
    this.context = context;
    this.radius = 50;
    this.internalPart = 0.5;
    this.colors = THREE_STREAMS_COLORS;
    this.startAngle = Math.PI / 2;
    this.sectorAngle = 0.45 * Math.PI;

    this.drawSectors = function (x, y, startSectorIndex) {
        if (startSectorIndex == null) {
            startSectorIndex = 0;
        }
        var internalRadius = this.radius * this.internalPart;
        var r = 0.5 * (internalRadius + this.radius);
        var angle = -this.startAngle;
        for (var k = 0; k < 3; k++) {
            var gradient = this.context.createRadialGradient(x, y, internalRadius, x, y, this.radius);
            gradient.addColorStop(0, "#fff");
            gradient.addColorStop(1, this.colors[(3 - startSectorIndex + k) % 3]);
            this.context.fillStyle = gradient;
            this.context.lineCap = "butt";
            this.context.beginPath();
            this.context.moveTo(x, y);
            this.context.arc(x, y, this.radius, angle - 0.5 * this.sectorAngle, angle + 0.5 * this.sectorAngle, false);
            this.context.lineTo(x, y);
            this.context.closePath();
            this.context.fill();
            angle += 2 * Math.PI / 3;
        }
    }
}

function setThreeStream(namePrefix, startSectorIndex, rotem, kim, datan) {
    document.getElementById(namePrefix + (startSectorIndex % 3)).innerHTML = rotem;
    document.getElementById(namePrefix + ((startSectorIndex + 1) % 3)).innerHTML = kim;
    document.getElementById(namePrefix + ((startSectorIndex + 2) % 3)).innerHTML = datan;
}

function BaseLevelConnector(context) {
    this.context = context;
    this.active = -1;
    this.pointRadius = 3;
    this.wireLength = 30;
    this.lineWidth = 3;
    this.step = 30;
    this.angle = 0.0;
    this.passiveColor = "#FF0000";
    this.activeColor = "#00FF00";
    this.otherColor = "#000000";
    this.gapToTitle = 15;
    this.titles = [];
    this.titlesFont = "bold 18px sans-serif";

    this.drawConnector = function(x, y, titles) {
        this.titles = titles;
        this.context.font = this.titlesFont;
        this.drawRotatedLine(x, y, -0.5 * this.wireLength, -this.step, 0.5 * this.wireLength, -this.step, false, 0);
        this.drawRotatedLine(x, y, -0.5 * this.wireLength, 0.0, 0.5 * this.wireLength, 0.0, false, 1);
        this.drawRotatedLine(x, y, -0.5 * this.wireLength, this.step, 0.5 * this.wireLength, this.step, false, 2);
        this.drawRotatedLine(x, y, -0.5 * this.wireLength, -this.step, -0.5 * this.wireLength, this.step, true, -1);
        this.drawRotatedLine(x, y, 0.5 * this.wireLength, -this.step, 0.5 * this.wireLength, this.step, true, -1);
    }

    // private
    this.drawRotatedLine = function(centerX, centerY, x1, y1, x2, y2, dash, index) {
        var cos = Math.cos(-this.angle), sin = Math.sin(-this.angle);
        var rotatedX1 = centerX + x1 * cos + y1 * sin;
        var rotatedY1 = centerY - x1 * sin + y1 * cos;
        var rotatedX2 = centerX + x2 * cos + y2 * sin;
        var rotatedY2 = centerY - x2 * sin + y2 * cos;
        fillCircle(this.context, rotatedX1, rotatedY1, this.pointRadius, this.otherColor);
        fillCircle(this.context, rotatedX2, rotatedY2, this.pointRadius, this.otherColor);
        this.context.strokeStyle = index < 0 ? this.otherColor :
            index == this.active ? this.activeColor : this.passiveColor;
        this.context.lineCap = "round";
        this.context.lineJoin = "round";
        this.context.lineWidth = this.lineWidth;
        drawLine(this.context, rotatedX1, rotatedY1, rotatedX2, rotatedY2, dash);
        if (index >= 0 && this.titles != null && 2 * index < this.titles.length) {
            this.context.fillStyle = this.otherColor;
            this.context.textBaseline = "middle";
            this.context.textAlign = "center";
            var middleX = 0.5 * (rotatedX1 + rotatedX2);
            var middleY = 0.5 * (rotatedY1 + rotatedY2);
            var dX = (rotatedX2 - middleX) * (this.gapToTitle + 0.5 * this.wireLength) / (0.5 * this.wireLength);
            var dY = (rotatedY2 - middleY) * (this.gapToTitle + 0.5 * this.wireLength) / (0.5 * this.wireLength);
            this.context.fillText(this.titles[2 * index], middleX - dX, middleY - dY);
            this.context.fillText(this.titles[2 * index + 1], middleX + dX, middleY + dY);
        }
    }
}

function drawLine(context, x1, y1, x2, y2, dash) {
    context.setLineDash(dash ? [5, 10] : []);
    context.beginPath();
    context.moveTo(x1, y1);
    context.lineTo(x2, y2);
    context.stroke();
}

function drawTriangleBySide(context, x, y, sideLength, rotation, color, contourColor) {
    var xyz = rotateAroundZ(regularTriangleVertexes(sideLength, 0), rotation);
    context.fillStyle = color;
    context.setLineDash([]);
    context.beginPath();
    context.moveTo(x + xyz[0].x, y + xyz[0].y);
    context.lineTo(x + xyz[1].x, y + xyz[1].y);
    context.lineTo(x + xyz[2].x, y + xyz[2].y);
    context.lineTo(x + xyz[0].x, y + xyz[0].y);
    context.closePath();
    context.fill();
    if (contourColor != null) {
        context.strokeStyle = contourColor;
        context.stroke();
    }
}

function regularTriangleVertexes(sideLength, z) {
    return [
        {x: 0.0, y: 2.0 * Math.sqrt(3.0) / 6.0 * sideLength, z: z},
        {x: 0.5 * sideLength, y: -Math.sqrt(3.0) / 6.0 * sideLength, z: z},
        {x: -0.5 * sideLength, y: -Math.sqrt(3.0) / 6.0 * sideLength, z: z}
    ];
}

function ellipseAtBottom(context, part, color) {
    var canvas = context.canvas;
    var w = part * canvas.width;
    var h = part * (canvas.height - 2);
    var x = 0.5 * canvas.width;
    var y = (canvas.height - 2) - 0.5 * h;
    var xScale = w / h;
    context.setTransform(xScale, 0, 0, 1, 0, 0);
    fillCircle(context, x / xScale, y, 0.5 * h, color);
    // - better compatibility than fillEllipse
    context.setTransform(1, 0, 0, 1, 0, 0);
}

function prefixDay7(level) {
    prefixDay(7, level);
}

function prefixDay8(level) {
    prefixDay(8, level);
}

function prefixDay78(level) {
    prefixDay("7<small><small><small><small>&nbsp;</small></small></small></small>/<small><small><small><small>&nbsp;</small></small></small></small>8", level);
}

function prefixDayEmpty(level) {
    prefixDay("&nbsp;", level);
}

// level is not used now
function prefixDay(day, level) {
    document.write('<span class="prefixDay78">'
        + day
        + '</span>&nbsp;&nbsp;');
}