"use strict";

var __awaiter = this && this.__awaiter || function(thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))((function(resolve, reject) {
        function fulfilled(value) {
            try {
                step(generator.next(value));
            } catch (e) {
                reject(e);
            }
        }
        function rejected(value) {
            try {
                step(generator.throw(value));
            } catch (e) {
                reject(e);
            }
        }
        function step(result) {
            var value;
            result.done ? resolve(result.value) : (value = result.value, value instanceof P ? value : new P((function(resolve) {
                resolve(value);
            }))).then(fulfilled, rejected);
        }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    }));
}, __importDefault = this && this.__importDefault || function(mod) {
    return mod && mod.__esModule ? mod : {
        default: mod
    };
};

Object.defineProperty(exports, "__esModule", {
    value: !0
}), exports.exportMultipleVTablesToExcel = exports.exportVTableToExcel = void 0;

const exceljs_1 = __importDefault(require("exceljs")), encode_1 = require("../util/encode"), style_1 = require("./style"), vtable_1 = require("@visactor/vtable"), vutils_1 = require("@visactor/vutils"), indent_1 = require("../util/indent"), promise_1 = require("../util/promise"), pagination_1 = require("../util/pagination");

function requestIdleCallbackPromise(options) {
    return new Promise((resolve => {
        requestIdleCallback((deadline => {
            resolve(deadline);
        }), options);
    }));
}

function exportVTableToExcel(tableInstance, options, optimization = !1) {
    return __awaiter(this, void 0, void 0, (function*() {
        const workbook = new exceljs_1.default.Workbook, worksheet = workbook.addWorksheet("sheet1");
        yield renderTableToWorksheet(tableInstance, worksheet, workbook, options, optimization);
        return yield workbook.xlsx.writeBuffer();
    }));
}

function exportMultipleVTablesToExcel(tables, options, optimization = !1) {
    return __awaiter(this, void 0, void 0, (function*() {
        const workbook = new exceljs_1.default.Workbook, usedWorksheetNames = new Set;
        for (let i = 0; i < tables.length; i++) {
            const {table: table, name: name} = tables[i], safeName = getUniqueWorksheetName(name || `sheet${i + 1}`, usedWorksheetNames);
            usedWorksheetNames.add(safeName);
            const worksheet = workbook.addWorksheet(safeName);
            yield renderTableToWorksheet(table, worksheet, workbook, options, optimization);
        }
        return yield workbook.xlsx.writeBuffer();
    }));
}

function renderTableToWorksheet(tableInstance, worksheet, workbook, options, optimization = !1) {
    return __awaiter(this, void 0, void 0, (function*() {
        const exportAllData = !!(null == options ? void 0 : options.exportAllData), {handleRowCount: handleRowCount, reset: reset} = (0, 
        pagination_1.handlePaginationExport)(tableInstance, exportAllData);
        try {
            const minRow = 0, maxRow = handleRowCount(), minCol = 0, maxCol = tableInstance.colCount - 1;
            worksheet.properties.defaultRowHeight = 40;
            const columns = [], mergeCells = [], mergeCellSet = new Set, SLICE_SIZE = 100;
            let currentRow = minRow;
            const processSlice = deadline => __awaiter(this, void 0, void 0, (function*() {
                for (;currentRow <= maxRow && (!optimization || (null == deadline ? void 0 : deadline.timeRemaining()) > 0); ) {
                    const endRow = Math.min(currentRow + SLICE_SIZE - 1, maxRow);
                    for (let col = minCol; col <= maxCol; col++) {
                        const colWidth = tableInstance.getColWidth(col);
                        void 0 === columns[col] && (columns[col] = {
                            width: colWidth / 6
                        });
                        for (let row = currentRow; row <= endRow; row++) {
                            if (col === minCol) {
                                const rowHeight = tableInstance.getRowHeight(row);
                                worksheet.getRow(row + 1).height = rowHeight;
                            }
                            yield addCell(col, row, tableInstance, worksheet, workbook, options);
                            const cellRange = tableInstance.getCellRange(col, row);
                            if (cellRange.start.col !== cellRange.end.col || cellRange.start.row !== cellRange.end.row) {
                                const key = `${cellRange.start.col},${cellRange.start.row}:${cellRange.end.col},${cellRange.end.row}`;
                                mergeCellSet.has(key) || (mergeCellSet.add(key), mergeCells.push(cellRange));
                            }
                        }
                    }
                    currentRow = endRow + 1;
                }
                if (currentRow <= maxRow) {
                    const nextDeadline = optimization ? yield requestIdleCallbackPromise() : void 0;
                    yield processSlice(nextDeadline);
                }
            }));
            if (optimization) {
                const deadline = yield requestIdleCallbackPromise();
                yield processSlice(deadline);
            } else yield processSlice();
            worksheet.columns = columns, mergeCells.forEach((mergeCell => {
                worksheet.mergeCells(mergeCell.start.row + 1, mergeCell.start.col + 1, mergeCell.end.row + 1, mergeCell.end.col + 1);
            }));
            const frozenView = [];
            tableInstance.frozenRowCount > 0 && frozenView.push({
                state: "frozen",
                ySplit: tableInstance.frozenRowCount,
                topLeftCell: (0, encode_1.encodeCellAddress)(0, tableInstance.frozenRowCount)
            }), tableInstance.frozenColCount > 0 && frozenView.push({
                state: "frozen",
                xSplit: tableInstance.frozenColCount,
                topLeftCell: (0, encode_1.encodeCellAddress)(tableInstance.frozenColCount, 0)
            }), worksheet.views = frozenView, (null == options ? void 0 : options.excelJSWorksheetCallback) && options.excelJSWorksheetCallback(worksheet);
        } finally {
            reset();
        }
    }));
}

