"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MotionMasterPubSubClient = void 0;
const tslib_1 = require("tslib");
const rxjs_1 = require("rxjs");
const uuid_1 = require("uuid");
const operators_1 = require("./operators");
const fast_deep_equal_1 = tslib_1.__importDefault(require("fast-deep-equal"));
const motion_master_pub_sub_worker_socket_1 = require("./motion-master-pub-sub-worker-socket");
const units_1 = require("./units");
class MotionMasterPubSubClient {
    constructor(socket) {
        this.socket = socket;
        this.data$ = new rxjs_1.Subject();
        this.subscriptions = new Map();
        // These observables are assigned here rather than via field initializers because they reference
        // `this.socket`. Under native class-field semantics (used by Bun, which ignores
        // `useDefineForClassFields`), field initializers run before the constructor body assigns the
        // parameter property, leaving `this.socket` undefined. Assigning here guarantees correct order.
        this.notification$ = this.socket.data$.pipe((0, operators_1.selectMotionMasterMessageByTopic)('notification'));
        this.systemEvent$ = this.notification$.pipe((0, operators_1.selectMotionMasterMessageStatusByKey)('systemEvent'));
        this.deviceEvent$ = this.notification$.pipe((0, operators_1.selectMotionMasterMessageStatusByKey)('deviceEvent'));
    }
    get messageIds() {
        return Array.from(this.subscriptions.keys());
    }
    subscribe(config) {
        var _a;
        const messageId = (_a = config.messageId) !== null && _a !== void 0 ? _a : (0, uuid_1.v4)();
        if (!config.messageId) {
            config = Object.assign(Object.assign({}, config), { messageId });
        }
        if (this.socket instanceof motion_master_pub_sub_worker_socket_1.MotionMasterPubSubWorkerSocket) {
            this.socket.worker.postMessage({ subscribe: config });
        }
        else {
            const { bufferSize = 1, distinct = false, topic, unitFormatters } = config;
            let observable = this.socket.data$.pipe((0, rxjs_1.filter)((data) => data[0] === topic), (0, rxjs_1.map)((data) => data[1]));
            if (distinct) {
                observable = observable.pipe((0, rxjs_1.distinctUntilChanged)((prev, curr) => {
                    var _a, _b, _c, _d, _e, _f;
                    if (((_c = (_b = (_a = prev === null || prev === void 0 ? void 0 : prev.status) === null || _a === void 0 ? void 0 : _a.monitoringParameterValues) === null || _b === void 0 ? void 0 : _b.deviceParameterValues) === null || _c === void 0 ? void 0 : _c.parameterValues) &&
                        ((_f = (_e = (_d = curr === null || curr === void 0 ? void 0 : curr.status) === null || _d === void 0 ? void 0 : _d.monitoringParameterValues) === null || _e === void 0 ? void 0 : _e.deviceParameterValues) === null || _f === void 0 ? void 0 : _f.parameterValues)) {
                        return (0, fast_deep_equal_1.default)(prev.status.monitoringParameterValues.deviceParameterValues.parameterValues, curr.status.monitoringParameterValues.deviceParameterValues.parameterValues);
                    }
                    return false;
                }));
            }
            if (unitFormatters) {
                observable = observable.pipe((0, rxjs_1.map)((message) => {
                    var _a, _b, _c;
                    if (unitFormatters) {
                        const values = (_c = (_b = (_a = message.status) === null || _a === void 0 ? void 0 : _a.monitoringParameterValues) === null || _b === void 0 ? void 0 : _b.deviceParameterValues) === null || _c === void 0 ? void 0 : _c.parameterValues;
                        if (values && unitFormatters) {
                            if (values.length > 0 && values.length !== unitFormatters.length) {
                                console.warn('The number of unit formatters does not correspond to the number of values. Unit formatting will not be applied.');
                                return message;
                            }
                            for (let i = 0; i < values.length; i++) {
                                const formatter = unitFormatters[i];
                                if (formatter) {
                                    const converter = units_1.unitConverters[formatter.converterId];
                                    if (converter) {
                                        if (values[i].intValue !== undefined) {
                                            // The uintValue will store a float value when formatted, rather than an unsigned integer.
                                            // Unlike position values from the drive, which are integers, degrees and revolutions in the UI are represented as float values.
                                            values[i].intValue = parseFloat(converter.formatter(formatter.unit, values[i].intValue, formatter.data));
                                        }
                                        else if (values[i].uintValue !== undefined) {
                                            // The uintValue will store a float value when formatted, rather than an unsigned integer.
                                            // Unlike position values from the drive, which are integers, degrees and revolutions in the UI are represented as float values.
                                            values[i].uintValue = parseFloat(converter.formatter(formatter.unit, values[i].uintValue, formatter.data));
                                        }
                                        else if (values[i].floatValue !== undefined) {
                                            values[i].floatValue = parseFloat(converter.formatter(formatter.unit, values[i].floatValue, formatter.data));
                                        }
                                    }
                                }
                            }
                        }
                    }
                    return message;
                }));
            }
            const subscription = observable.pipe((0, rxjs_1.bufferCount)(bufferSize)).subscribe((data) => {
                this.data$.next([topic, data]);
            });
            this.subscriptions.set(messageId, subscription);
        }
        return messageId;
    }
    unsubscribe(messageId) {
        if (this.socket instanceof motion_master_pub_sub_worker_socket_1.MotionMasterPubSubWorkerSocket) {
            this.socket.worker.postMessage({ unsubcribe: { messageId } });
        }
        else {
            const subscription = this.subscriptions.get(messageId);
            if (subscription) {
                subscription.unsubscribe();
                this.subscriptions.delete(messageId);
            }
        }
    }
    unsubscribeAll() {
        if (this.socket instanceof motion_master_pub_sub_worker_socket_1.MotionMasterPubSubWorkerSocket) {
            this.socket.worker.postMessage({ unsubscribeAll: true });
        }
        else {
            this.subscriptions.forEach((subscription) => subscription.unsubscribe());
            this.subscriptions.clear();
        }
    }
    createSystemStatesObservable() {
        return this.systemEvent$.pipe((0, rxjs_1.map)(({ state }) => state), (0, rxjs_1.filter)(Boolean), // this will exclude the UNSPECIFIED state
        (0, rxjs_1.scan)((acc, state) => {
            acc.push(state);
            return acc;
        }, []));
    }
}
exports.MotionMasterPubSubClient = MotionMasterPubSubClient;
//# sourceMappingURL=motion-master-pub-sub-client.js.map