"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NameIo = void 0;
const bin_util_1 = require("@ot-builder/bin-util");
const errors_1 = require("@ot-builder/errors");
const ot_name_1 = require("@ot-builder/ot-name");
const iconv = require("iconv-lite");
// TODO: add more encodings
function SupportedEncoding(platformID, encodingID) {
    if (platformID === 3 && encodingID === 1)
        return `utf16-be`;
    if (platformID === 3 && encodingID === 10)
        return `utf32-be`;
    return null;
}
class NameOffsetAllocator {
    constructor() {
        this.bw = new bin_util_1.BufferWriter();
        this.offsetMap = new Map();
    }
    add(buf) {
        const hash = buf.toString("base64");
        const existing = this.offsetMap.get(hash);
        if (existing !== undefined)
            return existing;
        const offset = this.bw.currentOffset;
        this.offsetMap.set(hash, offset);
        this.bw.bytes(buf);
        return offset;
    }
    getBuffer() {
        return this.bw.toBuffer();
    }
}
const NameRecord = {
    read(view, strings) {
        const platformID = view.uint16();
        const encodingID = view.uint16();
        const languageID = view.uint16();
        const nameID = view.uint16();
        const length = view.uint16();
        const offset = view.uint16();
        const buf = Buffer.from(strings.lift(offset).bytes(length));
        const encName = SupportedEncoding(platformID, encodingID);
        const value = encName ? iconv.decode(buf, encName) : buf;
        return { platformID, encodingID, languageID, nameID, value };
    },
    write(frag, rec, alloc) {
        frag.uint16(rec.platformID);
        frag.uint16(rec.encodingID);
        frag.uint16(rec.languageID);
        frag.uint16(rec.nameID);
        let buf = null;
        if (typeof rec.value === "string") {
            const enc = SupportedEncoding(rec.platformID, rec.encodingID);
            if (enc)
                buf = iconv.encode(rec.value, enc);
        }
        else {
            buf = rec.value;
        }
        if (!buf)
            throw errors_1.Errors.Name.EncodingNotSupported(rec.platformID, rec.encodingID);
        frag.uint16(buf.byteLength);
        frag.uint16(alloc.add(buf));
    },
    compare(a, b) {
        if (a.platformID !== b.platformID)
            return a.platformID - b.platformID;
        if (a.encodingID !== b.encodingID)
            return a.encodingID - b.encodingID;
        if (a.languageID !== b.languageID)
            return a.languageID - b.languageID;
        return a.nameID - b.nameID;
    }
};
const LangTagRecord = {
    read(view, strings) {
        const length = view.uint16();
        const offset = view.uint16();
        const buf = Buffer.from(strings.lift(offset).bytes(length));
        return iconv.decode(buf, `utf16-be`);
    },
    write(frag, ltr, alloc) {
        const buf = iconv.encode(ltr, `utf16-be`);
        frag.uint16(buf.byteLength);
        frag.uint16(alloc.add(buf));
    }
};
exports.NameIo = {
    read(view) {
        const format = view.uint16();
        errors_1.Assert.FormatSupported("NameTable", format, 0, 1);
        const count = view.uint16();
        const strings = view.ptr16();
        const table = new ot_name_1.Name.Table();
        for (let rid = 0; rid < count; rid++) {
            table.records.push(view.next(NameRecord, strings));
        }
        if (format >= 1) {
            table.langTagMap = [];
            const langTagCount = view.uint16();
            for (let lidOffset = 0; lidOffset < langTagCount; lidOffset++) {
                table.langTagMap.push(view.next(LangTagRecord, strings));
            }
        }
        return table;
    },
    write(frag, table) {
        const format = table.langTagMap && table.langTagMap.length ? 1 : 0;
        const frStrings = new bin_util_1.Frag();
        const noa = new NameOffsetAllocator();
        const sortedRecords = Array.from(table.records).sort(NameRecord.compare);
        frag.uint16(format);
        frag.uint16(sortedRecords.length);
        frag.ptr16(frStrings);
        for (const rec of sortedRecords) {
            frag.push(NameRecord, rec, noa);
        }
        if (format === 1) {
            if (!table.langTagMap || !table.langTagMap.length)
                throw errors_1.Errors.Unreachable();
            frag.uint16(table.langTagMap.length);
            for (const lt of table.langTagMap)
                frag.push(LangTagRecord, lt, noa);
        }
        noa.add(iconv.encode("\n", "utf-8")); // Ensure that the frStrings will not be empty
        frStrings.bytes(noa.getBuffer());
    }
};
//# sourceMappingURL=index.js.map