"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments)).next());
    });
};
const packet_stream_1 = require("./packet-stream");
const net = require("net");
const ws = require("ws");
const temp = require("temp");
const helpers = require("../../common/helpers");
class SocketProxyFactory {
    constructor($logger, $config, $options) {
        this.$logger = $logger;
        this.$config = $config;
        this.$options = $options;
    }
    createTCPSocketProxy(factory) {
        let socketFactory = (callback) => helpers.connectEventually(factory, callback);
        this.$logger.info("\nSetting up proxy...\nPress Ctrl + C to terminate, or disconnect.\n");
        let server = net.createServer({
            allowHalfOpen: true
        });
        server.on("connection", (frontendSocket) => {
            this.$logger.info("Frontend client connected.");
            frontendSocket.on("end", () => {
                this.$logger.info('Frontend socket closed!');
                if (!(this.$config.debugLivesync && this.$options.watch)) {
                    process.exit(0);
                }
            });
            socketFactory((backendSocket) => {
                this.$logger.info("Backend socket created.");
                backendSocket.on("end", () => {
                    this.$logger.info("Backend socket closed!");
                    if (!(this.$config.debugLivesync && this.$options.watch)) {
                        process.exit(0);
                    }
                });
                frontendSocket.on("close", () => {
                    console.log("frontend socket closed");
                    if (!backendSocket.destroyed) {
                        backendSocket.destroy();
                    }
                });
                backendSocket.on("close", () => {
                    console.log("backend socket closed");
                    if (!frontendSocket.destroyed) {
                        frontendSocket.destroy();
                    }
                });
                backendSocket.pipe(frontendSocket);
                frontendSocket.pipe(backendSocket);
                frontendSocket.resume();
            });
        });
        let socketFileLocation = temp.path({ suffix: ".sock" });
        server.listen(socketFileLocation);
        if (!this.$options.client) {
            this.$logger.info("socket-file-location: " + socketFileLocation);
        }
        return server;
    }
    createWebSocketProxy(factory) {
        let localPort = 8080;
        this.$logger.info("\nSetting up debugger proxy...\nPress Ctrl + C to terminate, or disconnect.\n");
        let server = new ws.Server({
            port: localPort,
            verifyClient: (info, callback) => __awaiter(this, void 0, void 0, function* () {
                this.$logger.info("Frontend client connected.");
                const _socket = yield factory();
                this.$logger.info("Backend socket created.");
                info.req["__deviceSocket"] = _socket;
                callback(true);
            })
        });
        server.on("connection", (webSocket) => {
            let encoding = "utf16le";
            let deviceSocket = webSocket.upgradeReq["__deviceSocket"];
            let packets = new packet_stream_1.PacketStream();
            deviceSocket.pipe(packets);
            packets.on("data", (buffer) => {
                webSocket.send(buffer.toString(encoding));
            });
            webSocket.on("message", (message, flags) => {
                let length = Buffer.byteLength(message, encoding);
                let payload = new Buffer(length + 4);
                payload.writeInt32BE(length, 0);
                payload.write(message, 4, length, encoding);
                deviceSocket.write(payload);
            });
            deviceSocket.on("end", () => {
                this.$logger.info("Backend socket closed!");
                process.exit(0);
            });
            webSocket.on("close", () => {
                this.$logger.info('Frontend socket closed!');
                if (!this.$options.watch) {
                    process.exit(0);
                }
            });
        });
        this.$logger.info("Opened localhost " + localPort);
        return server;
    }
}
exports.SocketProxyFactory = SocketProxyFactory;
$injector.register("socketProxyFactory", SocketProxyFactory);
