"use strict";
/*
 * Copyright 2020 Scott Bender <scott@scottbender.net>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0

 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
const fs_1 = __importDefault(require("fs"));
const lodash_1 = __importDefault(require("lodash"));
const VALUES = 'values';
const META = 'meta';
const SELF_VESSEL = 'vessels.self';
class DeltaEditor {
    constructor() {
        this.deltas = [];
    }
    load(filename) {
        const data = fs_1.default.readFileSync(filename, 'utf8');
        const deltas = JSON.parse(data);
        if (!lodash_1.default.isArray(deltas)) {
            throw new Error(`${filename} should contain an array of deltas`);
        }
        this.deltas = deltas;
    }
    saveSync(filename) {
        fs_1.default.writeFileSync(filename, JSON.stringify(this.deltas, null, 2));
    }
    save(filename) {
        return fs_1.default.promises.writeFile(filename, JSON.stringify(this.deltas, null, 2));
    }
    setValue(context, path, value) {
        if (lodash_1.default.isUndefined(value)) {
            return this.removeValue(context, path);
        }
        if (path.indexOf('.') === -1) {
            const deltaInfo = getDelta(this.deltas, context, '', VALUES);
            const newVal = deltaInfo && deltaInfo.kp ? deltaInfo.kp.value : {};
            newVal[path] = value;
            return setDelta(this.deltas, context, '', newVal, VALUES);
        }
        else {
            return setDelta(this.deltas, context, path, value, VALUES);
        }
    }
    setSelfValue(path, value) {
        return this.setValue(SELF_VESSEL, path, value);
    }
    setMeta(context, path, value) {
        return setDelta(this.deltas, context, path, value, META);
    }
    getValue(context, path) {
        if (path.indexOf('.') === -1) {
            const deltaInfo = getDelta(this.deltas, context, '', VALUES);
            return deltaInfo && deltaInfo.kp && deltaInfo.kp.value[path];
        }
        else {
            const deltaInfo = getDelta(this.deltas, context, path, VALUES);
            return deltaInfo && deltaInfo.kp && deltaInfo.kp.value;
        }
    }
    getSelfValue(path) {
        return this.getValue(SELF_VESSEL, path);
    }
    getMeta(context, path) {
        const deltaInfo = getDelta(this.deltas, context, path, META);
        return deltaInfo && deltaInfo.kp && deltaInfo.kp.value;
    }
    removeValue(context, path) {
        if (path.indexOf('.') === -1) {
            const deltaInfo = getDelta(this.deltas, context, '', VALUES);
            if (deltaInfo && deltaInfo.kp) {
                delete deltaInfo.kp.value[path];
                if (lodash_1.default.keys(deltaInfo.kp.value).length === 0) {
                    lodash_1.default.pull(this.deltas, deltaInfo.delta);
                }
            }
        }
        else {
            const deltaInfo = getDelta(this.deltas, context, path, VALUES);
            if (deltaInfo && deltaInfo.kp) {
                lodash_1.default.pull(deltaInfo.delta.updates[0].values, deltaInfo.kp);
                if (deltaInfo.delta.updates[0].values.length === 0) {
                    lodash_1.default.pull(this.deltas, deltaInfo.delta);
                }
            }
        }
    }
    removeSelfValue(path) {
        return this.removeValue(SELF_VESSEL, path);
    }
    removeMeta(context, path) {
        const deltaInfo = getDelta(this.deltas, context, path, META);
        if (deltaInfo && deltaInfo.kp) {
            lodash_1.default.pull(deltaInfo.delta.updates[0].meta, deltaInfo.kp);
            if (deltaInfo.delta.updates[0].meta.length === 0) {
                lodash_1.default.pull(this.deltas, deltaInfo.delta);
            }
        }
    }
}
function setDelta(deltas, context, path, value, type) {
    const deltaInfo = getDelta(deltas, context, path, type);
    if (deltaInfo && deltaInfo.kp) {
        deltaInfo.kp.value = value;
        return deltaInfo.delta;
    }
    else if (deltaInfo) {
        deltaInfo.delta.updates[0][type].push({ path, value });
        return deltaInfo.delta;
    }
    else {
        const delta = {
            context,
            updates: [
                {
                    [type]: [
                        {
                            path,
                            value
                        }
                    ]
                }
            ]
        };
        deltas.push(delta);
        return delta;
    }
}
function getDelta(deltas, context, path, type) {
    for (const delta of deltas) {
        if (delta.updates && delta.context === context) {
            for (const update of delta.updates) {
                if (update[type]) {
                    const foundKp = update[type].find((kp) => kp.path === path);
                    return { delta, kp: foundKp };
                }
            }
        }
    }
    return null;
}
module.exports = DeltaEditor;
