"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ServerClockVector = exports.ClockVector = exports.LogicalClock = exports.interval = exports.printTs = exports.containsId = exports.contains = exports.compare = exports.equal = exports.tick = exports.tss = exports.ts = exports.Timespan = exports.Timestamp = void 0;
class Timestamp {
    constructor(sid, time) {
        this.sid = sid;
        this.time = time;
    }
}
exports.Timestamp = Timestamp;
class Timespan {
    constructor(sid, time, span) {
        this.sid = sid;
        this.time = time;
        this.span = span;
    }
}
exports.Timespan = Timespan;
/**
 * A factory function for creating a new timestamp.
 *
 * @param sid Session ID.
 * @param time Logical clock sequence number.
 * @returns A new timestamp.
 */
const ts = (sid, time) => new Timestamp(sid, time);
exports.ts = ts;
/**
 * A factory function for creating a new timespan.
 *
 * @param sid Session ID.
 * @param time Logical clock sequence number.
 * @param span Length of the timespan.
 * @returns A new timespan.
 */
const tss = (sid, time, span) => new Timespan(sid, time, span);
exports.tss = tss;
/**
 * Advance a timestamp by a number of cycles.
 *
 * @param stamp A reference timestamp.
 * @param cycles Number of cycles to advance.
 * @returns A new timestamp.
 */
const tick = (stamp, cycles) => (0, exports.ts)(stamp.sid, stamp.time + cycles);
exports.tick = tick;
/**
 * Compares for equality two timestamps, time first.
 *
 * @returns True if timestamps are equal.
 */
const equal = (ts1, ts2) => ts1.time === ts2.time && ts1.sid === ts2.sid;
exports.equal = equal;
/**
 * Compares two timestamps, time first.
 *
 * @returns 1 if current timestamp is larger, -1 if smaller, and 0 otherwise.
 */
const compare = (ts1, ts2) => {
    const t1 = ts1.time;
    const t2 = ts2.time;
    if (t1 > t2)
        return 1;
    if (t1 < t2)
        return -1;
    const s1 = ts1.sid;
    const s2 = ts2.sid;
    if (s1 > s2)
        return 1;
    if (s1 < s2)
        return -1;
    return 0;
};
exports.compare = compare;
/**
 * Checks if the first timespan contains the second timespan.
 *
 * @param ts1 Start of container timespan.
 * @param span1 Length of container timespan.
 * @param ts2 Start of contained timespan.
 * @param span2 Length of contained timespan.
 * @returns Returns true if the first timespan contains the second timespan.
 */
const contains = (ts1, span1, ts2, span2) => {
    if (ts1.sid !== ts2.sid)
        return false;
    const t1 = ts1.time;
    const t2 = ts2.time;
    if (t1 > t2)
        return false;
    if (t1 + span1 < t2 + span2)
        return false;
    return true;
};
exports.contains = contains;
/**
 * Checks if a timespan contains the `ts2` point.
 *
 * @param ts1 Start of container timespan.
 * @param span1 Length of container timespan.
 * @param ts2 A point in time.
 * @returns Returns true if the first timespan contains the `ts2` point.
 */
const containsId = (ts1, span1, ts2) => {
    if (ts1.sid !== ts2.sid)
        return false;
    const t1 = ts1.time;
    const t2 = ts2.time;
    if (t1 > t2)
        return false;
    if (t1 + span1 < t2 + 1)
        return false;
    return true;
};
exports.containsId = containsId;
/**
 * Returns a human-readable string representation of the timestamp.
 *
 * @param id A timestamp.
 * @returns Human-readable string representation of the timestamp.
 */
const printTs = (id) => {
    if (id.sid === 1 /* SESSION.SERVER */)
        return '.' + id.time;
    let session = '' + id.sid;
    if (session.length > 4)
        session = '..' + session.slice(session.length - 4);
    return session + '.' + id.time;
};
exports.printTs = printTs;
/**
 * Advances a given timestamp by a number of cycles and then returns a timespan
 * starting from that position.
 *
 * @param ts A start timestamp.
 * @param tick Number of cycles to advance the starting timestamp.
 * @param span Length of the timespan.
 * @returns A new timespan.
 */
