"use strict";
/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.applyMoveEffectsToMark = exports.isMoveMark = exports.makeMergeable = exports.clearMergeability = exports.getMoveEffect = exports.getOrAddEffect = exports.splitMove = exports.PairedMarkUpdate = exports.MoveEnd = void 0;
const common_utils_1 = require("@fluidframework/common-utils");
const modular_schema_1 = require("../modular-schema");
const utils_1 = require("./utils");
var MoveEnd;
(function (MoveEnd) {
    MoveEnd[MoveEnd["Source"] = 0] = "Source";
    MoveEnd[MoveEnd["Dest"] = 1] = "Dest";
})(MoveEnd = exports.MoveEnd || (exports.MoveEnd = {}));
var PairedMarkUpdate;
(function (PairedMarkUpdate) {
    /**
     * Indicates that the mark's matching mark is now inactive.
     */
    PairedMarkUpdate[PairedMarkUpdate["Deactivated"] = 0] = "Deactivated";
    /**
     * Indicates that the mark's matching mark is now active.
     */
    PairedMarkUpdate[PairedMarkUpdate["Reactivated"] = 1] = "Reactivated";
})(PairedMarkUpdate = exports.PairedMarkUpdate || (exports.PairedMarkUpdate = {}));
function splitMove(effects, target, revision, id, newId, count1, count2) {
    (0, common_utils_1.assert)(newId !== id, 0x5ba /* Must have distinct ID for each piece of the split mark */);
    const effect = getOrAddEffect(effects, target, revision, id);
    const newEffect = getOrAddEffect(effects, target, revision, newId);
    newEffect.count = count2;
    if (effect.child !== undefined) {
        newEffect.child = effect.child;
    }
    effect.child = newId;
    effect.count = count1;
}
exports.splitMove = splitMove;
function getOrAddEffect(moveEffects, target, revision, id, resetMerges = false, invalidate = true) {
    if (resetMerges) {
        clearMergeability(moveEffects, target, revision, id);
    }
    return moveEffects.getOrCreate(target, revision, id, {}, invalidate);
}
exports.getOrAddEffect = getOrAddEffect;
function getMoveEffect(moveEffects, target, revision, id, addDependency = true) {
    var _a;
    return (_a = moveEffects.get(target, revision, id, addDependency)) !== null && _a !== void 0 ? _a : {};
}
exports.getMoveEffect = getMoveEffect;
function clearMergeability(moveEffects, target, revision, id) {
    const effect = getOrAddEffect(moveEffects, target, revision, id);
    if (effect.mergeLeft !== undefined) {
        delete getOrAddEffect(moveEffects, target, revision, effect.mergeLeft, false, false)
            .mergeRight;
        delete effect.mergeLeft;
    }
    if (effect.mergeRight !== undefined) {
        delete getOrAddEffect(moveEffects, target, revision, effect.mergeRight, false, false)
            .mergeLeft;
        delete effect.mergeRight;
    }
}
exports.clearMergeability = clearMergeability;
function makeMergeable(moveEffects, target, revision, leftId, rightId) {
    getOrAddEffect(moveEffects, target, revision, leftId, false, false).mergeRight = rightId;
    getOrAddEffect(moveEffects, target, revision, rightId, false, false).mergeLeft = leftId;
}
exports.makeMergeable = makeMergeable;
function isMoveMark(mark) {
    switch (mark.type) {
        case "MoveIn":
        case "MoveOut":
        case "ReturnFrom":
        case "ReturnTo":
            return true;
        default:
            return false;
    }
}
exports.isMoveMark = isMoveMark;
function applyMoveEffectsToDest(mark, revision, effects, consumeEffect) {
    var _a, _b, _c;
    const effect = getMoveEffect(effects, modular_schema_1.CrossFieldTarget.Destination, (_a = mark.revision) !== null && _a !== void 0 ? _a : revision, mark.id);
    const result = [];
    (0, common_utils_1.assert)(effect.modifyAfter === undefined, 0x566 /* Cannot modify move destination */);
    if (effect.shouldRemove !== true) {
        const newMark = Object.assign(Object.assign({}, mark), { count: (_b = effect.count) !== null && _b !== void 0 ? _b : mark.count });
        if (effect.pairedMarkStatus !== undefined) {
            if (effect.pairedMarkStatus === PairedMarkUpdate.Deactivated) {
                newMark.isSrcConflicted = true;
            }
            else {
                delete newMark.isSrcConflicted;
            }
        }
        result.push(newMark);
    }
    if (effect.child !== undefined) {
        const childEffect = getMoveEffect(effects, modular_schema_1.CrossFieldTarget.Destination, (_c = mark.revision) !== null && _c !== void 0 ? _c : revision, effect.child);
        (0, common_utils_1.assert)(childEffect.count !== undefined, 0x567 /* Child effects should have size */);
        const newMark = Object.assign(Object.assign({}, mark), { id: effect.child, count: childEffect.count });
        if (newMark.type === "ReturnTo" && newMark.detachEvent !== undefined) {
            (0, common_utils_1.assert)(effect.count !== undefined, 0x699 /* Should have a count when splitting a mark */);
            newMark.detachEvent = Object.assign(Object.assign({}, newMark.detachEvent), { index: newMark.detachEvent.index + effect.count });
        }
        // TODO: Split detachEvent if necessary
        result.push(...applyMoveEffectsToDest(newMark, revision, effects, consumeEffect));
    }
    if (consumeEffect) {
        delete effect.count;
        delete effect.child;
    }
    return result;
}
function applyMoveEffectsToSource(mark, revision, effects, consumeEffect, composeChildren) {
    var _a, _b, _c;
    const effect = getMoveEffect(effects, modular_schema_1.CrossFieldTarget.Source, (_a = mark.revision) !== null && _a !== void 0 ? _a : revision, mark.id);
    const result = [];
    if (effect.shouldRemove !== true) {
        const newMark = (0, utils_1.cloneMark)(mark);
        newMark.count = (_b = effect.count) !== null && _b !== void 0 ? _b : newMark.count;
        if (effect.modifyAfter !== undefined) {
            (0, common_utils_1.assert)(composeChildren !== undefined, 0x569 /* Must provide a change composer if modifying moves */);
            const changes = composeChildren(newMark.changes, effect.modifyAfter);
            if (changes !== undefined) {
                newMark.changes = changes;
            }
            else {
                delete newMark.changes;
            }
        }
        if (effect.pairedMarkStatus !== undefined) {
            (0, common_utils_1.assert)(newMark.type === "ReturnFrom", 0x56a /* TODO: support updating MoveOut.isSrcConflicted */);
            if (effect.pairedMarkStatus === PairedMarkUpdate.Deactivated) {
                newMark.isDstConflicted = true;
            }
            else {
                delete newMark.isDstConflicted;
            }
        }
        result.push(newMark);
    }
    if (effect.child !== undefined) {
        const childEffect = getMoveEffect(effects, modular_schema_1.CrossFieldTarget.Source, (_c = mark.revision) !== null && _c !== void 0 ? _c : revision, effect.child);
        (0, common_utils_1.assert)(childEffect.count !== undefined, 0x56b /* Child effects should have size */);
        const newMark = Object.assign(Object.assign({}, mark), { id: effect.child, count: childEffect.count });
        if (newMark.detachEvent !== undefined) {
            (0, common_utils_1.assert)(effect.count !== undefined, 0x69a /* Should specify a count when splitting a mark */);
            newMark.detachEvent = Object.assign(Object.assign({}, newMark.detachEvent), { index: newMark.detachEvent.index + effect.count });
        }
        result.push(...applyMoveEffectsToSource(newMark, revision, effects, consumeEffect, composeChildren));
    }
    if (consumeEffect) {
        delete effect.count;
        delete effect.child;
        delete effect.modifyAfter;
    }
    return result;
}
function applyMoveEffectsToMark(mark, revision, effects, consumeEffect, composeChildren) {
    if (isMoveMark(mark)) {
        const type = mark.type;
        switch (type) {
            case "MoveOut":
            case "ReturnFrom": {
                return applyMoveEffectsToSource(mark, revision, effects, consumeEffect, composeChildren);
            }
            case "MoveIn":
            case "ReturnTo": {
                return applyMoveEffectsToDest(mark, revision, effects, consumeEffect);
            }
            default:
                (0, common_utils_1.unreachableCase)(type);
        }
    }
    return [mark];
}
exports.applyMoveEffectsToMark = applyMoveEffectsToMark;
//# sourceMappingURL=moveEffectTable.js.map