"use strict";
/* eslint-disable @typescript-eslint/no-explicit-any */
/*
 * Copyright 2015 Teppo Kurki <teppo.kurki@iki.fi>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const net_1 = require("net");
const split_1 = __importDefault(require("split"));
const debug_1 = require("../debug");
const types_1 = require("../types");
const BackpressureManager_1 = require("../BackpressureManager");
const debug = (0, debug_1.createDebug)('signalk-server:interfaces:tcp:signalk');
const thresholds = (0, BackpressureManager_1.parseBackpressureThresholds)();
module.exports = (app) => {
    'use strict';
    let idSequence = 0;
    let server;
    const port = Number(process.env.TCPSTREAMPORT) || 8375;
    const api = new types_1.Interface();
    api.start = () => {
        if (!app.securityStrategy.allowReadOnly()) {
            debug('Not starting tcp interface because readOnly is false');
            return;
        }
        debug('Starting tcp interface');
        debug('Backpressure thresholds: enter=%d, exit=%d, max=%d, maxTime=%d', thresholds.enterThreshold, thresholds.exitThreshold, thresholds.maxBufferSize, thresholds.maxBufferCheckTime);
        server = (0, net_1.createServer)((socket) => {
            socket.id = idSequence++;
            socket.name = socket.remoteAddress + ':' + socket.remotePort;
            debug('Connected:' + socket.id + ' ' + socket.name);
            const manager = new BackpressureManager_1.BackpressureManager({
                get id() {
                    return `TCP ${socket.name}`;
                },
                getBufferLength() {
                    return socket.writableLength;
                },
                write(delta) {
                    if (!socket.destroyed) {
                        socket.write(`${JSON.stringify(delta)}\r\n`);
                    }
                },
                destroy() {
                    socket.destroy();
                }
            }, thresholds);
            socket.on('error', (err) => {
                debug('Error:' + err + ' ' + socket.id + ' ' + socket.name);
            });
            socket.on('close', (hadError) => {
                debug('Close:' + hadError + ' ' + socket.id + ' ' + socket.name);
            });
            socket.on('drain', () => manager.onDrain());
            const unsubscibes = [];
            socket
                .pipe((0, split_1.default)((s) => {
                if (s.length > 0) {
                    try {
                        return JSON.parse(s);
                    }
                    catch (e) {
                        console.log(e);
                    }
                }
            }))
                .on('data', socketMessageHandler(app, socket, unsubscibes, manager))
                .on('error', (err) => {
                console.error(err);
            });
            socket.on('close', () => {
                unsubscibes.forEach((f) => f());
                manager.clear();
                debug('Closed:' + socket.id + ' ' + socket.name);
            });
            socket.write(JSON.stringify(app.getHello()) + '\r\n');
        });
        server.on('listening', () => debug('Signal K tcp server listening on ' + port));
        server.on('error', (e) => {
            console.error(`Signal K tcp server error: ${e.message}`);
        });
        if (process.env.TCPSTREAMADDRESS) {
            debug('Binding to ' + process.env.TCPSTREAMADDRESS);
            server.listen(port, process.env.TCPSTREAMADDRESS);
        }
        else {
            server.listen(port);
        }
        return {
            port
        };
    };
    api.stop = () => {
        if (server) {
            server.close();
            server = null;
        }
    };
    api.mdns = {
        name: '_signalk-tcp',
        type: 'tcp',
        port
    };
    return api;
};
function socketMessageHandler(app, socket, unsubscribes, manager) {
    let lastUpdateErrorLogged = 0;
    return (msg) => {
        if (msg.updates) {
            if (app.securityStrategy.isDummy()) {
                app.handleMessage('tcp', msg);
            }
            else {
                if (Date.now() - lastUpdateErrorLogged > 60 * 1000) {
                    console.error(`Security is enabled, deltas over tcp ignored`);
                    lastUpdateErrorLogged = Date.now();
                }
            }
        }
        else if (msg.subscribe) {
            debug.enabled && debug(`subscribe:${JSON.stringify(msg)}`);
            app.subscriptionmanager.subscribe(msg, unsubscribes, (err) => {
                console.error(`Subscribe  failed:${err}`);
            }, (delta) => {
                manager.send(delta);
            });
        }
        else if (msg.unsubscribe) {
            debug.enabled && debug(`unsubscribe:${JSON.stringify(msg)}`);
            try {
                app.subscriptionmanager.unsubscribe(msg, unsubscribes);
            }
            catch (e) {
                console.error(e);
                socket.write(JSON.stringify(e.message));
                socket.end(() => {
                    console.error(`Closed ${socket.name}`);
                });
            }
        }
    };
}
module.exports.socketMessageHandler = socketMessageHandler;
//# sourceMappingURL=tcp.js.map