import { colCache } from "./utils/col-cache.js";
import { isEqual } from "./utils/under-dash.js";
import { Enums } from "./enums.js";
const DEFAULT_COLUMN_WIDTH = 9;
/**
 * Column defines the column properties for 1 column.
 * This includes header rows, widths, key, (style), etc.
 * Worksheet will condense the columns as appropriate during serialization
 */
class Column {
    constructor(worksheet, number, defn) {
        this._worksheet = worksheet;
        this._number = number;
        if (defn !== false) {
            // sometimes defn will follow
            this.defn = defn;
        }
    }
    get number() {
        return this._number;
    }
    get worksheet() {
        return this._worksheet;
    }
    /**
     * Column letter key
     */
    get letter() {
        return colCache.n2l(this._number);
    }
    get isCustomWidth() {
        return this.width !== undefined && this.width !== DEFAULT_COLUMN_WIDTH;
    }
    get defn() {
        return {
            header: this._header,
            key: this.key,
            width: this.width,
            style: this.style,
            hidden: this.hidden,
            outlineLevel: this.outlineLevel,
            bestFit: this.bestFit
        };
    }
    set defn(value) {
        if (value) {
            this.key = value.key;
            this.width = value.width !== undefined ? value.width : DEFAULT_COLUMN_WIDTH;
            this.outlineLevel = value.outlineLevel;
            if (value.style) {
                this.style = value.style;
            }
            else {
                this.style = {};
            }
            // headers must be set after style
            this.header = value.header;
            this._hidden = !!value.hidden;
            this.bestFit = value.bestFit;
        }
        else {
            delete this._header;
            delete this._key;
            delete this.width;
            this.style = {};
            this.outlineLevel = 0;
            delete this.bestFit;
        }
    }
    /**
     * Get header values as an array (for multi-row header support)
     */
    get headers() {
        if (Array.isArray(this._header)) {
            return this._header;
        }
        if (this._header !== undefined) {
            return [this._header];
        }
        return [];
    }
    /**
     * Can be a single value or an array for multi-row headers.
     * Supports any CellValue type including Date, number, string, etc.
     */
    get header() {
        return this._header;
    }
    set header(value) {
        if (value !== undefined) {
            this._header = value;
            this.headers.forEach((cellValue, index) => {
                this._worksheet.getCell(index + 1, this.number).value = cellValue;
            });
        }
        else {
            this._header = undefined;
        }
    }
    /**
     * The name of the properties associated with this column in each row
     */
    get key() {
        return this._key;
    }
    set key(value) {
        const column = this._key && this._worksheet.getColumnKey(this._key);
        if (column === this) {
            this._worksheet.deleteColumnKey(this._key);
        }
        this._key = value;
        if (value) {
            this._worksheet.setColumnKey(this._key, this);
        }
    }
    /**
     * Hides the column
     */
    get hidden() {
        return !!this._hidden;
    }
    set hidden(value) {
        this._hidden = value;
    }
    /**
     * Set an outline level for columns
     */
    get outlineLevel() {
        return this._outlineLevel ?? 0;
    }
    set outlineLevel(value) {
        this._outlineLevel = value;
    }
    /**
     * Indicate the collapsed state based on outlineLevel
     */
    get collapsed() {
        return !!(this._outlineLevel && this._outlineLevel >= this._worksheet.properties.outlineLevelCol);
    }
    toString() {
        return JSON.stringify({
            key: this.key,
            width: this.width,
            headers: this.headers.length ? this.headers : undefined
        });
    }
    equivalentTo(other) {
        return (this.width === other.width &&
            this.hidden === other.hidden &&
            this.outlineLevel === other.outlineLevel &&
            isEqual(this.style, other.style));
    }
    equivalentToModel(model) {
        return (this.width === model.width &&
            this.hidden === model.hidden &&
            this.outlineLevel === model.outlineLevel &&
            this.bestFit === model.bestFit &&
            isEqual(this.style, model.style));
    }
    get isDefault() {
        if (this.isCustomWidth) {
            return false;
        }
        if (this.hidden) {
            return false;
        }
        if (this.outlineLevel) {
            return false;
        }
        if (this.bestFit) {
            return false;
        }
        const s = this.style;
        if (s && (s.font || s.numFmt || s.alignment || s.border || s.fill || s.protection)) {
            return false;
        }
        return true;
    }
    get headerCount() {
        return this.headers.length;
    }
    eachCell(optionsOrCallback, maybeCallback) {
        const colNumber = this.number;
        let options;
        let callback;
        if (typeof optionsOrCallback === "function") {
            options = {};
            callback = optionsOrCallback;
        }
        else {
            options = optionsOrCallback;
            callback = maybeCallback;
        }
        this._worksheet.eachRow(options, (row, rowNumber) => {
            callback(row.getCell(colNumber), rowNumber);
        });
    }
    /**
     * The cell values in the column
     */
    get values() {
        const v = [];
        this.eachCell((cell, rowNumber) => {
            if (cell && cell.type !== Enums.ValueType.Null) {
                v[rowNumber] = cell.value;
            }
        });
        return v;
    }
    set values(v) {
        if (!v) {
            return;
        }
        const colNumber = this.number;
        let offset = 0;
        if (Object.prototype.hasOwnProperty.call(v, "0")) {
            // assume contiguous array, start at row 1
            offset = 1;
        }
        v.forEach((value, index) => {
            this._worksheet.getCell(index + offset, colNumber).value = value;
        });
    }
    // =========================================================================
    // Page Breaks
    /**
     * Add a page break after this column.
     * @param top - Optional top row limit for the page break (1-indexed)
     * @param bottom - Optional bottom row limit for the page break (1-indexed)
     */
    addPageBreak(top, bottom) {
        const ws = this._worksheet;
        const topRow = Math.max(0, (top ?? 0) - 1) || 0;
        const bottomRow = Math.max(0, (bottom ?? 0) - 1) || 1048575;
        const pb = {
            id: this._number,
            max: bottomRow,
            man: 1
        };
        if (topRow) {
            pb.min = topRow;
        }
        ws.colBreaks.push(pb);
    }
    // =========================================================================
    // styles
    get numFmt() {
        return this.style.numFmt;
    }
    set numFmt(value) {
        this.style.numFmt = value;
        this.eachCell(cell => {
            cell.numFmt = value;
        });
    }
    get font() {
        return this.style.font;
    }
    set font(value) {
        this.style.font = value;
        this.eachCell(cell => {
            cell.font = value;
        });
    }
    get alignment() {
        return this.style.alignment;
    }
    set alignment(value) {
        this.style.alignment = value;
        this.eachCell(cell => {
            cell.alignment = value;
        });
    }
    get protection() {
        return this.style.protection;
    }
    set protection(value) {
        this.style.protection = value;
        this.eachCell(cell => {
            cell.protection = value;
        });
    }
    get border() {
        return this.style.border;
    }
    set border(value) {
        this.style.border = value;
        this.eachCell(cell => {
            cell.border = value;
        });
    }
    get fill() {
        return this.style.fill;
    }
    set fill(value) {
        this.style.fill = value;
        this.eachCell(cell => {
            cell.fill = value;
        });
    }
    // =============================================================================
    // static functions
    static toModel(columns) {
        // Convert array of Column into compressed list cols
        const cols = [];
        let col = null;
        if (columns) {
            columns.forEach((column, index) => {
                if (column.isDefault) {
                    if (col) {
                        col = null;
                    }
                }
                else if (!col || !column.equivalentToModel(col)) {
                    col = {
                        min: index + 1,
                        max: index + 1,
                        width: column.width !== undefined ? column.width : DEFAULT_COLUMN_WIDTH,
                        style: column.style,
                        isCustomWidth: column.isCustomWidth,
                        hidden: column.hidden,
                        outlineLevel: column.outlineLevel,
                        collapsed: column.collapsed,
                        bestFit: column.bestFit
                    };
                    cols.push(col);
                }
                else {
                    col.max = index + 1;
                }
            });
        }
        return cols.length ? cols : undefined;
    }
    static fromModel(worksheetOrCols, colsMaybe) {
        // Streaming readers historically called Column.fromModel(cols) without a Worksheet.
        // Preserve that behavior by accepting the 1-arg form and intentionally using the
        // cols array as the backing "worksheet" object.
        const worksheet = Array.isArray(worksheetOrCols)
            ? worksheetOrCols
            : worksheetOrCols;
        let cols = Array.isArray(worksheetOrCols) ? worksheetOrCols : (colsMaybe ?? []);
        cols = cols ?? [];
        const columns = [];
        let count = 1;
        let index = 0;
        /**
         * sort cols by min
         * If it is not sorted, the subsequent column configuration will be overwritten
         * */
        cols = cols.sort(function (pre, next) {
            return pre.min - next.min;
        });
        while (index < cols.length) {
            const col = cols[index++];
            while (count < col.min) {
                columns.push(new Column(worksheet, count++));
            }
            while (count <= col.max) {
                columns.push(new Column(worksheet, count++, col));
            }
        }
        return columns.length ? columns : null;
    }
}
export { Column };