function getUniqueWorksheetName(rawName, used) {
    const cleanedBase = sanitizeWorksheetName(rawName) || "sheet";
    if (!used.has(cleanedBase)) return cleanedBase;
    for (let n = 2; n < 1e4; n++) {
        const suffix = `-${n}`, baseMax = 31 - suffix.length, candidate = `${cleanedBase.length > baseMax ? cleanedBase.slice(0, baseMax) : cleanedBase}${suffix}`;
        if (!used.has(candidate)) return candidate;
    }
    return `${cleanedBase.slice(0, 25)}-${Date.now().toString().slice(-5)}`;
}

function sanitizeWorksheetName(name) {
    const nonEmpty = (null != name ? name : "").toString().trim().replace(/[:\\\/\?\*\[\]]/g, " ").replace(/\s+/g, " ").trim() || "sheet";
    return nonEmpty.length > 31 ? nonEmpty.slice(0, 31) : nonEmpty;
}

function addCell(col, row, tableInstance, worksheet, workbook, options) {
    var _a;
    return __awaiter(this, void 0, void 0, (function*() {
        const {layoutMap: layoutMap} = tableInstance.internalProps, cellType = tableInstance.getCellType(col, row), rawRecord = tableInstance.getCellRawRecord(col, row);
        let cellValue = null !== (_a = rawRecord && rawRecord.vtableMergeName) && void 0 !== _a ? _a : tableInstance.getCellValue(col, row, !1);
        (0, promise_1.isPromise)(cellValue) && (cellValue = yield cellValue);
        const cellStyle = tableInstance.getCellStyle(col, row), cellLocation = tableInstance.getCellLocation(col, row), define = "body" !== cellLocation ? tableInstance.getHeaderDefine(col, row) : tableInstance.getBodyColumnDefine(col, row);
        let icons, customRender, customLayout, cell;
        if (("body" !== cellLocation || ((null == define ? void 0 : define.dragOrder) || !!(null == define ? void 0 : define.icon) || !!(null == define ? void 0 : define.tree))) && (icons = tableInstance.getCellIcons(col, row)), 
        "body" !== cellLocation ? (customRender = null == define ? void 0 : define.headerCustomRender, 
        customLayout = null == define ? void 0 : define.headerCustomLayout) : (customRender = (null == define ? void 0 : define.customRender) || tableInstance.customRender, 
        customLayout = null == define ? void 0 : define.customLayout), null == options ? void 0 : options.formatExportOutput) {
            const cellInfo = {
                cellType: cellType,
                cellValue: cellValue,
                table: tableInstance,
                col: col,
                row: row
            }, formattedValue = options.formatExportOutput(cellInfo);
            if (void 0 !== formattedValue) {
                let cell = worksheet.getCell((0, encode_1.encodeCellAddress)(col, row));
                cell.value = formattedValue, cell.font = (0, style_1.getCellFont)(cellStyle, cellType), 
                cell.fill = (0, style_1.getCellFill)(cellStyle), cell.border = (0, style_1.getCellBorder)(cellStyle);
                const offset = (0, indent_1.getHierarchyOffset)(col, row, tableInstance);
                if (cell.alignment = (0, style_1.getCellAlignment)(cellStyle, Math.ceil(offset / cell.font.size)), 
                cell && (null == options ? void 0 : options.formatExcelJSCell)) {
                    const formatedCell = options.formatExcelJSCell({
                        cellType: cellType,
                        cellValue: cellValue,
                        table: tableInstance,
                        col: col,
                        row: row
                    }, cell);
                    formatedCell && (cell = formatedCell);
                }
                return cell;
            }
        }
        if ("image" === cellType || "video" === cellType || "progressbar" === cellType || "sparkline" === cellType || layoutMap.isAxisCell(col, row) || !(null == options ? void 0 : options.ignoreIcon) && (0, 
        vutils_1.isArray)(icons) && icons.length || customRender || customLayout) if (needCellImageExport(cellType, layoutMap.isAxisCell(col, row), !!customRender || !!customLayout, !((null == options ? void 0 : options.ignoreIcon) || !(0, 
        vutils_1.isArray)(icons) || !icons.length), options)) {
            const cellImageBase64 = exportCellImg(col, row, tableInstance), imageId = workbook.addImage({
                base64: cellImageBase64,
                extension: "png"
            });
            worksheet.addImage(imageId, {
                tl: {
                    col: col + 1 / 80,
                    row: row + 1 / 120
                },
                br: {
                    col: col + 1,
                    row: row + 1
                },
                editAs: "oneCell"
            });
        } else cell = worksheet.getCell((0, encode_1.encodeCellAddress)(col, row)); else if ("text" === cellType || "link" === cellType) {
            let linkUrl;
            if (cell = worksheet.getCell((0, encode_1.encodeCellAddress)(col, row)), "link" === cellType) {
                const templateLink = define.templateLink;
                if (templateLink) {
                    const rowData = tableInstance.getCellOriginRecord(col, row);
                    if (rowData && rowData.vtableMerge) ; else {
                        const cellOriginValue = tableInstance.getCellOriginValue(col, row), data = Object.assign({
                            __value: cellValue,
                            __dataValue: cellOriginValue
                        }, rowData);
                        if ((0, vutils_1.isFunction)(templateLink)) linkUrl = templateLink(data, col, row, tableInstance); else {
                            const re = /\{\s*(\S+?)\s*\}/g;
                            linkUrl = templateLink.replace(re, ((matchs, key) => data[key]));
                        }
                    }
                }
            }
            cell.value = getCellValue(cellValue, cellType, linkUrl), cell.font = (0, style_1.getCellFont)(cellStyle, cellType), 
            cell.fill = (0, style_1.getCellFill)(cellStyle), cell.border = (0, style_1.getCellBorder)(cellStyle);
            const offset = (0, indent_1.getHierarchyOffset)(col, row, tableInstance);
            cell.alignment = (0, style_1.getCellAlignment)(cellStyle, Math.ceil(offset / cell.font.size));
        } else if ("chart" === cellType) if (needCellImageExport("chart", !1, !1, !1, options)) {
            const cellGroup = tableInstance.scenegraph.getCell(col, row);
            (0, vtable_1.renderChart)(cellGroup.firstChild);
            const cellImageBase64 = exportCellImg(col, row, tableInstance), imageId = workbook.addImage({
                base64: cellImageBase64,
                extension: "png"
            });
            worksheet.addImage(imageId, {
                tl: {
                    col: col + 1 / 80,
                    row: row + 1 / 120
                },
                br: {
                    col: col + 1,
                    row: row + 1
                },
                editAs: "oneCell"
            }), tableInstance.scenegraph.updateNextFrame();
        } else cell = worksheet.getCell((0, encode_1.encodeCellAddress)(col, row));
        if (cell && (null == options ? void 0 : options.formatExcelJSCell)) {
            const formatedCell = options.formatExcelJSCell({
                cellType: cellType,
                cellValue: cellValue,
                table: tableInstance,
                col: col,
                row: row
            }, cell);
            formatedCell && (cell = formatedCell);
        }
        return cell;
    }));
}

