"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AvroDecoder = void 0;
const Reader_1 = require("@jsonjoy.com/buffers/lib/Reader");
/**
 * Apache Avro binary decoder for basic value decoding.
 * Implements the Avro binary decoding specification without schema validation.
 * Based on https://avro.apache.org/docs/1.12.0/specification/
 */
class AvroDecoder {
    constructor() {
        this.reader = new Reader_1.Reader();
    }
    read(uint8) {
        this.reader.reset(uint8);
        return this.readAny();
    }
    decode(uint8) {
        this.reader.reset(uint8);
        return this.readAny();
    }
    /**
     * Generic method to read any value - typically used when schema type is unknown
     */
    readAny() {
        throw new Error('readAny() requires schema information. Use readNull, readBoolean, etc. directly.');
    }
    /**
     * Reads an Avro null value.
     */
    readNull() {
        // Null values are encoded as zero bytes
        return null;
    }
    /**
     * Reads an Avro boolean value.
     */
    readBoolean() {
        return this.reader.u8() === 1;
    }
    /**
     * Reads an Avro int value using zigzag decoding.
     */
    readInt() {
        const zigzag = this.readVarIntUnsigned();
        return this.decodeZigZag32(zigzag);
    }
    /**
     * Reads an Avro long value using zigzag decoding.
     */
    readLong() {
        const zigzag = this.readVarLong();
        const decoded = this.decodeZigZag64(zigzag);
        // Return number if it fits in safe integer range, otherwise bigint
        if (decoded >= BigInt(Number.MIN_SAFE_INTEGER) && decoded <= BigInt(Number.MAX_SAFE_INTEGER)) {
            return Number(decoded);
        }
        return decoded;
    }
    /**
     * Reads an Avro float value using IEEE 754 single-precision.
     */
    readFloat() {
        const reader = this.reader;
        const value = reader.view.getFloat32(reader.x, true); // little-endian
        reader.x += 4;
        return value;
    }
    /**
     * Reads an Avro double value using IEEE 754 double-precision.
     */
    readDouble() {
        const reader = this.reader;
        const value = reader.view.getFloat64(reader.x, true); // little-endian
        reader.x += 8;
        return value;
    }
    /**
     * Reads an Avro bytes value with length-prefixed encoding.
     */
    readBytes() {
        const length = this.readVarIntUnsigned();
        return this.reader.buf(length);
    }
    /**
     * Reads an Avro string value with UTF-8 encoding and length prefix.
     */
    readString() {
        const length = this.readVarIntUnsigned();
        const bytes = this.reader.buf(length);
        return new TextDecoder().decode(bytes);
    }
    /**
     * Reads an Avro array with length-prefixed encoding.
     * The itemReader function is called for each array item.
     */
    readArray(itemReader) {
        const result = [];
        while (true) {
            const count = this.readVarIntUnsigned();
            if (count === 0)
                break; // End of array marker
            for (let i = 0; i < count; i++) {
                result.push(itemReader());
            }
        }
        return result;
    }
    /**
     * Reads an Avro map with length-prefixed encoding.
     * The valueReader function is called for each map value.
     */
    readMap(valueReader) {
        const result = {};
        while (true) {
            const count = this.readVarIntUnsigned();
            if (count === 0)
                break; // End of map marker
            for (let i = 0; i < count; i++) {
                const key = this.readString();
                if (key === '__proto__')
                    throw new Error('INVALID_KEY');
                result[key] = valueReader();
            }
        }
        return result;
    }
    /**
     * Reads an Avro union value.
     * Returns an object with index and value.
     */
    readUnion(schemaReaders) {
        const index = this.decodeZigZag32(this.readVarIntUnsigned());
        if (index < 0 || index >= schemaReaders.length) {
            throw new Error(`Invalid union index: ${index}`);
        }
        const value = schemaReaders[index]();
        return { index, value };
    }
    /**
     * Reads an Avro enum value.
     * Returns the symbol index.
     */
    readEnum() {
        return this.decodeZigZag32(this.readVarIntUnsigned());
    }
    /**
     * Reads an Avro fixed value with specified length.
     */
    readFixed(size) {
        return this.reader.buf(size);
    }
    /**
     * Reads an Avro record.
     * The fieldReaders array contains functions to read each field in order.
     */
    readRecord(fieldReaders) {
        const result = {};
        for (let i = 0; i < fieldReaders.length; i++) {
            const fieldValue = fieldReaders[i]();
            // Note: This generic record reader doesn't know field names
            // The schema-aware decoder will handle proper field mapping
            result[`field${i}`] = fieldValue;
        }
        return result;
    }
    // Utility methods for Avro decoding
    /**
     * Reads a variable-length integer (for unsigned values like lengths)
     */
    readVarIntUnsigned() {
        const reader = this.reader;
        let result = 0;
        let shift = 0;
        while (true) {
            const byte = reader.u8();
            result |= (byte & 0x7f) << shift;
            if ((byte & 0x80) === 0)
                break;
            shift += 7;
            if (shift >= 32) {
                throw new Error('Variable-length integer is too long');
            }
        }
        return result >>> 0; // Convert to unsigned 32-bit
    }
    /**
     * Reads a variable-length long
     */
    readVarLong() {
        const reader = this.reader;
        let result = BigInt(0);
        let shift = BigInt(0);
        while (true) {
            const byte = BigInt(reader.u8());
            result |= (byte & BigInt(0x7f)) << shift;
            if ((byte & BigInt(0x80)) === BigInt(0))
                break;
            shift += BigInt(7);
            if (shift >= BigInt(64)) {
                throw new Error('Variable-length long is too long');
            }
        }
        return result;
    }
    /**
     * Decodes a 32-bit integer using zigzag decoding
     */
    decodeZigZag32(value) {
        return (value >>> 1) ^ -(value & 1);
    }
    /**
     * Decodes a 64-bit integer using zigzag decoding
     */
    decodeZigZag64(value) {
        return (value >> BigInt(1)) ^ -(value & BigInt(1));
    }
}
exports.AvroDecoder = AvroDecoder;
//# sourceMappingURL=AvroDecoder.js.map