"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
Object.defineProperty(exports, "BGCodeParser", {
    enumerable: true,
    get: function() {
        return BGCodeParser;
    }
});
const _promises = /*#__PURE__*/ _interop_require_wildcard(require("node:fs/promises"));
const _path = /*#__PURE__*/ _interop_require_wildcard(require("path"));
function _getRequireWildcardCache(nodeInterop) {
    if (typeof WeakMap !== "function") return null;
    var cacheBabelInterop = new WeakMap();
    var cacheNodeInterop = new WeakMap();
    return (_getRequireWildcardCache = function(nodeInterop) {
        return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
    })(nodeInterop);
}
function _interop_require_wildcard(obj, nodeInterop) {
    if (!nodeInterop && obj && obj.__esModule) {
        return obj;
    }
    if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
        return {
            default: obj
        };
    }
    var cache = _getRequireWildcardCache(nodeInterop);
    if (cache && cache.has(obj)) {
        return cache.get(obj);
    }
    var newObj = {
        __proto__: null
    };
    var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
    for(var key in obj){
        if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
            var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
            if (desc && (desc.get || desc.set)) {
                Object.defineProperty(newObj, key, desc);
            } else {
                newObj[key] = obj[key];
            }
        }
    }
    newObj.default = obj;
    if (cache) {
        cache.set(obj, newObj);
    }
    return newObj;
}
class BGCodeParser {
    async parse(filePath) {
        const stats = await _promises.stat(filePath);
        const fileName = _path.basename(filePath);
        const buffer = await this.readFileHeader(filePath, 4096);
        const metadata = this.parseHeader(buffer);
        const blocks = this.parseBlocks(buffer);
        const thumbnails = this.extractThumbnails(buffer);
        const normalized = {
            fileName,
            fileFormat: "bgcode",
            fileSize: stats.size,
            producer: metadata.producer,
            producedOn: metadata.produced_on,
            checksumType: metadata.checksum_type,
            gcodePrintTimeSeconds: this.parseTime(metadata.estimated_printing_time_normal_mode || metadata.estimated_printing_time || metadata.print_time),
            nozzleDiameterMm: this.parseFirstValue(metadata.nozzle_diameter),
            filamentDiameterMm: this.parseFirstValue(metadata.filament_diameter) || 1.75,
            filamentDensityGramsCm3: this.parseFirstValue(metadata.filament_density),
            filamentUsedMm: this.parseFirstValue(metadata.filament_used_mm),
            filamentUsedCm3: this.parseFirstValue(metadata.filament_used_cm3),
            filamentUsedGrams: this.parseFirstValue(metadata.filament_used_g),
            totalFilamentUsedGrams: this.parseFirstValue(metadata.filament_used_g),
            layerHeight: this.parseFloat(metadata.layer_height),
            firstLayerHeight: this.parseFloat(metadata.first_layer_height || metadata.initial_layer_height),
            bedTemperature: this.parseFirstValue(metadata.bed_temperature),
            nozzleTemperature: this.parseFirstValue(metadata.temperature),
            fillDensity: metadata.fill_density || null,
            filamentType: this.parseFirstCsvValue(metadata.filament_type),
            printerModel: metadata.printer_model || null,
            slicerVersion: metadata.producer || null,
            maxLayerZ: this.parseFloat(metadata.max_layer_z),
            totalLayers: this.parseInt(metadata.total_layers || metadata.layer_count),
            thumbnails: thumbnails.length > 0 ? thumbnails : undefined,
            blocks: blocks.length > 0 ? blocks : undefined
        };
        return {
            raw: {
                _thumbnails: thumbnails,
                blocks
            },
            normalized
        };
    }
    async readFileHeader(filePath, bytes) {
        const fileHandle = await _promises.open(filePath, "r");
        try {
            const buffer = Buffer.alloc(bytes);
            await fileHandle.read(buffer, 0, bytes, 0);
            return buffer;
        } finally{
            await fileHandle.close();
        }
    }
    parseHeader(buffer) {
        const metadata = {};
        const magic = buffer.toString("ascii", 0, 4);
        if (magic !== "GCDE" && magic !== "BGCD") {
            throw new Error(`Invalid BGCode file: magic number not found (got "${magic}", expected "GCDE" or "BGCD")`);
        }
        const version = buffer.readUInt32LE(4);
        metadata.version = version.toString();
        const headerText = buffer.toString("utf8", 0, Math.min(buffer.length, 2048));
        this.extractMetadataFromText(headerText, metadata);
        return metadata;
    }
    extractMetadataFromText(text, metadata) {
        const lines = text.split('\n');
        const producerMatch = text.match(/Producer=([^\n\r\0]+)/);
        if (producerMatch) {
            metadata.producer = producerMatch[1].trim();
        }
        for (const line of lines){
            const match = line.match(/^([a-z_\s]+(?:\[[^\]]+\])?(?:\([^)]+\))?)\s*=\s*(.+)/i);
            if (match) {
                let key = match[1].trim().toLowerCase().replace(/\s+/g, '_');
                let value = match[2].trim();
                key = key.replace(/\(([^)]+)\)/g, (_, content)=>'_' + content.replace(/\s+/g, '_'));
                key = key.replace(/\[([^\]]+)\]/g, (_, unit)=>'_' + unit.replace(/\^/g, ''));
                key = key.replace(/_+/g, '_');
                metadata[key] = value;
            }
        }
        const patterns = [
            /producer[=:]\s*([^\n\r]+)/i,
            /produced[\s_]on[=:]\s*([^\n\r]+)/i,
            /print[\s_]time[=:]\s*([^\n\r]+)/i,
            /layer[\s_]height[=:]\s*([^\n\r]+)/i,
            /nozzle_diameter[=:]\s*([^\n\r]+)/i,
            /bed_temperature[=:]\s*([^\n\r]+)/i,
            /nozzle_temperature[=:]\s*([^\n\r]+)/i
        ];
        for (const pattern of patterns){
            const match = text.match(pattern);
            if (match) {
                const key = pattern.source.match(/^(\w+)/)?.[1] || "";
                metadata[key] = match[1].trim();
            }
        }
    }
    parseBlocks(buffer) {
        const blocks = [];
        try {
            let offset = 8;
            const blockCount = Math.min(buffer.readUInt16LE(offset), 10);
            for(let i = 0; i < blockCount; i++){
                blocks.push({
                    type: `Block${i}`,
                    compressedSize: 0,
                    uncompressedSize: 0,
                    compression: "unknown"
                });
            }
        } catch (error) {}
        return blocks;
    }
    extractThumbnails(buffer) {
        const thumbnails = [];
        try {
            for(let i = 0; i < buffer.length - 8; i++){
                if (buffer[i] === 0x89 && buffer[i + 1] === 0x50 && buffer[i + 2] === 0x4E && buffer[i + 3] === 0x47) {
                    thumbnails.push({
                        width: 0,
                        height: 0,
                        format: "PNG",
                        size: 0
                    });
                    break;
                }
            }
        } catch (error) {}
        return thumbnails;
    }
    parseFloat(value) {
        if (!value) return null;
        const num = parseFloat(value);
        return isNaN(num) ? null : num;
    }
    parseInt(value) {
        if (!value) return null;
        const num = parseInt(value, 10);
        return isNaN(num) ? null : num;
    }
    parseTime(value) {
        if (!value) return null;
        const match = value.match(/(?:(\d+)h)?(?:\s*(\d+)m)?(?:\s*(\d+)s)?/);
        if (match && (match[1] || match[2] || match[3])) {
            const hours = parseInt(match[1] || "0");
            const minutes = parseInt(match[2] || "0");
            const secs = parseInt(match[3] || "0");
            return hours * 3600 + minutes * 60 + secs;
        }
        const seconds = parseFloat(value);
        if (!isNaN(seconds)) return seconds;
        return null;
    }
    parseFirstValue(value) {
        if (!value) return null;
        const firstValue = value.split(',')[0].trim();
        const num = parseFloat(firstValue);
        return isNaN(num) ? null : num;
    }
    parseFirstCsvValue(value) {
        if (!value) return null;
        const firstValue = value.split(/[;,]/)[0].trim();
        return firstValue || null;
    }
}

//# sourceMappingURL=bgcode.parser.js.map