import { MsgPackEncoderFast as EncoderMessagePack } from '@jsonjoy.com/json-pack/lib/msgpack/MsgPackEncoderFast';
import { OPCODE } from '../../constants';
export class Encoder extends EncoderMessagePack {
    encode(patch) {
        this.writer.reset();
        this.encodeArrayHeader(patch.length);
        const length = patch.length;
        for (let i = 0; i < length; i++)
            this.encodeOp(patch[i]);
        return this.writer.flush();
    }
    encodeOp(op, parent) {
        switch (op.code()) {
            case OPCODE.add: {
                const addOp = op;
                this.encodeArrayHeader(3);
                this.writer.u8(OPCODE.add);
                this.encodeArray(addOp.path);
                this.encodeAny(addOp.value);
                break;
            }
            case OPCODE.remove: {
                const removeOp = op;
                const hasOldValue = removeOp.oldValue !== undefined;
                this.encodeArrayHeader(hasOldValue ? 3 : 2);
                this.writer.u8(OPCODE.remove);
                this.encodeArray(removeOp.path);
                if (hasOldValue)
                    this.encodeAny(removeOp.oldValue);
                break;
            }
            case OPCODE.replace: {
                const replaceOp = op;
                const hasOldValue = replaceOp.oldValue !== undefined;
                this.encodeArrayHeader(hasOldValue ? 4 : 3);
                this.writer.u8(OPCODE.replace);
                this.encodeArray(replaceOp.path);
                this.encodeAny(replaceOp.value);
                if (hasOldValue)
                    this.encodeAny(replaceOp.oldValue);
                break;
            }
            case OPCODE.copy: {
                const copyOp = op;
                this.encodeArrayHeader(3);
                this.writer.u8(OPCODE.copy);
                this.encodeArray(copyOp.path);
                this.encodeArray(copyOp.from);
                break;
            }
            case OPCODE.move: {
                const moveOp = op;
                this.encodeArrayHeader(3);
                this.writer.u8(OPCODE.move);
                this.encodeArray(moveOp.path);
                this.encodeArray(moveOp.from);
                break;
            }
            case OPCODE.test: {
                const testOp = op;
                this.encodeArrayHeader(testOp.not ? 4 : 3);
                this.writer.u8(OPCODE.test);
                this.encodeArray(parent ? testOp.path.slice(parent.path.length) : testOp.path);
                this.encodeAny(testOp.value);
                if (testOp.not)
                    this.writer.u8(1);
                break;
            }
            case OPCODE.str_ins: {
                const strInsOp = op;
                this.encodeArrayHeader(4);
                this.writer.u8(OPCODE.str_ins);
                this.encodeArray(strInsOp.path);
                this.encodeNumber(strInsOp.pos);
                this.encodeString(strInsOp.str);
                break;
            }
            case OPCODE.str_del: {
                const strDelOp = op;
                const hasStr = typeof strDelOp.str === 'string';
                this.encodeArrayHeader(hasStr ? 4 : 5);
                this.writer.u8(OPCODE.str_del);
                this.encodeArray(strDelOp.path);
                this.encodeNumber(strDelOp.pos);
                if (hasStr) {
                    this.encodeString(strDelOp.str);
                }
                else {
                    this.writer.u8(0);
                    this.encodeNumber(strDelOp.len);
                }
                break;
            }
            case OPCODE.flip: {
                const flipOp = op;
                this.encodeArrayHeader(2);
                this.writer.u8(OPCODE.flip);
                this.encodeArray(flipOp.path);
                break;
            }
            case OPCODE.inc: {
                const incOp = op;
                this.encodeArrayHeader(3);
                this.writer.u8(OPCODE.inc);
                this.encodeArray(incOp.path);
                this.encodeNumber(incOp.inc);
                break;
            }
            case OPCODE.split: {
                const splitOp = op;
                this.encodeArrayHeader(splitOp.props ? 4 : 3);
                this.writer.u8(OPCODE.split);
                this.encodeArray(splitOp.path);
                this.encodeNumber(splitOp.pos);
                if (splitOp.props)
                    this.encodeObject(splitOp.props);
                break;
            }
            case OPCODE.merge: {
                const mergeOp = op;
                this.encodeArrayHeader(mergeOp.props ? 4 : 3);
                this.writer.u8(OPCODE.merge);
                this.encodeArray(mergeOp.path);
                this.encodeNumber(mergeOp.pos);
                if (mergeOp.props)
                    this.encodeAny(mergeOp.props);
                break;
            }
            case OPCODE.extend: {
                const extendOp = op;
                const { deleteNull } = extendOp;
                this.encodeArrayHeader(deleteNull ? 4 : 3);
                this.writer.u8(OPCODE.extend);
                this.encodeArray(extendOp.path);
                this.encodeObject(extendOp.props);
                if (deleteNull)
                    this.writer.u8(1);
                break;
            }
            case OPCODE.contains: {
                const containsOp = op;
                const ignoreCase = containsOp.ignore_case;
                this.encodeArrayHeader(ignoreCase ? 4 : 3);
                this.writer.u8(OPCODE.contains);
                this.encodeArray(parent ? containsOp.path.slice(parent.path.length) : containsOp.path);
                this.encodeString(containsOp.value);
                if (ignoreCase)
                    this.writer.u8(1);
                break;
            }
            case OPCODE.defined: {
                const definedOp = op;
                this.encodeArrayHeader(2);
                this.writer.u8(OPCODE.defined);
                this.encodeArray(parent ? definedOp.path.slice(parent.path.length) : definedOp.path);
                break;
            }
            case OPCODE.ends: {
                const endsOp = op;
                const ignoreCase = endsOp.ignore_case;
                this.encodeArrayHeader(ignoreCase ? 4 : 3);
                this.writer.u8(OPCODE.ends);
                this.encodeArray(parent ? endsOp.path.slice(parent.path.length) : endsOp.path);
                this.encodeString(endsOp.value);
                if (ignoreCase)
                    this.writer.u8(1);
                break;
            }
            case OPCODE.in: {
                const inOp = op;
                this.encodeArrayHeader(3);
                this.writer.u8(OPCODE.in);
                this.encodeArray(parent ? inOp.path.slice(parent.path.length) : inOp.path);
                this.encodeArray(inOp.value);
                break;
            }
            case OPCODE.less: {
                const lessOp = op;
                this.encodeArrayHeader(3);
                this.writer.u8(OPCODE.less);
                this.encodeArray(parent ? lessOp.path.slice(parent.path.length) : lessOp.path);
                this.encodeNumber(lessOp.value);
                break;
            }
            case OPCODE.matches: {
                const matchesOp = op;
                const ignoreCase = matchesOp.ignore_case;
                this.encodeArrayHeader(ignoreCase ? 4 : 3);
                this.writer.u8(OPCODE.matches);
                this.encodeArray(parent ? matchesOp.path.slice(parent.path.length) : matchesOp.path);
                this.encodeString(matchesOp.value);
                if (ignoreCase)
                    this.writer.u8(1);
                break;
            }
            case OPCODE.more: {
                const moreOp = op;
                this.encodeArrayHeader(3);
                this.writer.u8(OPCODE.more);
                this.encodeArray(parent ? moreOp.path.slice(parent.path.length) : moreOp.path);
                this.encodeNumber(moreOp.value);
                break;
            }
            case OPCODE.starts: {
                const startsOp = op;
                const ignoreCase = startsOp.ignore_case;
                this.encodeArrayHeader(ignoreCase ? 4 : 3);
                this.writer.u8(OPCODE.starts);
                this.encodeArray(parent ? startsOp.path.slice(parent.path.length) : startsOp.path);
                this.encodeString(startsOp.value);
                if (ignoreCase)
                    this.writer.u8(1);
                break;
            }
            case OPCODE.test_type: {
                const testTypeOp = op;
                this.encodeArrayHeader(3);
                this.writer.u8(OPCODE.test_type);
                this.encodeArray(parent ? testTypeOp.path.slice(parent.path.length) : testTypeOp.path);
                this.encodeArray(testTypeOp.type);
                break;
            }
            case OPCODE.test_string: {
                const testStringOp = op;
                this.encodeArrayHeader(testStringOp.not ? 5 : 4);
                this.writer.u8(OPCODE.test_string);
                this.encodeArray(parent ? testStringOp.path.slice(parent.path.length) : testStringOp.path);
                this.encodeNumber(testStringOp.pos);
                this.encodeString(testStringOp.str);
                if (testStringOp.not)
                    this.writer.u8(1);
                break;
            }
            case OPCODE.test_string_len: {
                const testStringLenOp = op;
                this.encodeArrayHeader(testStringLenOp.not ? 4 : 3);
                this.writer.u8(OPCODE.test_string_len);
                this.encodeArray(parent ? testStringLenOp.path.slice(parent.path.length) : testStringLenOp.path);
                this.encodeNumber(testStringLenOp.len);
                if (testStringLenOp.not)
                    this.writer.u8(1);
                break;
            }
            case OPCODE.type: {
                const typeOp = op;
                this.encodeArrayHeader(3);
                this.writer.u8(OPCODE.type);
                this.encodeArray(parent ? typeOp.path.slice(parent.path.length) : typeOp.path);
                this.encodeString(typeOp.value);
                break;
            }
            case OPCODE.undefined: {
                const undefinedOp = op;
                this.encodeArrayHeader(2);
                this.writer.u8(OPCODE.undefined);
                this.encodeArray(parent ? undefinedOp.path.slice(parent.path.length) : undefinedOp.path);
                break;
            }
            case OPCODE.and: {
                const andOp = op;
                const path = parent ? andOp.path.slice(parent.path.length) : andOp.path;
                this.encodeArrayHeader(3);
                this.writer.u8(OPCODE.and);
                this.encodeArray(path);
                const length = andOp.ops.length;
                this.encodeArrayHeader(length);
                for (let i = 0; i < length; i++)
                    this.encodeOp(andOp.ops[i], andOp);
                break;
            }
            case OPCODE.not: {
                const notOp = op;
                this.encodeArrayHeader(3);
                this.writer.u8(OPCODE.not);
                this.encodeArray(parent ? notOp.path.slice(parent.path.length) : notOp.path);
                const length = notOp.ops.length;
                this.encodeArrayHeader(length);
                for (let i = 0; i < length; i++)
                    this.encodeOp(notOp.ops[i], notOp);
                break;
            }
            case OPCODE.or: {
                const orOp = op;
                this.encodeArrayHeader(3);
                this.writer.u8(OPCODE.or);
                this.encodeArray(parent ? orOp.path.slice(parent.path.length) : orOp.path);
                const length = orOp.ops.length;
                this.encodeArrayHeader(length);
                for (let i = 0; i < length; i++)
                    this.encodeOp(orOp.ops[i], orOp);
                break;
            }
            default:
                throw new Error(`Unknown operation code: ${op.code()}`);
        }
    }
}
//# sourceMappingURL=Encoder.js.map