"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.apply = void 0;
const json_pointer_1 = require("@jsonjoy.com/json-pointer");
const evaluate_1 = require("@jsonjoy.com/json-expression/lib/evaluate");
const util_1 = require("./util");
const constants_1 = require("./constants");
const apply_1 = require("../ot-string-irreversible/apply");
const apply_2 = require("../ot-binary-irreversible/apply");
const Vars_1 = require("@jsonjoy.com/json-expression/lib/Vars");
const apply = (doc, op) => {
    const [test, pick = [], data = [], drop = [], edit = []] = op;
    const testLength = test.length;
    if (testLength) {
        const expressionContext = { vars: new Vars_1.Vars(doc) };
        for (let i = 0; i < testLength; i++) {
            const testExpr = test[i];
            const testValue = (0, evaluate_1.evaluate)(testExpr, expressionContext);
            if (!testValue)
                throw new Error('TEST');
        }
    }
    const registers = new Map();
    const picksSorted = pick.sort((a, b) => (0, util_1.comparePath)(a[1], b[1]));
    for (const [regId, path] of picksSorted) {
        const ref = (0, json_pointer_1.find)(doc, path);
        if ((0, json_pointer_1.isArrayReference)(ref)) {
            const { obj, key, val } = ref;
            obj.splice(key, 1);
            registers.set(regId, val);
        }
        else if ((0, json_pointer_1.isObjectReference)(ref)) {
            const { obj, key, val } = ref;
            delete obj[key];
            registers.set(regId, val);
        }
        else {
            doc = undefined;
            registers.set(regId, ref.val);
        }
    }
    for (const [regId, value] of data)
        registers.set(regId, value);
    const dropsSorted = drop.sort((a, b) => (0, util_1.comparePath)(b[1], a[1]));
    for (const [regId, where] of dropsSorted) {
        const value = registers.get(regId);
        if (!where.length) {
            doc = value;
            continue;
        }
        const path = where.slice(0, -1);
        const { val } = (0, json_pointer_1.find)(doc, path);
        if (val instanceof Array) {
            let index = where[where.length - 1];
            if (index === '-')
                index = val.length;
            const index2 = ~~index;
            if (typeof index === 'string')
                if ('' + index2 !== index)
                    throw new Error('INVALID_INDEX');
            if (index2 > val.length || index2 < -1)
                throw new Error('INVALID_INDEX');
            if (index2 === -1 || index2 === val.length)
                val.push(value);
            else
                val.splice(index2, 0, value);
        }
        else if (val && typeof val === 'object') {
            const key = where[where.length - 1];
            val[key] = value;
        }
        else {
            throw new Error('NOT_FOUND');
        }
    }
    for (const [type, path, operation] of edit) {
        const { val, obj, key } = (0, json_pointer_1.find)(doc, path);
        let newVal;
        switch (type) {
            case constants_1.EDIT_TYPE.OT_STRING: {
                if (typeof val !== 'string')
                    throw new Error('NOT_STR');
                newVal = (0, apply_1.apply)(val, operation);
                break;
            }
            case constants_1.EDIT_TYPE.OT_BINARY: {
                if (!(val instanceof Uint8Array))
                    throw new Error('NOT_BIN');
                newVal = (0, apply_2.apply)(val, operation);
                break;
            }
        }
        if (!obj)
            doc = newVal;
        else
            obj[key] = newVal;
    }
    return doc;
};
exports.apply = apply;
//# sourceMappingURL=apply.js.map