"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.WorkSheetXform = void 0;
const col_cache_1 = require("../../../utils/col-cache.js");
const xml_stream_1 = require("../../../utils/xml-stream.js");
const rel_type_1 = require("../../rel-type.js");
const merges_1 = require("./merges.js");
const base_xform_1 = require("../base-xform.js");
const list_xform_1 = require("../list-xform.js");
const row_xform_1 = require("./row-xform.js");
const col_xform_1 = require("./col-xform.js");
const dimension_xform_1 = require("./dimension-xform.js");
const hyperlink_xform_1 = require("./hyperlink-xform.js");
const merge_cell_xform_1 = require("./merge-cell-xform.js");
const data_validations_xform_1 = require("./data-validations-xform.js");
const sheet_properties_xform_1 = require("./sheet-properties-xform.js");
const sheet_format_properties_xform_1 = require("./sheet-format-properties-xform.js");
const sheet_view_xform_1 = require("./sheet-view-xform.js");
const sheet_protection_xform_1 = require("./sheet-protection-xform.js");
const page_margins_xform_1 = require("./page-margins-xform.js");
const page_setup_xform_1 = require("./page-setup-xform.js");
const print_options_xform_1 = require("./print-options-xform.js");
const auto_filter_xform_1 = require("./auto-filter-xform.js");
const picture_xform_1 = require("./picture-xform.js");
const drawing_xform_1 = require("./drawing-xform.js");
const table_part_xform_1 = require("./table-part-xform.js");
const row_breaks_xform_1 = require("./row-breaks-xform.js");
const col_breaks_xform_1 = require("./col-breaks-xform.js");
const header_footer_xform_1 = require("./header-footer-xform.js");
const conditional_formattings_xform_1 = require("./cf/conditional-formattings-xform.js");
const ext_lst_xform_1 = require("./ext-lst-xform.js");
const ooxml_paths_1 = require("../../../utils/ooxml-paths.js");
const mergeRule = (rule, extRule) => {
    Object.keys(extRule).forEach(key => {
        const value = rule[key];
        const extValue = extRule[key];
        if (value === undefined && extValue !== undefined) {
            rule[key] = extValue;
        }
    });
};
const mergeConditionalFormattings = (model, extModel) => {
    // conditional formattings are rendered in worksheet.conditionalFormatting and also in
    // worksheet.extLst.ext.x14:conditionalFormattings
    // some (e.g. dataBar) are even spread across both!
    if (!extModel || !extModel.length) {
        return model;
    }
    if (!model || !model.length) {
        return extModel;
    }
    // index model rules by x14Id
    const cfMap = {};
    const ruleMap = {};
    model.forEach(cf => {
        cfMap[cf.ref] = cf;
        cf.rules.forEach(rule => {
            const { x14Id } = rule;
            if (x14Id) {
                ruleMap[x14Id] = rule;
            }
        });
    });
    extModel.forEach(extCf => {
        extCf.rules.forEach(extRule => {
            const rule = ruleMap[extRule.x14Id];
            if (rule) {
                // merge with matching rule
                mergeRule(rule, extRule);
            }
            else if (cfMap[extCf.ref]) {
                // reuse existing cf ref
                cfMap[extCf.ref].rules.push(extRule);
            }
            else {
                // create new cf
                model.push({
                    ref: extCf.ref,
                    rules: [extRule]
                });
            }
        });
    });
    // need to cope with rules in extModel that don't exist in model
    return model;
};
class WorkSheetXform extends base_xform_1.BaseXform {
    constructor(options) {
        super();
        const { maxRows, maxCols, ignoreNodes } = options || {};
        this.ignoreNodes = ignoreNodes ?? [];
        this.map = {
            sheetPr: new sheet_properties_xform_1.SheetPropertiesXform(),
            dimension: new dimension_xform_1.DimensionXform(),
            sheetViews: new list_xform_1.ListXform({
                tag: "sheetViews",
                count: false,
                childXform: new sheet_view_xform_1.SheetViewXform()
            }),
            sheetFormatPr: new sheet_format_properties_xform_1.SheetFormatPropertiesXform(),
            cols: new list_xform_1.ListXform({ tag: "cols", count: false, childXform: new col_xform_1.ColXform() }),
            sheetData: new list_xform_1.ListXform({
                tag: "sheetData",
                count: false,
                empty: true,
                childXform: new row_xform_1.RowXform({ maxItems: maxCols }),
                maxItems: maxRows
            }),
            autoFilter: new auto_filter_xform_1.AutoFilterXform(),
            mergeCells: new list_xform_1.ListXform({
                tag: "mergeCells",
                count: true,
                childXform: new merge_cell_xform_1.MergeCellXform()
            }),
            rowBreaks: new row_breaks_xform_1.RowBreaksXform(),
            colBreaks: new col_breaks_xform_1.ColBreaksXform(),
            hyperlinks: new list_xform_1.ListXform({
                tag: "hyperlinks",
                count: false,
                childXform: new hyperlink_xform_1.HyperlinkXform()
            }),
            pageMargins: new page_margins_xform_1.PageMarginsXform(),
            dataValidations: new data_validations_xform_1.DataValidationsXform(),
            pageSetup: new page_setup_xform_1.PageSetupXform(),
            headerFooter: new header_footer_xform_1.HeaderFooterXform(),
            printOptions: new print_options_xform_1.PrintOptionsXform(),
            picture: new picture_xform_1.PictureXform(),
            drawing: new drawing_xform_1.DrawingXform(),
            sheetProtection: new sheet_protection_xform_1.SheetProtectionXform(),
            tableParts: new list_xform_1.ListXform({
                tag: "tableParts",
                count: true,
                childXform: new table_part_xform_1.TablePartXform()
            }),
            conditionalFormatting: new conditional_formattings_xform_1.ConditionalFormattingsXform(),
            extLst: new ext_lst_xform_1.ExtLstXform()
        };
    }
    prepare(model, options) {
        options.merges = new merges_1.Merges();
        model.hyperlinks = options.hyperlinks = [];
        model.comments = options.comments = [];
        // Some Excel builds are surprisingly strict when legacy form controls exist.
        // Emitting a default sheetView (workbookViewId=0) matches typical Excel output
        // and avoids relying on optional element handling.
        if (model.formControls && model.formControls.length > 0) {
            if (!model.views || model.views.length === 0) {
                model.views = [{ workbookViewId: 0 }];
            }
        }
        options.formulae = {};
        options.siFormulae = 0;
        this.map.cols.prepare(model.cols, options);
        this.map.sheetData.prepare(model.rows, options);
        this.map.conditionalFormatting.prepare(model.conditionalFormattings, options);
        model.mergeCells = options.merges.mergeCells;
        // prepare relationships
        const rels = (model.rels = []);
        function nextRid(r) {
            return `rId${r.length + 1}`;
        }
        model.hyperlinks.forEach(hyperlink => {
            const rId = nextRid(rels);
            hyperlink.rId = rId;
            rels.push({
                Id: rId,
                Type: rel_type_1.RelType.Hyperlink,
                Target: hyperlink.target,
                TargetMode: "External"
            });
        });
        // prepare comment relationships
        // Use fileIndex (sequential 1-based) for file naming instead of model.id
        // (the workbook-level sheet ID) because model.id can have gaps when sheets
        // have been deleted, causing a mismatch between the relationship targets
        // and the actual ZIP entry paths written by addWorksheets().
        const { fileIndex } = model;
        if (model.comments.length > 0) {
            const comment = {
                Id: nextRid(rels),
                Type: rel_type_1.RelType.Comments,
                Target: (0, ooxml_paths_1.commentsRelTargetFromWorksheet)(fileIndex)
            };
            rels.push(comment);
            const vmlDrawing = {
                Id: nextRid(rels),
                Type: rel_type_1.RelType.VmlDrawing,
                Target: (0, ooxml_paths_1.vmlDrawingRelTargetFromWorksheet)(fileIndex)
            };
            rels.push(vmlDrawing);
            model.comments.forEach(item => {
                item.refAddress = col_cache_1.colCache.decodeAddress(item.ref);
            });
            options.commentRefs.push({
                commentName: `comments${fileIndex}`,
                vmlDrawing: `vmlDrawing${fileIndex}`
            });
        }
        // Handle pre-loaded drawing (from file read) that may contain charts or other non-image content.
        // Reset anchors and rels so they are rebuilt cleanly from model.media (images) and
        // model.formControls (shapes) below.  Without this reset, every read-write cycle would
        // duplicate image anchors because the same images exist in both model.drawing.anchors
        // (preserved for round-trip) and model.media (the canonical image list).
        // For chart drawings, rels are preserved because the raw XML passthrough references
        // original rIds; anchors are still cleared since they are unused for chart drawings.
        if (model.drawing && model.drawing.anchors) {
            const drawing = model.drawing;
            drawing.rId = nextRid(rels);
            if (!drawing.name) {
                drawing.name = `drawing${++options.drawingsCount}`;
            }
            const hasChartRels = (drawing.rels ?? []).some((rel) => rel.Target && rel.Target.includes("/charts/"));
            // Anchors are always reset: for chart drawings they are unused (raw XML passthrough),
            // for normal drawings they are rebuilt from model.media below.
            drawing.anchors = [];
            if (!hasChartRels) {
                // Non-chart drawings: clear rels so image rels are rebuilt from scratch.
                drawing.rels = [];
            }
            // Chart drawings keep their original rels intact since the raw drawing XML
            // references those rIds directly.
            options.drawings.push(drawing);
            rels.push({
                Id: drawing.rId,
                Type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing",
                Target: (0, ooxml_paths_1.drawingRelTargetFromWorksheet)(drawing.name)
            });
        }
        const drawingRelsHash = [];
        let bookImage;
        model.media.forEach(medium => {
            if (medium.type === "background") {
                const rId = nextRid(rels);
                bookImage = options.media[medium.imageId];
                rels.push({
                    Id: rId,
                    Type: rel_type_1.RelType.Image,
                    Target: (0, ooxml_paths_1.mediaRelTargetFromRels)(`${bookImage.name}.${bookImage.extension}`)
                });
                model.background = {
                    rId
                };
                model.image = options.media[medium.imageId];
            }
            else if (medium.type === "image") {
                let { drawing } = model;
                bookImage = options.media[medium.imageId];
                if (!drawing) {
                    drawing = model.drawing = {
                        rId: nextRid(rels),
                        name: `drawing${++options.drawingsCount}`,
                        anchors: [],
                        rels: []
                    };
                    options.drawings.push(drawing);
                    rels.push({
                        Id: drawing.rId,
                        Type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing",
                        Target: (0, ooxml_paths_1.drawingRelTargetFromWorksheet)(drawing.name)
                    });
                }
                let rIdImage = this.preImageId === medium.imageId
                    ? drawingRelsHash[medium.imageId]
                    : drawingRelsHash[drawing.rels.length];
                if (!rIdImage) {
                    rIdImage = nextRid(drawing.rels);
                    drawingRelsHash[drawing.rels.length] = rIdImage;
                    drawing.rels.push({
                        Id: rIdImage,
                        Type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
                        Target: (0, ooxml_paths_1.mediaRelTargetFromRels)(`${bookImage.name}.${bookImage.extension}`)
                    });
                }
                const anchor = {
                    picture: {
                        rId: rIdImage
                    },
                    range: medium.range
                };
                if (medium.hyperlinks && medium.hyperlinks.hyperlink) {
                    const rIdHyperLink = nextRid(drawing.rels);
                    drawingRelsHash[drawing.rels.length] = rIdHyperLink;
                    anchor.picture.hyperlinks = {
                        tooltip: medium.hyperlinks.tooltip,
                        rId: rIdHyperLink
                    };
                    drawing.rels.push({
                        Id: rIdHyperLink,
                        Type: rel_type_1.RelType.Hyperlink,
                        Target: medium.hyperlinks.hyperlink,
                        TargetMode: "External"
                    });
                }
                this.preImageId = medium.imageId;
                drawing.anchors.push(anchor);
            }
        });
        // prepare tables
        model.tables.forEach(table => {
            // relationships
            const rId = nextRid(rels);
            table.rId = rId;
            rels.push({
                Id: rId,
                Type: rel_type_1.RelType.Table,
                Target: (0, ooxml_paths_1.tableRelTargetFromWorksheet)(table.target)
            });
            // dynamic styles
            table.columns.forEach(column => {
                const { style } = column;
                if (style) {
                    column.dxfId = options.styles.addDxfStyle(style);
                }
            });
        });
        // prepare pivot tables
        (model.pivotTables ?? []).forEach((pivotTable) => {
            rels.push({
                Id: nextRid(rels),
                Type: rel_type_1.RelType.PivotTable,
                Target: (0, ooxml_paths_1.pivotTableRelTargetFromWorksheet)(pivotTable.tableNumber)
            });
        });
        // prepare form controls (legacy checkboxes)
        // Form controls share the VML file with comments, but need separate ctrlProp relationships
        if (model.formControls && model.formControls.length > 0) {
            // Ensure a DrawingML drawing part exists for form controls.
            // Excel often repairs sheets that have legacy controls but no <drawing> part.
            let { drawing } = model;
            if (!drawing) {
                drawing = model.drawing = {
                    rId: nextRid(rels),
                    name: `drawing${++options.drawingsCount}`,
                    anchors: [],
                    rels: []
                };
                options.drawings.push(drawing);
                rels.push({
                    Id: drawing.rId,
                    Type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing",
                    Target: (0, ooxml_paths_1.drawingRelTargetFromWorksheet)(drawing.name)
                });
            }
            // If no comments, we need to add the VML drawing relationship for form controls
            if (model.comments.length === 0) {
                rels.push({
                    Id: nextRid(rels),
                    Type: rel_type_1.RelType.VmlDrawing,
                    Target: (0, ooxml_paths_1.vmlDrawingRelTargetFromWorksheet)(fileIndex)
                });
            }
            // Add hidden DrawingML shapes that bridge to the VML shape ids.
            // This mirrors what Excel writes when it "repairs" legacy form controls.
            const toNativePos = (p) => ({
                nativeCol: p.col,
                nativeColOff: p.colOff,
                nativeRow: p.row,
                nativeRowOff: p.rowOff
            });
            // Add ctrlProp relationships for each form control
            for (const control of model.formControls) {
                const globalCtrlPropId = options.formControlRefs.length + 1;
                control.ctrlPropId = globalCtrlPropId;
                const relId = nextRid(rels);
                control.ctrlPropRelId = relId;
                rels.push({
                    Id: relId,
                    Type: rel_type_1.RelType.CtrlProp,
                    Target: (0, ooxml_paths_1.ctrlPropRelTargetFromWorksheet)(globalCtrlPropId)
                });
                options.formControlRefs.push(globalCtrlPropId);
                const defaultName = `Check Box ${Math.max(1, control.shapeId - 1024)}`;
                drawing.anchors.push({
                    range: {
                        editAs: "absolute",
                        tl: toNativePos(control.tl),
                        br: toNativePos(control.br)
                    },
                    alternateContent: { requires: "a14" },
                    shape: {
                        cNvPrId: control.shapeId,
                        name: control.name || defaultName,
                        hidden: true,
                        spid: `_x0000_s${control.shapeId}`,
                        text: control.text
                    }
                });
            }
        }
        // prepare ext items
        this.map.extLst.prepare(model, options);
    }
    render(xmlStream, model) {
        xmlStream.openXml(xml_stream_1.XmlStream.StdDocAttributes);
        const worksheetAttrs = { ...WorkSheetXform.WORKSHEET_ATTRIBUTES };
        if (model.formControls && model.formControls.length > 0) {
            worksheetAttrs["xmlns:x14"] = "http://schemas.microsoft.com/office/spreadsheetml/2009/9/main";
            worksheetAttrs["xmlns:xdr"] =
                "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing";
            worksheetAttrs["mc:Ignorable"] = `${worksheetAttrs["mc:Ignorable"]} x14`;
        }
        xmlStream.openNode("worksheet", worksheetAttrs);
        const sheetFormatPropertiesModel = model.properties
            ? {
                defaultRowHeight: model.properties.defaultRowHeight,
                dyDescent: model.properties.dyDescent,
                outlineLevelCol: model.properties.outlineLevelCol,
                outlineLevelRow: model.properties.outlineLevelRow,
                customHeight: model.properties.customHeight
            }
            : undefined;
        if (model.properties && model.properties.defaultColWidth) {
            sheetFormatPropertiesModel.defaultColWidth = model.properties.defaultColWidth;
        }
        const sheetPropertiesModel = {
            outlineProperties: model.properties && model.properties.outlineProperties,
            tabColor: model.properties && model.properties.tabColor,
            pageSetup: model.pageSetup && model.pageSetup.fitToPage
                ? {
                    fitToPage: model.pageSetup.fitToPage
                }
                : undefined
        };
        const pageMarginsModel = model.pageSetup && model.pageSetup.margins;
        const printOptionsModel = {
            showRowColHeaders: model.pageSetup && model.pageSetup.showRowColHeaders,
            showGridLines: model.pageSetup && model.pageSetup.showGridLines,
            horizontalCentered: model.pageSetup && model.pageSetup.horizontalCentered,
            verticalCentered: model.pageSetup && model.pageSetup.verticalCentered
        };
        const sheetProtectionModel = model.sheetProtection;
        this.map.sheetPr.render(xmlStream, sheetPropertiesModel);
        this.map.dimension.render(xmlStream, model.dimensions);
        this.map.sheetViews.render(xmlStream, model.views);
        this.map.sheetFormatPr.render(xmlStream, sheetFormatPropertiesModel);
        this.map.cols.render(xmlStream, model.cols);
        this.map.sheetData.render(xmlStream, model.rows);
        this.map.sheetProtection.render(xmlStream, sheetProtectionModel); // Note: must be after sheetData and before autoFilter
        this.map.autoFilter.render(xmlStream, model.autoFilter);
        this.map.mergeCells.render(xmlStream, model.mergeCells);
        this.map.conditionalFormatting.render(xmlStream, model.conditionalFormattings); // Note: must be before dataValidations
        this.map.dataValidations.render(xmlStream, model.dataValidations);
        // For some reason hyperlinks have to be after the data validations
        this.map.hyperlinks.render(xmlStream, model.hyperlinks);
        this.map.printOptions.render(xmlStream, printOptionsModel); // Note: must be before pageMargins
        this.map.pageMargins.render(xmlStream, pageMarginsModel);
        this.map.pageSetup.render(xmlStream, model.pageSetup);
        this.map.headerFooter.render(xmlStream, model.headerFooter);
        this.map.rowBreaks.render(xmlStream, model.rowBreaks);
        this.map.colBreaks.render(xmlStream, model.colBreaks);
        this.map.drawing.render(xmlStream, model.drawing); // Note: must be after rowBreaks/colBreaks
        this.map.picture.render(xmlStream, model.background); // Note: must be after drawing
        if (model.rels) {
            // Add a <legacyDrawing /> node for each VML drawing relationship (comments and/or form controls).
            // NOTE: Excel is picky about worksheet child element order; legacyDrawing must come before controls.
            model.rels.forEach(rel => {
                if (rel.Type === rel_type_1.RelType.VmlDrawing) {
                    xmlStream.leafNode("legacyDrawing", { "r:id": rel.Id });
                }
            });
        }
        // Controls section for legacy form controls (checkboxes, etc.)
        // Excel expects <controls> entries that reference ctrlProp relationships.
        if (model.formControls && model.formControls.length > 0) {
            xmlStream.openNode("mc:AlternateContent");
            xmlStream.openNode("mc:Choice", { Requires: "x14" });
            xmlStream.openNode("controls");
            for (const control of model.formControls) {
                if (!control.ctrlPropRelId) {
                    continue;
                }
                const defaultName = `Check Box ${Math.max(1, control.shapeId - 1024)}`;
                xmlStream.openNode("mc:AlternateContent");
                xmlStream.openNode("mc:Choice", { Requires: "x14" });
                xmlStream.openNode("control", {
                    shapeId: control.shapeId,
                    "r:id": control.ctrlPropRelId,
                    name: control.name || defaultName
                });
                xmlStream.openNode("controlPr", {
                    locked: 0,
                    defaultSize: 0,
                    print: control.print ? 1 : 0,
                    autoFill: 0,
                    autoLine: 0,
                    autoPict: 0
                });
                xmlStream.openNode("anchor");
                xmlStream.openNode("from");
                xmlStream.leafNode("xdr:col", undefined, control.tl.col);
                xmlStream.leafNode("xdr:colOff", undefined, control.tl.colOff);
                xmlStream.leafNode("xdr:row", undefined, control.tl.row);
                xmlStream.leafNode("xdr:rowOff", undefined, control.tl.rowOff);
                xmlStream.closeNode();
                xmlStream.openNode("to");
                xmlStream.leafNode("xdr:col", undefined, control.br.col);
                xmlStream.leafNode("xdr:colOff", undefined, control.br.colOff);
                xmlStream.leafNode("xdr:row", undefined, control.br.row);
                xmlStream.leafNode("xdr:rowOff", undefined, control.br.rowOff);
                xmlStream.closeNode(); // to
                xmlStream.closeNode(); // anchor
                xmlStream.closeNode(); // controlPr
                xmlStream.closeNode(); // control
                xmlStream.closeNode(); // mc:Choice
                xmlStream.leafNode("mc:Fallback");
                xmlStream.closeNode(); // mc:AlternateContent
            }
            xmlStream.closeNode();
            xmlStream.closeNode();
            xmlStream.leafNode("mc:Fallback");
            xmlStream.closeNode();
        }
        // Table parts must come after <controls> in worksheet element order.
        this.map.tableParts.render(xmlStream, model.tables);
        // extLst should be the last element in the worksheet.
        this.map.extLst.render(xmlStream, model);
        xmlStream.closeNode();
    }
    parseOpen(node) {
        if (this.parser) {
            this.parser.parseOpen(node);
            return true;
        }
        if (node.name === "worksheet") {
            Object.values(this.map).forEach((xform) => {
                xform.reset();
            });
            return true;
        }
        if (this.map[node.name] && !this.ignoreNodes.includes(node.name)) {
            this.parser = this.map[node.name];
            this.parser.parseOpen(node);
        }
        return true;
    }
    parseText(text) {
        if (this.parser) {
            this.parser.parseText(text);
        }
    }
    parseClose(name) {
        if (this.parser) {
            if (!this.parser.parseClose(name)) {
                this.parser = undefined;
            }
            return true;
        }
        switch (name) {
            case "worksheet": {
                const properties = this.map.sheetFormatPr.model || {};
                if (this.map.sheetPr.model && this.map.sheetPr.model.tabColor) {
                    properties.tabColor = this.map.sheetPr.model.tabColor;
                }
                if (this.map.sheetPr.model && this.map.sheetPr.model.outlineProperties) {
                    properties.outlineProperties = this.map.sheetPr.model.outlineProperties;
                }
                const sheetProperties = {
                    fitToPage: (this.map.sheetPr.model &&
                        this.map.sheetPr.model.pageSetup &&
                        this.map.sheetPr.model.pageSetup.fitToPage) ||
                        false,
                    margins: this.map.pageMargins.model
                };
                const pageSetup = Object.assign(sheetProperties, this.map.pageSetup.model, this.map.printOptions.model);
                const conditionalFormattings = mergeConditionalFormattings(this.map.conditionalFormatting.model, this.map.extLst.model && this.map.extLst.model["x14:conditionalFormattings"]);
                this.model = {
                    dimensions: this.map.dimension.model,
                    cols: this.map.cols.model,
                    rows: this.map.sheetData.model,
                    mergeCells: this.map.mergeCells.model,
                    hyperlinks: this.map.hyperlinks.model,
                    dataValidations: this.map.dataValidations.model,
                    properties,
                    views: this.map.sheetViews.model,
                    pageSetup,
                    headerFooter: this.map.headerFooter.model,
                    background: this.map.picture.model,
                    drawing: this.map.drawing.model,
                    tables: this.map.tableParts.model,
                    conditionalFormattings,
                    rowBreaks: this.map.rowBreaks.model ?? [],
                    colBreaks: this.map.colBreaks.model ?? []
                };
                if (this.map.autoFilter.model) {
                    this.model.autoFilter = this.map.autoFilter.model;
                }
                if (this.map.sheetProtection.model) {
                    this.model.sheetProtection = this.map.sheetProtection.model;
                }
                return false;
            }
            default:
                // not quite sure how we get here!
                return true;
        }
    }
    reconcile(model, options) {
        // options.merges = new Merges();
        // options.merges.reconcile(model.mergeCells, model.rows);
        const rels = (model.relationships ?? []).reduce((h, rel) => {
            h[rel.Id] = rel;
            if (rel.Type === rel_type_1.RelType.Comments) {
                model.comments = options.comments[rel.Target].comments;
            }
            if (rel.Type === rel_type_1.RelType.VmlDrawing && model.comments && model.comments.length) {
                const vmlComment = options.vmlDrawings[rel.Target].comments;
                model.comments.forEach((comment, index) => {
                    comment.note = Object.assign({}, comment.note, vmlComment[index]);
                });
            }
            return h;
        }, {});
        options.commentsMap = (model.comments ?? []).reduce((h, comment) => {
            if (comment.ref) {
                h[comment.ref] = comment;
            }
            return h;
        }, {});
        options.hyperlinkMap = (model.hyperlinks ?? []).reduce((h, hyperlink) => {
            if (hyperlink.rId) {
                h[hyperlink.address] = rels[hyperlink.rId].Target;
            }
            return h;
        }, {});
        options.formulae = {};
        // compact the rows and cells
        model.rows = model.rows?.filter(Boolean) ?? [];
        model.rows.forEach(row => {
            row.cells = row.cells?.filter(Boolean) ?? [];
        });
        this.map.cols.reconcile(model.cols, options);
        this.map.sheetData.reconcile(model.rows, options);
        this.map.conditionalFormatting.reconcile(model.conditionalFormattings, options);
        model.media = [];
        if (model.drawing) {
            const drawingRel = rels[model.drawing.rId];
            const match = drawingRel.Target.match(/\/drawings\/([a-zA-Z0-9]+)[.][a-zA-Z]{3,4}$/);
            if (match) {
                const drawingName = match[1];
                const drawing = options.drawings[drawingName];
                if (drawing) {
                    // Preserve the drawing object for round-trip (charts, etc.)
                    // This includes the name, anchors, and rels
                    model.drawing = {
                        ...drawing,
                        name: drawingName,
                        rels: options.drawingRels?.[drawingName] ?? drawing.rels ?? []
                    };
                    // Also extract images to model.media for backward compatibility
                    drawing.anchors.forEach(anchor => {
                        if (anchor.medium) {
                            const image = {
                                type: "image",
                                imageId: anchor.medium.index,
                                range: anchor.range,
                                hyperlinks: anchor.picture.hyperlinks
                            };
                            model.media.push(image);
                        }
                    });
                }
            }
        }
        const backgroundRel = model.background && rels[model.background.rId];
        if (backgroundRel) {
            const target = backgroundRel.Target.split("/media/")[1];
            const imageId = options.mediaIndex && options.mediaIndex[target];
            if (imageId !== undefined) {
                model.media.push({
                    type: "background",
                    imageId
                });
            }
        }
        model.tables = (model.tables ?? []).map(tablePart => {
            const rel = rels[tablePart.rId];
            return options.tables[rel.Target];
        });
        // Link pivot tables from relationships to worksheet
        // This is needed so that when writing, the worksheet knows which pivot tables it contains
        model.pivotTables = [];
        (model.relationships ?? []).forEach(rel => {
            if (rel.Type === rel_type_1.RelType.PivotTable && options.pivotTables) {
                const pivotTable = options.pivotTables[rel.Target];
                if (pivotTable) {
                    model.pivotTables.push(pivotTable);
                }
            }
        });
        delete model.relationships;
        delete model.hyperlinks;
        delete model.comments;
    }
}
exports.WorkSheetXform = WorkSheetXform;
WorkSheetXform.WORKSHEET_ATTRIBUTES = {
    xmlns: "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
    "xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
    "xmlns:mc": "http://schemas.openxmlformats.org/markup-compatibility/2006",
    "xmlns:x14ac": "http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac",
    "mc:Ignorable": "x14ac"
};
