importScripts(
    '../assets/pdfkit.js',
    '../assets/blob-stream.js'
);


onmessage = function(e) {

    let doc = new PDFDocument(
        {
            size: 'A4',
            layout: 'portrait',
            margin: 15
        }
    );

    let stream = doc.pipe(blobStream());

    for (let fd in e.data.fontData) {
        let cfd = e.data.fontData[fd];
        doc.registerFont(cfd.name, cfd.data);
    }

    // Convenience variables for less cluttered code...
    // font variables
    let romanFont = e.data.fontData.roman.name;
    let boldFont = e.data.fontData.bold.name;

    let fontSize = {
        'base': e.data.baseFontSize,
        'small': e.data.baseFontSize * e.data.fontRatios.small,
        'medium': e.data.baseFontSize * e.data.fontRatios.medium,
        'large': e.data.baseFontSize * e.data.fontRatios.large
    };

    // Content related variables
    let language = e.data.language;
    let title = e.data.title[language];
    let appTitle = e.data.appTitle[language];
    let description = e.data.description[language];
    let summary = e.data.summary[language];
    let referenceClasses = e.data.referenceClasses[language];
    let categories = e.data.labels.categories;
    let faq = e.data.faq;

    // data variables
    let classes = e.data.classes;

    // icon array configuration
    let iconArray = e.data.iconArraySizes[e.data.selectedSize];
    iconArray.size = e.data.selectedSize;
    iconArray.y_blocks = iconArray.size / iconArray.x_blocks / iconArray.blocksize.width / iconArray.blocksize.height;

    // hand-adjusted positioning
    iconArray.x_offset = fontSize.base * 35.625;
    iconArray.y_offset = fontSize.base * 11.25;

    // ***MAGIC NUMBERS*** for conversion from svg web layout to pdf print
    let magicSpacingRatio = 0.425;
    let magicRadiusRatio = 0.49;

    // meta data
    doc.info.Title = title;
    doc.info.Author = appTitle;
    doc.info.Subject = description;

    // -
    // --- Title Page ---
    // -
    // title
    doc.font(boldFont)  // bold font
        .fontSize(fontSize.large)
        .text(
            title,
            3 * fontSize.base,
            10 * fontSize.base,
            { width: 410 }
        );

    // // description
    doc.moveDown(0.4);
    doc.font(boldFont)  // roman font
        .fontSize(fontSize.medium);
    doc.text(description);

    // summary
    doc.moveDown();
    doc.font(romanFont)
        .fontSize(fontSize.medium);
    doc.text(summary, { width: 550, align: 'left' });

    doc.moveDown();

    // faq
    doc.fontSize(fontSize.medium);

    let cellHeight = 0;
    let ty_offset = 25 * fontSize.base;
    let gap = fontSize.base * 2;
    let tableHeight = 0;

    let tfaq = faq.filter(el => el.titlePage === true);  // print faq for title page

    tfaq.forEach(qa => {

        doc.font(boldFont)
            .text(
                qa.question[language],
                3 * fontSize.base,
                tableHeight + ty_offset,
                {columns: 1, width: 150}
            );
        doc.font(romanFont)
            .text(
                qa.answer[language],
                200,
                tableHeight + ty_offset,
                {columns: 1, width: 350}
            );
        cellHeight = doc.heightOfString(qa.answer[language], { lineGap: 0, width: 350 });
        tableHeight += cellHeight + gap;
    });

    // -
    // --- Category: Risks / Benefits ---
    // -


    // Filter benefits
    let benefits = classes.filter(c => c.risk === false);

    if (benefits.length > 0) {
        doc.addPage();
        // Benefits headline
        doc.font(boldFont)  // bold font
            .fontSize(fontSize.large)
            .text(
                categories.benefits[language],
                3 * fontSize.base,
                7 * fontSize.base,
                { width: 410 }
            );

        drawIconArrays(benefits);
    }

    // filter risks
    let risks = classes.filter(c => c.risk === true);

    if (risks.length > 0) {
        doc.addPage();
        // Risks headline
        doc.font(boldFont)  // bold font
            .fontSize(fontSize.large)
            .text(
                categories.risks[language],
                3 * fontSize.base,
                7 * fontSize.base,
                { width: 410 }
            );

        drawIconArrays(risks);
    }

    // last page // source, last update
    doc.addPage();

    let lfaq = faq.filter(el => el.titlePage === false);  // print faq for title page
    tableHeight = 0;
    cellHeight = 0;
    ty_offset = 7 * fontSize.base;

    lfaq.forEach(qa => {

        doc.font(boldFont)
            .text(
                qa.question[language],
                3 * fontSize.base,
                tableHeight + ty_offset,
                {columns: 1, width: 150}
            );
        doc.font(romanFont)
            .text(
                qa.answer[language],
                200,
                tableHeight + ty_offset,
                {columns: 1, width: 350}
            );
        cellHeight = doc.heightOfString(qa.answer[language], { lineGap: 0, width: 350 });
        tableHeight += cellHeight + gap;
    });

    doc.save();
    doc.moveTo(3 * fontSize.base, tableHeight + ty_offset + fontSize.base * 3)
        .lineWidth(1)
        .lineTo(567, tableHeight + ty_offset + fontSize.base * 3)
        .strokeOpacity(0.4)
        .stroke();
    doc.restore();

    // about
    doc.fontSize(fontSize.medium)
        .font(boldFont)
        .text(
            e.data.labels.about.title[language],
            3 * fontSize.base,
            ty_offset + tableHeight + 6 * fontSize.base
        )
        .moveDown()
        .font(romanFont)
        .text(
            e.data.labels.about.text[language],
        );

    // sources
    doc
        .moveDown()
        .text(
            e.data.labels.source[e.data.language],
        )
        .text(
            e.data.reference[e.data.language],
        )
        .moveDown()
        .text(
            e.data.labels.update.label[e.data.language] + ': ' + e.data.lastUpdate[e.data.language],
        );

    // url
    doc
        .font(romanFont)
        .text(
            e.data.url[e.data.language],
            fontSize.base * 3,
            iconArray.y_offset + fontSize.base * 3
        );

    doc.end();  // <<<<<<<<<<<<<<<<<<<<<<< E N D   O F   D O C U M E N T <<<<<<<<<<<<<<<<<<<<<<<<<<

    stream.on('finish', function() {

        let pdfURL = this.toBlobURL('application/pdf');

        postMessage(pdfURL);
    });

    /**
     * Create array for single outcome
     *
     * @param cidx: Class id
     * @param csize: Class size
     * @param iasize: Size of icon array
     *
     * @return: Array filled with samples represented by class id, base event represented by -1
     */
    function createSamplesArray(cidx, csize, iasize) {
        let array = Array(iasize);
        array.fill(-1);
        for (let i = 0; i < csize; ++i) {
            array[i] = cidx;
        }
        return array;
    }

    function drawIconArrays(rbclasses) {
        for (let c = 0; c < rbclasses.length; ++c) {

            if ((c % 2) === 1) {  // shift every second icon array to the right
                doc.moveTo(3 * fontSize.base, 435)
                    .lineWidth(1)
                    .lineTo(567, 435)
                    .strokeOpacity(0.3)
                    .stroke();
                doc.translate(0, 390);
            }

            // TEXT

            // Class name
            doc.font(boldFont)
                .fillColor('#000')
                .fontSize(fontSize.medium)
                .text(
                    rbclasses[c].name[e.data.language],
                    fontSize.base * 3,
                    11 * fontSize.base
                );

            // Reference classes
            doc.font(boldFont)
                .fontSize(fontSize.medium)
                .text(
                    referenceClasses[0],
                    3 * fontSize.base,
                    15 * fontSize.base,
                    { width: 250 }
                )
                .text(
                    referenceClasses[1],
                    3 * fontSize.base + iconArray.x_offset,
                    15 * fontSize.base,
                    { width: 250 }
                );

            // Samples reference group and / or context
            for (let j = 0; j < 2; ++j) {

                doc.fontSize(fontSize.small);

                if (rbclasses[c].samples[j] !== 0) {
                    doc.font(boldFont)
                        .fontSize(fontSize.large)
                        .fillColor(rbclasses[c].colour)
                        .text(
                            rbclasses[c].samples[j],
                            fontSize.base * 3 + j * 285,
                            18 * fontSize.base,
                            {
                                continued: true,
                                width: 250
                            }
                        );
                    doc
                        // origin is top left; move smaller text down to *approximately* fit baseline of larger text
                        // (units seem to be based on font size, so only call moveDown() when font size is the same (small in this case)
                        .fontSize(fontSize.small)
                        .moveDown(0.75)
                        .font(romanFont)
                        .fillColor('#000')
                        .text(
                            ' ' + e.data.labels.of[language] + ' ',
                            { continued: true }
                        );
                    doc
                        .moveDown(-0.75)  // move large text back up (!?)
                        .font(boldFont)
                        .fontSize(fontSize.large)
                        .fillColor(e.data.baseClass.textColour)
                        .text(
                            e.data.selectedSize,
                            { continued: true }
                        );
                    doc.fontSize(fontSize.small)
                        .moveDown(0.75)  // move small text back down
                        .font(romanFont)
                        .fillColor('#000')
                        .text(
                            ' ' + rbclasses[c].context[j][language] + ' ',
                        );
                } else {
                    doc.fontSize(fontSize.small)
                        .font(romanFont)
                        .fillColor('#000')
                        .moveDown(0.75)
                        .text(
                            rbclasses[c].context[j][language],
                            fontSize.base * 3 + j * 285,
                            19 * fontSize.base,
                            { width: 250 }
                        );
                }
            }

            // Icon arrays

            // Set magic ratios according to icon array size
            if (e.data.selectedSize === 100) {
                magicSpacingRatio = 0;
                magicRadiusRatio = 0.4;
            }

            let diameter = parseFloat(iconArray.radius) * 1.18;
            let dotspacing = diameter * iconArray.dotspacingRatio * magicSpacingRatio;
            let blockspacing = diameter * iconArray.blockspacingRatio * 1.5;

            // Left and right icon arrays
            for (let j = 0; j < 2; ++j) {

                let samples = createSamplesArray(rbclasses[c].id, rbclasses[c].samples[j], iconArray.size);

                let iaxfirst = iconArray.size === 100 ? 50 : 0;

                // Set y offsets according to icon array size
                if (iconArray.size === 1000) {
                    iconArray.y_offset = fontSize.base * 24;
                } else {
                    iconArray.y_offset = fontSize.base * 24;
                }

                for (let i = 0; i < samples.length; ++i) {

                    let x_offset = j * iconArray.x_offset + iaxfirst + fontSize.base * 3 + diameter * magicRadiusRatio;  // x-offset per icon array
                    if (e.data.selectedLayout === 0) {
                        doc.ellipse(
                            x_offset +
                            // COLUMN-wise block layout
                            (i % (iconArray.blocksize.width)) * (dotspacing + diameter) +  // x-offset per icon ('line-wrap' after block width)
                            Math.floor(i / (samples.length / iconArray.x_blocks)) *  // blockspacing x: applies to all icons that have an index greater than samples.length / x_blocks)
                                (blockspacing + (iconArray.blocksize.width) * (dotspacing + diameter)), // (?! magic factor) shift icon to the right

                            iconArray.y_offset +  // y-offset
                            Math.floor((i % (samples.length / iconArray.x_blocks)) / (iconArray.blocksize.width)) * (dotspacing + diameter) +  // y-position, column-wise
                            blockspacing * (Math.floor((i % (samples.length / iconArray.x_blocks)) / (iconArray.blocksize.width * iconArray.blocksize.height))), // blockspacing y
                            diameter * magicRadiusRatio  // radius
                        );
                    } else if (e.data.selectedLayout === 1) {
                        doc.ellipse(
                            x_offset +
                            // ROW-wise block layout
                            (i % (iconArray.blocksize.width)) * (dotspacing + diameter) +  // x-offset per icon
                            Math.floor((i / (iconArray.blocksize.width * iconArray.blocksize.height)) % iconArray.x_blocks) *
                                (blockspacing + (iconArray.blocksize.width) * (dotspacing + diameter)),

                            iconArray.y_offset +
                            // read from the inside: reset y-coordinate after each block (index modulo (width*height);
                            // inside that block increase y-coordinate after each line (width), multiply by icon diameter plus spacing
                            (Math.floor((i % (iconArray.blocksize.width * iconArray.blocksize.height) / iconArray.blocksize.width))) * (dotspacing + diameter) +
                            // blockspacing y
                            (iconArray.blocksize.height * (dotspacing + diameter) + blockspacing) *
                                Math.floor((i / (iconArray.blocksize.width * iconArray.blocksize.height * iconArray.x_blocks))),
                            diameter * magicRadiusRatio  // radius
                        );
                    } else if (e.data.selectedLayout === 2) {
                        doc.ellipse(
                            x_offset +
                            // LINE-wise block-spaced layout
                            (i % (iconArray.x_blocks * iconArray.blocksize.width)) * (dotspacing + diameter) +  // x-offset per icon
                            blockspacing * (Math.floor(i / iconArray.blocksize.width) % iconArray.x_blocks),  // blockspacing x: for row-wise layout.

                            iconArray.y_offset +  // y-offset
                            Math.floor(i / (iconArray.x_blocks * iconArray.blocksize.width)) * (dotspacing + diameter) +  // y-position
                            blockspacing * (Math.floor(i / iconArray.blocksize.width / iconArray.blocksize.height / iconArray.x_blocks)),  // blockspacing y
                            diameter * magicRadiusRatio  // radius
                        );
                    }
                    let col = '';
                    if (samples[i] === -1) {
                        col = e.data.baseClass.colour;
                    } else {
                        col = rbclasses[c].colour;
                    }
                    doc.fill(col);
                }

                // If icon array event size is 0: draw white question marks on top!
                let bc = samples.filter(s => s === -1);  // print faq for title page
                if (bc.length === iconArray.size) {
                    doc.fillColor('#ffffff')
                        .font(boldFont)
                        .fontSize(210)
                        .text(
                            '?',
                            14 * fontSize.base + j * 35.5 * fontSize.base,
                            18 * fontSize.base
                        );
                }
            }

            doc.fillColor('#000')
                .fontSize(fontSize.medium)
                .text(
                    rbclasses[c].comment[language],
                    3 * fontSize.base,
                    47 * fontSize.base,
                );
            if ((c % 2) === 1 && c < rbclasses.length - 1) {
                doc.addPage();
            }
        }
    } // drawIconArrays
};
