"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.XdrSchemaDecoder = void 0;
const Reader_1 = require("@jsonjoy.com/buffers/lib/Reader");
const XdrDecoder_1 = require("./XdrDecoder");
const XdrUnion_1 = require("./XdrUnion");
class XdrSchemaDecoder {
    constructor(reader = new Reader_1.Reader()) {
        this.reader = reader;
        this.decoder = new XdrDecoder_1.XdrDecoder(reader);
    }
    decode(data, schema) {
        this.reader.reset(data);
        return this.readValue(schema);
    }
    readValue(schema) {
        switch (schema.type) {
            case 'void':
                return this.decoder.readVoid();
            case 'int':
                return this.decoder.readInt();
            case 'unsigned_int':
                return this.decoder.readUnsignedInt();
            case 'boolean':
                return this.decoder.readBoolean();
            case 'hyper':
                return this.decoder.readHyper();
            case 'unsigned_hyper':
                return this.decoder.readUnsignedHyper();
            case 'float':
                return this.decoder.readFloat();
            case 'double':
                return this.decoder.readDouble();
            case 'quadruple':
                return this.decoder.readQuadruple();
            case 'enum':
                return this.readEnum(schema);
            case 'opaque':
                return this.readOpaque(schema);
            case 'vopaque':
                return this.readVarlenOpaque(schema);
            case 'string':
                return this.readString(schema);
            case 'array':
                return this.readArray(schema);
            case 'varray':
                return this.readVarlenArray(schema);
            case 'struct':
                return this.readStruct(schema);
            case 'union':
                return this.readUnion(schema);
            case 'optional':
                return this.readOptional(schema);
            case 'const':
                return undefined;
            default:
                throw new Error(`Unknown schema type: ${schema.type}`);
        }
    }
    readEnum(schema) {
        const value = this.decoder.readEnum();
        for (const [name, enumValue] of Object.entries(schema.values)) {
            if (enumValue === value) {
                return name;
            }
        }
        return value;
    }
    readOpaque(schema) {
        return this.decoder.readOpaque(schema.size);
    }
    readVarlenOpaque(schema) {
        const data = this.decoder.readVarlenOpaque();
        if (schema.size !== undefined && data.length > schema.size) {
            throw new Error(`Variable-length opaque data size ${data.length} exceeds maximum ${schema.size}`);
        }
        return data;
    }
    readString(schema) {
        const str = this.decoder.readString();
        if (schema.size !== undefined && str.length > schema.size) {
            throw new Error(`String length ${str.length} exceeds maximum ${schema.size}`);
        }
        return str;
    }
    readArray(schema) {
        return this.decoder.readArray(schema.size, () => this.readValue(schema.elements));
    }
    readVarlenArray(schema) {
        const array = this.decoder.readVarlenArray(() => this.readValue(schema.elements));
        if (schema.size !== undefined && array.length > schema.size) {
            throw new Error(`Variable-length array size ${array.length} exceeds maximum ${schema.size}`);
        }
        return array;
    }
    readStruct(schema) {
        const struct = {};
        for (const [fieldSchema, fieldName] of schema.fields) {
            struct[fieldName] = this.readValue(fieldSchema);
        }
        return struct;
    }
    readUnion(schema) {
        const discriminant = this.decoder.readInt();
        for (const [armDiscriminant, armSchema] of schema.arms) {
            if (armDiscriminant === discriminant) {
                const value = this.readValue(armSchema);
                return new XdrUnion_1.XdrUnion(discriminant, value);
            }
        }
        if (schema.default) {
            const value = this.readValue(schema.default);
            return new XdrUnion_1.XdrUnion(discriminant, value);
        }
        throw new Error(`No matching union arm for discriminant: ${discriminant}`);
    }
    readOptional(schema) {
        const opted = this.decoder.readBoolean();
        if (!opted)
            return null;
        return this.readValue(schema.element);
    }
}
exports.XdrSchemaDecoder = XdrSchemaDecoder;
//# sourceMappingURL=XdrSchemaDecoder.js.map