const interval = (ts, tick, span) => new Timespan(ts.sid, ts.time + tick, span);
exports.interval = interval;
/**
 * Represents a *Logical Clock*, which can be advanced by a number of cycles.
 */
class LogicalClock extends Timestamp {
    /**
     * Returns a new timestamp, which is the current clock value, and advances the
     * clock by a number of cycles.
     *
     * @param cycles Number of cycles to advance the clock.
     * @returns A new timestamp, which is the current clock value.
     */
    tick(cycles) {
        const timestamp = new Timestamp(this.sid, this.time);
        this.time += cycles;
        return timestamp;
    }
}
exports.LogicalClock = LogicalClock;
/**
 * Represents a clock vector, which is a local logical clock together with a set
 * of logical clocks of other peers.
 */
class ClockVector extends LogicalClock {
    constructor() {
        super(...arguments);
        /**
         * A set of logical clocks of other peers.
         */
        this.peers = new Map();
    }
    /**
     * Advances local time every time we see any timestamp with higher time value.
     * This is an idempotent method which can be called every time a new timestamp
     * is observed, it advances the local time only if the observed timestamp is
     * greater than the current local time.
     *
     * @param id The time stamp we observed.
     * @param span Length of the time span.
     */
    observe(id, span) {
        // if (this.time < ts.time) throw new Error('TIME_TRAVEL');
        const edge = id.time + span - 1;
        const sid = id.sid;
        if (sid !== this.sid) {
            const clock = this.peers.get(id.sid);
            if (!clock)
                this.peers.set(id.sid, (0, exports.ts)(sid, edge));
            else if (edge > clock.time)
                clock.time = edge;
        }
        if (edge >= this.time)
            this.time = edge + 1;
    }
    /**
     * Returns a deep copy of the current vector clock with the same session ID.
     *
     * @returns A new vector clock, which is a clone of the current vector clock.
     */
    clone() {
        return this.fork(this.sid);
    }
    /**
     * Returns a deep copy of the current vector clock with a different session ID.
     *
     * @param sessionId The session ID of the new vector clock.
     * @returns A new vector clock, which is a fork of the current vector clock.
     */
    fork(sessionId) {
        const clock = new ClockVector(sessionId, this.time);
        if (sessionId !== this.sid)
            clock.observe((0, exports.tick)(this, -1), 1);
        this.peers.forEach((peer) => {
            clock.observe(peer, 1);
        });
        return clock;
    }
    /**
     * Returns a human-readable string representation of the clock vector.
     *
     * @param tab String to use for indentation.
     * @returns Human-readable string representation of the clock vector.
     */
    toString(tab = '') {
        const last = this.peers.size;
        let i = 1;
        let lines = '';
        this.peers.forEach((clock) => {
            const isLast = i === last;
            lines += `\n${tab}${isLast ? '└─' : '├─'} ${clock.sid}.${clock.time}`;
            i++;
        });
        return `clock ${this.sid}.${this.time}${lines}`;
    }
}
exports.ClockVector = ClockVector;
/**
 * Implements a clock vector with a fixed session ID. The *server clock*
 * is used when the CRDT is powered by a central server.
 */
class ServerClockVector extends LogicalClock {
    constructor() {
        super(...arguments);
        /** A stub for other peers. Not used in the server clock. */
        this.peers = new Map();
    }
    observe(ts, span) {
        if (ts.sid > 8)
            throw new Error('INVALID_SERVER_SESSION');
        if (this.time < ts.time)
            throw new Error('TIME_TRAVEL');
        const time = ts.time + span;
        if (time > this.time)
            this.time = time;
    }
    clone() {
        return this.fork();
    }
    fork() {
        return new ServerClockVector(1 /* SESSION.SERVER */, this.time);
    }
    /**
     * Returns a human-readable string representation of the clock vector.
     *
     * @returns Human-readable string representation of the clock vector.
     */
    toString() {
        return `clock ${this.sid}.${this.time}`;
    }
}
exports.ServerClockVector = ServerClockVector;
//# sourceMappingURL=clock.js.map