"use strict";
/*
 * server component for the TimeLimit App
 * Copyright (C) 2019 - 2022 Jonas Lochmann
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.VisibleConnectedDevicesManager = exports.ConnectedDevicesManager = void 0;
const events_1 = require("events");
class ConnectedDevicesManager {
    constructor() {
        this.deviceConnectionCounters = new Map();
        // event name = key, value = boolean/ isConnected
        this.deviceConnectionChangeEmitter = new events_1.EventEmitter();
        this.reportDeviceConnected = ({ key }) => {
            const oldValue = this.deviceConnectionCounters.get(key) || 0;
            const newValue = oldValue + 1;
            this.deviceConnectionCounters.set(key, newValue);
            if (oldValue === 0) {
                this.deviceConnectionChangeEmitter.emit(key, true);
            }
        };
        this.reportDeviceDisconnected = ({ key }) => {
            const oldValue = this.deviceConnectionCounters.get(key) || 0;
            const newValue = Math.max(0, oldValue - 1);
            if (newValue === 0) {
                this.deviceConnectionCounters.delete(key);
            }
            else {
                this.deviceConnectionCounters.set(key, newValue);
            }
            if (oldValue !== 0 && newValue === 0) {
                this.deviceConnectionChangeEmitter.emit(key, false);
            }
        };
        this.isDeviceConnected = ({ key }) => (this.deviceConnectionCounters.has(key) &&
            (this.deviceConnectionCounters.get(key) !== 0));
    }
}
exports.ConnectedDevicesManager = ConnectedDevicesManager;
ConnectedDevicesManager.buildKey = ({ familyId, deviceId }) => `${familyId}_${deviceId}`;
class VisibleConnectedDevicesManager {
    constructor({ database }) {
        this.connectedDevicesManager = new ConnectedDevicesManager();
        this.familyDeviceShareConnectedChangeEmitter = new events_1.EventEmitter();
        this.notifyShareConnectedChanged = ({ familyId, deviceId, showDeviceConnected }) => {
            this.familyDeviceShareConnectedChangeEmitter.emit(familyId, { deviceId, showDeviceConnected });
        };
        this.observeConnectedDevicesOfFamily = ({ familyId, listener }) => {
            const observesDevices = new Set();
            const devicesWithSharingEnabled = new Set();
            const connectedDevices = new Set();
            const sentConnectedDevices = new Set();
            let hasShutDown = false;
            const shutdownHooks = [];
            const shutdown = () => {
                hasShutDown = true;
                shutdownHooks.forEach((hook) => hook());
            };
            const sendStatus = () => {
                if (hasShutDown) {
                    return;
                }
                const result = [];
                sentConnectedDevices.forEach((deviceId) => result.push(deviceId));
                listener(result);
            };
            const addDevice = ({ deviceId, showDeviceConnected }) => {
                const key = ConnectedDevicesManager.buildKey({ familyId, deviceId });
                const isConnected = this.connectedDevicesManager.isDeviceConnected({ key });
                if (!observesDevices.has(deviceId)) {
                    observesDevices.add(deviceId);
                    if (isConnected) {
                        connectedDevices.add(deviceId);
                    }
                    if (!hasShutDown) {
                        const listener = (nowConnected) => {
                            const oldConnected = connectedDevices.has(deviceId);
                            if (oldConnected !== nowConnected) {
                                if (nowConnected) {
                                    connectedDevices.add(deviceId);
                                    if (devicesWithSharingEnabled.has(deviceId)) {
                                        sentConnectedDevices.add(deviceId);
                                        sendStatus();
                                    }
                                }
                                else {
                                    connectedDevices.delete(deviceId);
                                    if (devicesWithSharingEnabled.has(deviceId)) {
                                        sentConnectedDevices.delete(deviceId);
                                        sendStatus();
                                    }
                                }
                            }
                        };
                        this.connectedDevicesManager.deviceConnectionChangeEmitter.addListener(key, listener);
                        shutdownHooks.push(() => {
                            this.connectedDevicesManager.deviceConnectionChangeEmitter.removeListener(key, listener);
                        });
                    }
                }
                if (showDeviceConnected) {
                    if (!devicesWithSharingEnabled.has(deviceId)) {
                        devicesWithSharingEnabled.add(deviceId);
                    }
                    if (isConnected) {
                        if (!sentConnectedDevices.has(deviceId)) {
                            sentConnectedDevices.add(deviceId);
                            sendStatus();
                        }
                    }
                }
                else {
                    if (devicesWithSharingEnabled.has(deviceId)) {
                        devicesWithSharingEnabled.delete(deviceId);
                        if (sentConnectedDevices.has(deviceId)) {
                            sentConnectedDevices.delete(deviceId);
                            sendStatus();
                        }
                    }
                }
            };
            (async () => {
                const devicesUnsafe = await this.database.device.findAll({
                    where: {
                        familyId
                    },
                    attributes: [
                        'deviceId',
                        'showDeviceConnected'
                    ]
                });
                const devices = devicesUnsafe.map(({ deviceId, showDeviceConnected }) => ({
                    deviceId, showDeviceConnected
                }));
                devices.forEach(({ deviceId, showDeviceConnected }) => {
                    addDevice({ deviceId, showDeviceConnected });
                });
            })().catch(() => { });
            {
                // add all new devices + apply changes of sharing
                const listener = ({ deviceId, showDeviceConnected }) => {
                    addDevice({
                        deviceId,
                        showDeviceConnected
                    });
                };
                this.familyDeviceShareConnectedChangeEmitter.addListener(familyId, listener);
                shutdownHooks.push(() => this.familyDeviceShareConnectedChangeEmitter.removeListener(familyId, listener));
            }
            return { shutdown };
        };
        this.database = database;
    }
}
exports.VisibleConnectedDevicesManager = VisibleConnectedDevicesManager;
//# sourceMappingURL=index.js.map