function getCellValue(cellValue, cellType, linkUrl) {
    return "link" === cellType ? {
        text: cellValue,
        hyperlink: linkUrl || cellValue,
        tooltip: cellValue
    } : cellValue;
}

function exportCellImg(col, row, tableInstance) {
    var _a;
    let cellGroup = tableInstance.scenegraph.getCell(col, row), needRemove = !1;
    "empty" === cellGroup.role && (cellGroup = (0, vtable_1.updateCell)(col, row, tableInstance, !0), 
    cellGroup.setStage(tableInstance.scenegraph.stage), needRemove = !0, cellGroup.stage.dirtyBounds.set(-1 / 0, -1 / 0, 1 / 0, 1 / 0));
    const oldStroke = cellGroup.attribute.stroke;
    cellGroup.attribute.stroke = !1;
    const canvas = vtable_1.graphicUtil.drawGraphicToCanvas(cellGroup, tableInstance.scenegraph.stage);
    return cellGroup.attribute.stroke = oldStroke, needRemove && (null === (_a = cellGroup.parent) || void 0 === _a || _a.removeChild(cellGroup)), 
    canvas.toDataURL();
}

function needCellImageExport(cellType, isAxisCell, isCustomCell, isTextWithIcon, options) {
    return !(null == options ? void 0 : options.skipImageExportCellType) || ("image" === cellType || "video" === cellType || "progressbar" === cellType || "sparkline" === cellType || "chart" === cellType ? !options.skipImageExportCellType.includes(cellType) : isAxisCell ? !options.skipImageExportCellType.includes("chart") : isCustomCell ? !options.skipImageExportCellType.includes("custom") : !isTextWithIcon || !options.skipImageExportCellType.includes("textWithIcon"));
}

exports.exportVTableToExcel = exportVTableToExcel, exports.exportMultipleVTablesToExcel = exportMultipleVTablesToExcel;
//# sourceMappingURL=index.js.map
