"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.rooms = void 0;
exports.sendLog = sendLog;
exports.sendCompileRequest = sendCompileRequest;
exports.sendLiveUpdateRequest = sendLiveUpdateRequest;
exports.createSocketServer = createSocketServer;
const ws_1 = __importStar(require("ws"));
const _rooms = [];
exports.rooms = new Proxy(_rooms, {
    set: () => {
        throw new Error("rooms is immutable");
    }
});
const aliveClients = new Map();
let wss;
function sendLog(message) {
    if (!wss) {
        console.error("Not ready to send messages");
    }
    else {
        let sent = false;
        for (let ws of wss.clients) {
            const ref = aliveClients.get(ws);
            if (ref && ref.type === 'cliui' && ws.readyState === ws_1.default.OPEN) {
                ws.send(JSON.stringify({ type: "log", message }));
                sent = true;
            }
        }
        if (!sent) {
            console.error("Failed to send log: ", message);
        }
    }
}
function sendCompileRequest(appid) {
    if (!wss) {
        console.error("Not ready to send compilation requests");
    }
    else {
        let sent = false;
        for (let ws of wss.clients) {
            const ref = aliveClients.get(ws);
            if (ref && ref.type === 'compiler' && ws.readyState === ws_1.default.OPEN) {
                ws.send(`{"type": "prepareCodepush", "appId": "${appid}"}`);
                sent = true;
                break;
            }
        }
        if (!sent) {
            console.error("Failed to send compilation request for ", appid);
        }
    }
}
function sendLiveUpdateRequest(appid) {
    if (!wss) {
        console.error("Not ready to send liveupdate requests");
    }
    else {
        let sent = false;
        for (let ws of wss.clients) {
            const ref = aliveClients.get(ws);
            if (ref && ref.type === 'compiler' && ws.readyState === ws_1.default.OPEN) {
                ws.send(`{"type": "liveUpdate", "appId": "${appid}"}`);
                sent = true;
                break;
            }
        }
        if (!sent) {
            console.error("Failed to send compilation request for ", appid);
        }
    }
}
function createSocketServer(server) {
    wss = new ws_1.WebSocketServer({ server });
    wss.on('connection', (ws, req) => {
        console.log('Websocket connection opened');
        const ref = aliveClients.get(ws);
        if (!ref) {
            aliveClients.set(ws, { isAlive: true, type: 'unknown', ip: req.socket.remoteAddress });
        }
        else {
            ref.isAlive = true;
        }
        ws.on('pong', () => {
            const ref = aliveClients.get(ws);
            if (!ref) {
                console.log("Got a pong from a client that is not registered! Something is broken");
            }
            else {
                // console.log("Pong from: ", ref);
                ref.isAlive = true;
                // console.log("After pong: ", ref);
            }
        });
        ws.on('message', (message, isBinary) => {
            const msg = JSON.parse(message.toString());
            console.log("Received message: ", msg);
            let ref;
            switch (msg.type) {
                case "registerCompiler":
                    console.log("registering compiler");
                    ref = aliveClients.get(ws);
                    if (ref) {
                        ref.type = "compiler";
                        ws.send(`{"status": "registered"}`);
                    }
                    else {
                        ws.send(`{"status": "failed"}`);
                    }
                    break;
                case "compilationFailed":
                case "compilationDone":
                    console.log("Broadcasting compilationDone/Failed message");
                    for (let client of wss.clients) {
                        if (client !== ws) {
                            const ref = aliveClients.get(client);
                            console.log("sending to: ", ref);
                            if (ref &&
                                ref.type !== "compiler" &&
                                client.readyState === ws_1.default.OPEN) {
                                client.send(message);
                            }
                        }
                    }
                    break;
                case "mobileLog":
                    const mobmessage = message.toString();
                    console.log("Broadcasting mobileLog/Failed message", mobmessage);
                    for (let client of wss.clients) {
                        const ref = aliveClients.get(client);
                        console.log("sending to: ", ref);
                        if (ref &&
                            client.readyState === ws_1.default.OPEN) {
                            client.send(JSON.stringify({ type: "log", message: mobmessage }));
                        }
                    }
                    break;
                case "register":
                    console.log("Registering client");
                    ref = aliveClients.get(ws);
                    if (ref) {
                        ref.type = msg.kind;
                        if (msg.appId) {
                            ref.appId = msg.appId;
                        }
                        ws.send(`{"status": "registered"}`);
                    }
                    else {
                        ws.send(`{"status": "failed"}`);
                    }
                    break;
                default:
                    console.log("Echoing back message");
                    ws.send(message);
            }
        });
        ws.on('close', () => {
            console.log('WebSocket connection closed');
            aliveClients.delete(ws);
        });
        ws.on('error', console.error);
    });
    const interval = setInterval(() => {
        // console.log("Socket clients maintenance");
        const wsClients = [];
        const sockets = [];
        for (let [ws, ref] of aliveClients.entries()) {
            wsClients.push(ref);
            sockets.push(ws);
        }
        const clientSnapshot = JSON.stringify({ type: 'status', clients: wsClients });
        // console.log("---", JSON.stringify(wsClients, null, 2));
        // console.log("Clients: ", wsClients.length, wss.clients.size);
        for (let i = 0; i < sockets.length; ++i) {
            const client = wsClients[i];
            const ws = sockets[i];
            if (!client.isAlive || (ws.readyState != ws_1.default.OPEN)) {
                aliveClients.delete(ws);
                console.log("Terminating websocket", aliveClients.size);
                ws.close();
                return ws.terminate();
            }
            else {
                if (client.type === 'cliui') {
                    // console.log('>>>', JSON.stringify(wsClients, null, 2));
                    ws.send(clientSnapshot);
                }
                client.isAlive = false;
                ws.ping();
            }
        }
        // console.log("-----");
    }, 5000);
    wss.on('close', () => clearInterval(interval));
}
