"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.observableTokenParseReducer = exports.subscriptionTokenParseReducer = void 0;
const ColdObservable_1 = require("rxjs/internal/testing/ColdObservable");
const TestMessage_1 = require("../message/TestMessage");
const ObservableMarbleToken_1 = require("./ObservableMarbleToken");
const SubscriptionMarbleToken_1 = require("./SubscriptionMarbleToken");
/**
 * Translate single token in marble diagram to correct metadata
 * @param {any} token Single char in marble diagram
 * @param {{ [key: string]: T }} [value] Custom value for marble value
 * @param {boolean} [materializeInnerObservables] Flatten inner observables in cold observable. False by default.
 */
const getMarbleTokenValue = (token, value, materializeInnerObservables) => {
    const customValue = value && token in value ? value[token] : token;
    return materializeInnerObservables && customValue instanceof ColdObservable_1.ColdObservable ? customValue.messages : customValue;
};
const timeFrameExpandTokenHandler = (acc, frameTimeFactor) => {
    const ret = Object.assign({}, acc);
    ret.expandingTokenCount += 1;
    //When token reaches ...xxx..., clean up state
    if (ret.expandingTokenCount === 6) {
        ret.expandingValue.splice(0);
        ret.expandingTokenCount = 0;
    }
    //When first ending token arrives ...xxx. , parse values and adjust timeframe
    if (ret.expandingTokenCount === 4) {
        if (ret.expandingValue.length === 0) {
            throw new Error(`There isn't value to expand timeframe`);
        }
        const expandedFrame = parseInt(ret.expandingValue.join(''), 10);
        ret.currentTimeFrame += expandedFrame * frameTimeFactor;
    }
    return ret;
};
const validateTimeFrameToken = (acc) => {
    if (acc.expandingTokenCount > 0 || acc.simultaneousGrouped) {
        throw new Error('Incorret timeframe specified');
    }
};
const validateSimultaneousGroupToken = (acc) => {
    if (acc.simultaneousGrouped) {
        throw new Error('Cannot nest grouped value');
    }
};
const increaseTimeFrame = (acc, frameTimeFactor) => {
    const ret = Object.assign({}, acc);
    ret.currentTimeFrame += 1 * frameTimeFactor;
    return ret;
};
/**
 * @internal
 * Reducer to traverse Observable marble diagram to generate TestMessage metadata.
 * @param value Custom values for marble values
 * @param error Custom error
 * @param materializeInnerObservables Flatten inner observable if available
 * @param frameTimeFactor Custom timeframe factor
 */
const observableTokenParseReducer = (value, error, materializeInnerObservables, frameTimeFactor, maxFrame) => (acc, token) => {
    if (acc.currentTimeFrame >= maxFrame) {
        return acc;
    }
    let message = null;
    switch (token) {
        case ObservableMarbleToken_1.ObservableMarbleToken.TIMEFRAME:
            validateTimeFrameToken(acc);
            acc = increaseTimeFrame(acc, frameTimeFactor);
            break;
        case ObservableMarbleToken_1.ObservableMarbleToken.ERROR:
            message = TestMessage_1.error(acc.currentTimeFrame, error || '#');
            break;
        case ObservableMarbleToken_1.ObservableMarbleToken.COMPLETE:
            message = TestMessage_1.complete(acc.currentTimeFrame);
            break;
        case ObservableMarbleToken_1.ObservableMarbleToken.TIMEFRAME_EXPAND:
            acc = timeFrameExpandTokenHandler(acc, frameTimeFactor);
            break;
        case ObservableMarbleToken_1.ObservableMarbleToken.SIMULTANEOUS_START:
            validateSimultaneousGroupToken(acc);
            acc.simultaneousGrouped = true;
            break;
        case ObservableMarbleToken_1.ObservableMarbleToken.SIMULTANEOUS_END:
            acc = increaseTimeFrame(acc, frameTimeFactor);
            acc.simultaneousGrouped = false;
            break;
        case SubscriptionMarbleToken_1.SubscriptionMarbleToken.SUBSCRIBE:
            acc = increaseTimeFrame(acc, frameTimeFactor);
            break;
        default:
            if (acc.expandingTokenCount > 0) {
                acc.expandingValue.push(token);
            }
            else {
                const tokenValue = getMarbleTokenValue(token, value, materializeInnerObservables);
                message = TestMessage_1.next(acc.currentTimeFrame, tokenValue);
            }
    }
    if (!!message) {
        acc.messages.push(message);
        if (!acc.simultaneousGrouped) {
            acc = increaseTimeFrame(acc, frameTimeFactor);
        }
    }
    return acc;
};
exports.observableTokenParseReducer = observableTokenParseReducer;
/**
 * @internal
 * Reducer to traverse subscription marble diagram to generate SubscriptionLog metadata.
 * @param frameTimeFactor Custom timeframe factor
 */
const subscriptionTokenParseReducer = (frameTimeFactor, maxFrame) => (acc, token) => {
    if (acc.currentTimeFrame >= maxFrame) {
        return acc;
    }
    switch (token) {
        case SubscriptionMarbleToken_1.SubscriptionMarbleToken.SUBSCRIBE:
            acc.subscriptionFrame = acc.currentTimeFrame;
            if (!acc.simultaneousGrouped) {
                acc = increaseTimeFrame(acc, frameTimeFactor);
            }
            break;
        case SubscriptionMarbleToken_1.SubscriptionMarbleToken.UNSUBSCRIBE:
            acc.unsubscriptionFrame = acc.currentTimeFrame;
            break;
        case ObservableMarbleToken_1.ObservableMarbleToken.TIMEFRAME_EXPAND:
            acc = timeFrameExpandTokenHandler(acc, frameTimeFactor);
            break;
        case ObservableMarbleToken_1.ObservableMarbleToken.SIMULTANEOUS_START:
            validateSimultaneousGroupToken(acc);
            acc.simultaneousGrouped = true;
            break;
        case ObservableMarbleToken_1.ObservableMarbleToken.SIMULTANEOUS_END:
            acc.simultaneousGrouped = false;
        case ObservableMarbleToken_1.ObservableMarbleToken.TIMEFRAME:
            validateTimeFrameToken(acc);
        case ObservableMarbleToken_1.ObservableMarbleToken.ERROR:
        case ObservableMarbleToken_1.ObservableMarbleToken.COMPLETE:
        default:
            if (acc.expandingTokenCount > 0) {
                acc.expandingValue.push(token);
            }
            else if (!acc.simultaneousGrouped) {
                acc = increaseTimeFrame(acc, frameTimeFactor);
            }
            break;
    }
    return acc;
};
exports.subscriptionTokenParseReducer = subscriptionTokenParseReducer;
//# sourceMappingURL=tokenParseReducer.js.map