'use strict';

// copied from https://www.andronio.me/2017/09/02/pdfkit-tables/

const PDFDocument = require('pdfkit');
class PDFDocumentWithTables extends PDFDocument {
    constructor(options) {
        super(options);
    }

    table(table, arg0, arg1, arg2) {
        let startX = this.page.margins.left, startY = this.y;
        let options = {};

        if ((typeof arg0 === 'number') && (typeof arg1 === 'number')) {
            startX = arg0;
            startY = arg1;

            if (typeof arg2 === 'object')
                options = arg2;
        } else if (typeof arg0 === 'object') {
            options = arg0;
        }

        const columnCount = table.headers.length;
        const columnSpacing = options.columnSpacing || 15;
        const rowSpacing = options.rowSpacing || 5;
        const usableWidth = options.width || (this.page.width - this.page.margins.left - this.page.margins.right);

        const prepareHeader = options.prepareHeader || (() => { });
        const prepareRow = options.prepareRow || (() => { });
        const computeRowHeight = (row, isHeader) => {
            let result = 0;

            row.forEach((cell) => {
                if (!cell) {
                    return;
                }

                let title = cell;
                if (typeof cell === 'object') {
                    let title = cell.title;
                    if (cell.wrap === false && isHeader) {
                        title = '';    
                    }
                }

                const cellHeight = this.heightOfString(title, {
                    width: columnWidth,
                    align: cell.align || 'left'
                });

                result = Math.max(result, cellHeight);
            });

            return result + rowSpacing;
        };

        const columnContainerWidth = usableWidth / columnCount;
        const columnWidth = columnContainerWidth - columnSpacing;
        const maxY = this.page.height - this.page.margins.bottom;

        let rowBottomY = 0;

        this.on('pageAdded', () => {
            startY = this.page.margins.top;
            rowBottomY = 0;
        });

        // Allow the user to override style for headers
        prepareHeader();

        // Check to have enough room for header and first rows
        if (startY + 3 * computeRowHeight(table.headers, true) > maxY)
            this.addPage();

        // Print all headers
        for (const [i, header] of table.headers.entries()) {
            let title = header.title;
            if (header.wrap === false) {
                title = '';    
            }

            const cellHeight = this.heightOfString(title, {
                width: columnWidth,
                align: header.align || 'left'
            });

            this.text(header.title, startX + i * columnContainerWidth, startY, {
                width: columnWidth,
                height: cellHeight,
                align: header.align || 'left'
            });
        }

        // Refresh the y coordinate of the bottom of the headers row
        rowBottomY = Math.max(startY + computeRowHeight(table.headers, true), rowBottomY);

        // Separation line between headers and rows
        this.moveTo(startX, rowBottomY - rowSpacing * 0.5)
            .lineTo(startX + usableWidth, rowBottomY - rowSpacing * 0.5)
            .lineWidth(2)
            .stroke();

        for (const [i, row] of table.rows.entries()) {
            const rowHeight = computeRowHeight(row, false);

            // Switch to next page if we cannot go any further because the space is over.
            // For safety, consider 3 rows margin instead of just one
            if (startY + 3 * rowHeight < maxY)
                startY = rowBottomY + rowSpacing;
            else
                this.addPage();

            // Allow the user to override style for rows
            prepareRow(row, i);

            // Print all cells of the current row
            for (const [i, cell] of row.entries()) {
                let text = cell;

                const options = {
                    width: columnWidth,
                    align: 'left'
                };

                if (cell && cell.type === 'url') {
                    text = cell.title;
                    options.link = cell.href;
                }

                this.text(text, startX + i * columnContainerWidth, startY, options);
            }

            // Refresh the y coordinate of the bottom of this row
            rowBottomY = Math.max(startY + rowHeight, rowBottomY);

            // Separation line between rows
            this.moveTo(startX, rowBottomY - rowSpacing * 0.5)
                .lineTo(startX + usableWidth, rowBottomY - rowSpacing * 0.5)
                .lineWidth(1)
                .opacity(0.7)
                .stroke()
                .opacity(1); // Reset opacity after drawing the line
        }

        this.x = startX;
        this.moveDown();

        return this;
    }
}

module.exports = PDFDocumentWithTables;