"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RespDecoder = void 0;
const Reader_1 = require("@jsonjoy.com/util/lib/buffers/Reader");
const extensions_1 = require("./extensions");
const isUtf8_1 = require("@jsonjoy.com/util/lib/buffers/utf8/isUtf8");
class RespDecoder {
    constructor(reader = new Reader_1.Reader()) {
        this.reader = reader;
        this.tryUtf8 = false;
    }
    read(uint8) {
        this.reader.reset(uint8);
        return this.val();
    }
    decode(uint8) {
        this.reader.reset(uint8);
        return this.val();
    }
    val() {
        const reader = this.reader;
        const type = reader.u8();
        switch (type) {
            case 58:
                return this.readInt();
            case 44:
                return this.readFloat();
            case 43:
                return this.readStrSimple();
            case 36:
                return this.readStrBulk();
            case 35:
                return this.readBool();
            case 95:
                return reader.skip(2), null;
            case 37:
                return this.readObj();
            case 42:
                return this.readArr();
            case 61:
                return this.readStrVerbatim();
            case 62:
                return new extensions_1.RespPush(this.readArr() || []);
            case 40:
                return this.readBigint();
            case 126:
                return this.readSet();
            case 45:
                return this.readErrSimple();
            case 33:
                return this.readErrBulk();
            case 124:
                return new extensions_1.RespAttributes(this.readObj());
        }
        throw new Error('UNKNOWN_TYPE');
    }
    readLength() {
        const reader = this.reader;
        let number = 0;
        while (true) {
            const c = reader.u8();
            if (c === 13)
                return reader.skip(1), number;
            number = number * 10 + (c - 48);
        }
    }
    readCmd() {
        const reader = this.reader;
        const type = reader.u8();
        if (type !== 42)
            throw new Error('INVALID_COMMAND');
        const c = reader.peak();
        if (c === 45)
            throw new Error('INVALID_COMMAND');
        const length = this.readLength();
        if (length === 0)
            throw new Error('INVALID_COMMAND');
        const cmd = this.readAsciiAsStrBulk().toUpperCase();
        const args = [cmd];
        this.tryUtf8 = false;
        for (let i = 1; i < length; i++) {
            const type = reader.u8();
            if (type !== 36)
                throw new Error('INVALID_COMMAND');
            args.push(this.readStrBulk());
        }
        return args;
    }
    readBool() {
        const reader = this.reader;
        const c = reader.u8();
        reader.skip(2);
        return c === 116;
    }
    readInt() {
        const reader = this.reader;
        let negative = false;
        let c = reader.u8();
        let number = 0;
        if (c === 45) {
            negative = true;
        }
        else if (c !== 43)
            number = c - 48;
        while (true) {
            c = reader.u8();
            if (c === 13) {
                reader.skip(1);
                return negative ? -number : number;
            }
            number = number * 10 + (c - 48);
        }
    }
    readFloat() {
        const reader = this.reader;
        const x = reader.x;
        while (true) {
            const c = reader.u8();
            if (c !== 13)
                continue;
            const length = reader.x - x - 1;
            reader.x = x;
            const str = reader.ascii(length);
            switch (length) {
                case 3:
                    switch (str) {
                        case 'inf':
                            return reader.skip(2), Infinity;
                        case 'nan':
                            return reader.skip(2), NaN;
                    }
                    break;
                case 4:
                    if (str === '-inf') {
                        return reader.skip(2), -Infinity;
                    }
                    break;
            }
            reader.skip(2);
            return Number(str);
        }
    }
    readBigint() {
        const reader = this.reader;
        const x = reader.x;
        while (true) {
            const c = reader.u8();
            if (c !== 13)
                continue;
            const length = reader.x - x;
            reader.x = x;
            const str = reader.ascii(length);
            reader.skip(1);
            return BigInt(str);
        }
    }
    readStrSimple() {
        const reader = this.reader;
        const x = reader.x;
        while (true) {
            const c = reader.u8();
            if (c !== 13)
                continue;
            const size = reader.x - x - 1;
            reader.x = x;
            const str = reader.utf8(size);
            reader.skip(2);
            return str;
        }
    }
    readStrBulk() {
        const reader = this.reader;
        if (reader.peak() === 45) {
            reader.skip(4);
            return null;
        }
        const length = this.readLength();
        let res;
        if (this.tryUtf8 && (0, isUtf8_1.isUtf8)(reader.uint8, reader.x, length))
            res = reader.utf8(length);
        else
            res = reader.buf(length);
        reader.skip(2);
        return res;
    }
    readAsciiAsStrBulk() {
        const reader = this.reader;
        reader.skip(1);
        const length = this.readLength();
        const buf = reader.ascii(length);
        reader.skip(2);
        return buf;
    }
    readStrVerbatim() {
        const reader = this.reader;
        const length = this.readLength();
        const u32 = reader.u32();
        const isTxt = u32 === 1954051130;
        if (isTxt) {
            const str = reader.utf8(length - 4);
            reader.skip(2);
            return str;
        }
        const buf = reader.buf(length - 4);
        reader.skip(2);
        return buf;
    }
    readErrSimple() {
        const reader = this.reader;
        const x = reader.x;
        while (true) {
            const c = reader.u8();
            if (c !== 13)
                continue;
            const size = reader.x - x - 1;
            reader.x = x;
            const str = reader.utf8(size);
            reader.skip(2);
            return new Error(str);
        }
    }
    readErrBulk() {
        const reader = this.reader;
        const length = this.readLength();
        const message = reader.utf8(length);
        reader.skip(2);
        return new Error(message);
    }
    readArr() {
        const reader = this.reader;
        const c = reader.peak();
        if (c === 45) {
            reader.skip(4);
            return null;
        }
        const length = this.readLength();
        const arr = [];
        for (let i = 0; i < length; i++)
            arr.push(this.val());
        return arr;
    }
    readSet() {
        const length = this.readLength();
        const set = new Set();
        for (let i = 0; i < length; i++)
            set.add(this.val());
        return set;
    }
    readObj() {
        const length = this.readLength();
        const obj = {};
        for (let i = 0; i < length; i++) {
            const key = this.val() + '';
            obj[key] = this.val();
        }
        return obj;
    }
    skipN(n) {
        for (let i = 0; i < n; i++)
            this.skipAny();
    }
    skipAny() {
        const reader = this.reader;
        const type = reader.u8();
        switch (type) {
            case 58:
                return this.skipInt();
            case 44:
                return this.skipFloat();
            case 43:
                return this.skipStrSimple();
            case 36:
                return this.skipStrBulk();
            case 35:
                return this.skipBool();
            case 95:
                return reader.skip(2);
            case 37:
                return this.skipObj();
            case 42:
                return this.skipArr();
            case 61:
                return this.skipStrVerbatim();
            case 62:
                return this.skipArr();
            case 40:
                return this.skipBigint();
            case 126:
                return this.skipSet();
            case 45:
                return this.skipErrSimple();
            case 33:
                return this.skipErrBulk();
            case 124:
                return this.skipObj();
        }
        throw new Error('UNKNOWN_TYPE');
    }
    skipBool() {
        this.reader.skip(3);
    }
    skipInt() {
        const reader = this.reader;
        while (true) {
            if (reader.u8() !== 13)
                continue;
            reader.skip(1);
            return;
        }
    }
    skipFloat() {
        const reader = this.reader;
        while (true) {
            if (reader.u8() !== 13)
                continue;
            reader.skip(1);
            return;
        }
    }
    skipBigint() {
        const reader = this.reader;
        while (true) {
            if (reader.u8() !== 13)
                continue;
            reader.skip(1);
            return;
        }
    }
    skipStrSimple() {
        const reader = this.reader;
        while (true) {
            if (reader.u8() !== 13)
                continue;
            reader.skip(1);
            return;
        }
    }
    skipStrBulk() {
        const reader = this.reader;
        if (reader.peak() === 45) {
            reader.skip(4);
            return;
        }
        reader.skip(this.readLength() + 2);
    }
    skipStrVerbatim() {
        const length = this.readLength();
        this.reader.skip(length + 2);
    }
    skipErrSimple() {
        const reader = this.reader;
        while (true) {
            if (reader.u8() !== 13)
                continue;
            reader.skip(1);
            return;
        }
    }
    skipErrBulk() {
        const length = this.readLength();
        this.reader.skip(length + 2);
    }
    skipArr() {
        const reader = this.reader;
        const c = reader.peak();
        if (c === 45) {
            reader.skip(4);
            return;
        }
        const length = this.readLength();
        for (let i = 0; i < length; i++)
            this.skipAny();
    }
    skipSet() {
        const length = this.readLength();
        for (let i = 0; i < length; i++)
            this.skipAny();
    }
    skipObj() {
        const length = this.readLength();
        for (let i = 0; i < length; i++) {
            this.skipAny();
            this.skipAny();
        }
    }
}
exports.RespDecoder = RespDecoder;
//# sourceMappingURL=RespDecoder.js.map