"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const socket_wrapper_factory_1 = require("./socket-wrapper-factory");
const types_1 = require("@deepstream/types");
const connection_endpoint_1 = require("../base/connection-endpoint");
const net_1 = require("net");
const tls_1 = require("tls");
// @ts-ignore
const mqttCon = require("mqtt-connection");
const constants_1 = require("../../constants");
const events_1 = require("events");
/**
 * This is the frontmost class of deepstream's message pipeline. It receives
 * connections and authentication requests, authenticates sockets and
 * forwards messages it receives from authenticated sockets.
 */
class MQTTConnectionEndpoint extends connection_endpoint_1.default {
    constructor(mqttOptions, services, config) {
        super(mqttOptions, services, config);
        this.mqttOptions = mqttOptions;
        this.connections = new Map();
        this.logger = this.services.logger.getNameSpace('MQTT');
        this.isReady = false;
        this.emitter = new events_1.EventEmitter();
        this.description = 'MQTT Protocol Connection Endpoint';
        this.onMessages = this.onMessages.bind(this);
    }
    async whenReady() {
        if (!this.isReady) {
            return new Promise((resolve) => this.emitter.once('ready', resolve));
        }
    }
    async close() {
        return new Promise((resolve) => this.server.close(() => resolve()));
    }
    /**
     * Initialize the ws endpoint, setup callbacks etc.
     */
    createWebsocketServer() {
        if (this.mqttOptions.ssl) {
            this.server = tls_1.createServer({
                key: this.mqttOptions.ssl.key,
                cert: this.mqttOptions.ssl.cert
            });
        }
        else {
            this.server = net_1.createServer();
        }
        this.server.on(this.mqttOptions.ssl ? 'secureConnection' : 'connection', (stream) => {
            const client = mqttCon(stream);
            const socketWrapper = socket_wrapper_factory_1.createMQTTSocketWrapper(client, {}, this.services, this.logger);
            this.connections.set(client, socketWrapper);
            this.onConnection(socketWrapper);
            socketWrapper.onMessage([{
                    topic: constants_1.TOPIC.CONNECTION,
                    action: constants_1.CONNECTION_ACTION.CHALLENGE
                }]);
            const logger = this.services.logger;
            // client connected
            client.on('connect', function (packet) {
                logger.debug(types_1.EVENT.INCOMING_CONNECTION, `MQTT Connection with username ${packet.username}`, { username: packet.username });
                socketWrapper.onMessage([{
                        topic: constants_1.TOPIC.AUTH,
                        action: constants_1.AUTH_ACTION.REQUEST,
                        parsedData: {
                            username: packet.username,
                            password: packet.password && packet.password.toString()
                        }
                    }]);
            });
            const closeClient = () => {
                if (!this.connections.has(client)) {
                    return;
                }
                this.onSocketClose(socketWrapper);
                this.connections.delete(client);
                socketWrapper.destroy();
            };
            // client disconnect
            client.on('disconnect', closeClient);
            // connection error handling
            client.on('close', closeClient);
            client.on('error', (e) => {
                this.logger.error('CLIENT ERROR', e.toString());
                closeClient();
            });
            // timeout idle streams after 5 minutes
            stream.setTimeout(this.mqttOptions.idleTimeout);
            // stream timeout
            stream.on('timeout', function () { client.destroy(); });
            // client published
            client.on('publish', (packet) => {
                this.onMessages(socketWrapper, socketWrapper.parseMessage(packet));
            });
            // // client pinged
            client.on('pingreq', function () {
                client.pingresp();
            });
            // client subscribed
            client.on('subscribe', (packet) => {
                this.onMessages(socketWrapper, socketWrapper.parseMessage(packet));
            });
            // client subscribed
            client.on('unsubscribe', (packet) => {
                this.onMessages(socketWrapper, socketWrapper.parseMessage(packet));
            });
        });
        this.server.listen(this.mqttOptions.port, this.mqttOptions.host, () => {
            this.services.logger.info(types_1.EVENT.INFO, `Listening for MQTT ${this.mqttOptions.ssl ? 'TLS' : 'TCP'} connections on ${this.mqttOptions.host}:${this.mqttOptions.port}`);
            this.isReady = true;
            this.emitter.emit('ready');
        });
        return this.server;
    }
    async closeWebsocketServer() {
        this.connections.forEach((conn) => {
            if (!conn.isClosed) {
                conn.destroy();
            }
        });
        this.connections.clear();
        return new Promise((resolve) => this.server.close(resolve));
    }
    onSocketWrapperClosed(socketWrapper) {
        socketWrapper.close();
    }
}
exports.MQTTConnectionEndpoint = MQTTConnectionEndpoint;
//# sourceMappingURL=connection-endpoint.js.map