"use strict";
// pony pone pone pony
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const ws_1 = require("ws");
const InternalAPI_1 = require("./utils/InternalAPI");
const User_1 = __importDefault(require("./types/User"));
const Session_1 = __importDefault(require("./types/Session"));
const Video_1 = require("./types/Video");
const crypto_1 = require("crypto");
const wss = new ws_1.WebSocketServer({ port: 22666 });
let sessions = new Map();
let partycodes = new Map();
wss.on('connection', (ws) => {
    let user;
    let timeoutProcess = setTimeout(() => {
        ws.send(JSON.stringify({
            "task": "TERMINATE",
            "payload": {
                "code": "NO_IDENT",
                "reason": "The client did not identify in time."
            }
        }));
        ws.close();
    }, 1000);
    ws.on("message", async (data) => {
        let event = JSON.parse(data.toString());
        if (event.task === "IDENTIFY") {
            if (user !== undefined) {
                ws.send(JSON.stringify({
                    "task": "TERMINATE",
                    "payload": {
                        "code": "ALREADY_IDENT",
                        "reason": "This session has already identified."
                    }
                }));
                return ws.close();
            }
            clearTimeout(timeoutProcess);
            if (["", undefined, null].includes(event.payload["token"]) || typeof event.payload["token"] !== "string") {
                ws.send(JSON.stringify({
                    "task": "TERMINATE",
                    "payload": {
                        "code": "BAD_TOKEN",
                        "reason": "The token is not provided or malformed."
                    }
                }));
                return ws.close();
            }
            let tokenValid = await (0, InternalAPI_1.isSessionValid)(event.payload["token"]);
            if (!tokenValid) {
                ws.send(JSON.stringify({
                    "task": "TERMINATE",
                    "payload": {
                        "code": "INVALID_TOKEN",
                        "reason": "The token provided is not valid."
                    }
                }));
                return ws.close();
            }
            ws.send(JSON.stringify({
                "task": "CONFIG",
                "payload": {
                    "heartbeatInterval": 100
                }
            }));
            timeoutProcess = setTimeout(() => {
                ws.send(JSON.stringify({
                    "task": "TERMINATE",
                    "payload": {
                        "code": "HEARTBEAT_MISS",
                        "reason": "The client missed a heartbeat (dead connection)."
                    }
                }));
                ws.close();
            }, 10000);
            user = new User_1.default(ws, event.payload["token"]);
        }
        if (event.task == "HEARTBEAT") {
            clearInterval(timeoutProcess);
            timeoutProcess = setTimeout(() => {
                ws.send(JSON.stringify({
                    "task": "TERMINATE",
                    "payload": {
                        "code": "HEARTBEAT_MISS",
                        "reason": "The client missed a heartbeat (dead connection)."
                    }
                }));
                ws.close();
            }, 2000);
            if (ws["currentSessionId"] !== undefined) {
                let session = sessions.get(ws["currentSessionId"]);
                user.videoPositon = event.payload["videoPosition"];
                let index = session.users.findIndex(iuser => iuser.id == user.id);
                session.users[index].videoPositon = event.payload["videoPositon"];
                sessions.set(session.id, session);
                let delays = {};
                session.users.forEach(iuser => {
                    delays[iuser.id] = Math.floor((iuser.videoPositon * 1000) - (user.videoPositon * 1000));
                    if (session.currentVideo === null)
                        return;
                    if (session.currentVideo.state !== Video_1.VideoState.Playing)
                        return;
                    if (delays[iuser.id] > 1000 || delays[iuser.id] < -1000) {
                        ws.send(JSON.stringify({
                            "task": "VIDEO_UPDATE",
                            "payload": {
                                "position": user.videoPositon
                            }
                        }));
                    }
                });
                ws.send(JSON.stringify({
                    "task": "HEARTBEAT_ACK",
                    "payload": {
                        "delays": delays
                    }
                }));
            }
            else {
                ws.send(JSON.stringify({
                    "task": "HEARTBEAT_ACK",
                    "payload": {
                        "delays": []
                    }
                }));
            }
        }
        if (event.task == "SESSION") {
            if (event.payload["id"] == null) {
                let session = new Session_1.default();
                session.users.push(user);
                sessions.set(session.id, session);
                partycodes.set(session.partyCode, session.id);
                ws["currentSessionId"] = session.id;
                let safeUsers = [];
                session.users.forEach(iuser => {
                    safeUsers.push({
                        id: iuser.id
                    });
                });
                ws.send(JSON.stringify({
                    "task": "SESSION",
                    "payload": {
                        "code": session.partyCode,
                        "users": safeUsers,
                        "queue": session.videoQueue
                    }
                }));
                /*let video = await user.getYoutubeVideo("wDVLrJESFNI");
                let ongoingVideo = video.toOngoingVideo();

                session.currentVideo = ongoingVideo;

                sessions.set(session.id, session);

                ws.send(JSON.stringify({
                    "task": "VIDEO_UPDATE",
                    "payload": {
                        "url": ongoingVideo.url,
                        "title": ongoingVideo.title,
                        "author": ongoingVideo.author,
                        "thumbnail": ongoingVideo.thumbnail,
                        "state": ongoingVideo.state,
                        "position": ongoingVideo.position
                    }
                }));*/
            }
            else {
                if (["", undefined, null].includes(event.payload["id"]) || typeof event.payload["id"] != "string")
                    return ws.send(JSON.stringify({
                        "task": "FAILURE",
                        "payload": {
                            "code": "BAD_CODE",
                            "reason": "The party code is not present or is malformed."
                        }
                    }));
                if (!partycodes.has(event.payload["id"]))
                    return ws.send(JSON.stringify({
                        "task": "FAILURE",
                        "payload": {
                            "code": "INVALID_CODE",
                            "reason": "This party code is not valid."
                        }
                    }));
                let sessionId = partycodes.get(event.payload["id"]);
                let session = sessions.get(sessionId);
                ws["currentSessionId"] = session.id;
                session.users.push(user);
                sessions.set(session.id, session);
                let safeUsers = [];
                session.users.forEach(iuser => {
                    safeUsers.push({
                        id: iuser.id
                    });
                });
                session.users.forEach(iuser => {
                    if (iuser.id == user.id)
                        return;
                    iuser.ws.send(JSON.stringify({
                        "task": "UPDATE_USERS",
                        "payload": {
                            "users": safeUsers
                        }
                    }));
                });
                ws.send(JSON.stringify({
                    "task": "SESSION",
                    "payload": {
                        "code": session.partyCode,
                        "users": safeUsers,
                        "queue": session.videoQueue
                    }
                }));
                if (session.currentVideo != null) {
                    ws.send(JSON.stringify({
                        "task": "VIDEO_UPDATE",
                        "payload": {
                            "id": session.currentVideo.id,
                            "url": session.currentVideo.url,
                            "title": session.currentVideo.title,
                            "author": session.currentVideo.author,
                            "duration": session.currentVideo.duration,
                            "duration_pretty": session.currentVideo.duration_pretty,
                            "thumbnail": session.currentVideo.thumbnail,
                            "state": session.currentVideo.state,
                            "position": session.currentVideo.position
                        }
                    }));
                }
            }
        }
        if (event.task == "VIDEO_UPDATE") {
            let session = sessions.get(ws["currentSessionId"]);
            if (session.currentVideo !== null && session.currentVideo.state === Video_1.VideoState.Loading)
                return;
            if (session.currentVideo) {
                if (event.payload["state"] === 2) {
                    session.currentVideo.state = Video_1.VideoState.Buffering;
                }
                else if (event.payload["state"] === 1) {
                    session.currentVideo.state = Video_1.VideoState.Playing;
                }
                else if (event.payload["state"] === 0) {
                    session.currentVideo.state = Video_1.VideoState.Paused;
                }
                session.currentVideo.position = event.payload["position"];
                if (event.payload["state"] === 0 && Math.floor(event.payload["position"]) >= (session.currentVideo.duration - 1)) {
                    console.log("aaaa gotta change (Twi is cute btw)");
                    session.currentVideo.state = Video_1.VideoState.Loading;
                    setTimeout(() => {
                        if (session.videoQueue.length === 0)
                            session.currentVideo = null;
                        else
                            session.currentVideo = (session.videoQueue.shift()).toOngoingVideo();
                        if (session.currentVideo !== null)
                            session.currentVideo.state = Video_1.VideoState.Playing;
                        session.users.forEach(iuser => {
                            iuser.ws.send(JSON.stringify({
                                "task": "VIDEO_UPDATE",
                                "payload": {
                                    "id": session.currentVideo ? session.currentVideo.id : null,
                                    "sha": session.currentVideo ? (0, crypto_1.createHash)("sha256").update(session.currentVideo.id).digest("hex") : null,
                                    "url": session.currentVideo ? session.currentVideo.url : null,
                                    "title": session.currentVideo ? session.currentVideo.title : null,
                                    "duration": session.currentVideo ? session.currentVideo.duration : null,
                                    "duration_pretty": session.currentVideo ? session.currentVideo.duration_pretty : null,
                                    "author": session.currentVideo ? session.currentVideo.author : null,
                                    "thumbnail": session.currentVideo ? session.currentVideo.thumbnail : null,
                                    "state": session.currentVideo ? session.currentVideo.state : null,
                                    "position": session.currentVideo ? session.currentVideo.position : null
                                }
                            }));
                            iuser.ws.send(JSON.stringify({
                                "task": "UPDATE_QUEUE",
                                "payload": {
                                    "queue": session.videoQueue,
                                    "poster": user.id
                                }
                            }));
                        });
                    }, 5000);
                }
                else {
                    session.users.forEach(iuser => {
                        if (iuser.id == user.id)
                            return;
                        iuser.ws.send(JSON.stringify({
                            "task": "VIDEO_UPDATE",
                            "payload": {
                                "state": event.payload["state"],
                                "position": event.payload["position"]
                            }
                        }));
                    });
                }
            }
            sessions.set(session.id, session);
        }
        if (event.task == "UPDATE_QUEUE") {
            let session = sessions.get(ws["currentSessionId"]);
            if (event.payload["operation"] == "+") {
                let video = await user.getYoutubeVideo(event.payload["video"]);
                session.videoQueue.push(video);
                if (session.videoQueue.length === 1 && session.currentVideo === null) {
                    session.currentVideo = (session.videoQueue.shift()).toOngoingVideo();
                    session.users.forEach(user => {
                        user.ws.send(JSON.stringify({
                            "task": "VIDEO_UPDATE",
                            "payload": {
                                "id": session.currentVideo.id,
                                "sha": (0, crypto_1.createHash)("sha256").update(session.currentVideo.id).digest("hex"),
                                "url": session.currentVideo.url,
                                "title": session.currentVideo.title,
                                "author": session.currentVideo.author,
                                "thumbnail": session.currentVideo.thumbnail,
                                "state": session.currentVideo.state,
                                "position": session.currentVideo.position
                            }
                        }));
                    });
                }
                sessions.set(session.id, session);
            }
            session.users.forEach(iuser => {
                iuser.ws.send(JSON.stringify({
                    "task": "UPDATE_QUEUE",
                    "payload": {
                        "queue": session.videoQueue,
                        "poster": user.id
                    }
                }));
            });
        }
    });
    ws.on("close", () => {
        if (ws["currentSessionId"] != undefined) {
            if (sessions.has(ws["currentSessionId"])) {
                let session = sessions.get(ws["currentSessionId"]);
                session.users = session.users.filter((iuser) => iuser.id != user.id);
                sessions.set(session.id, session);
                if (session.users.length == 0) {
                    sessions.delete(session.id);
                    partycodes.delete(session.partyCode);
                }
                else {
                    let safeUsers = [];
                    session.users.forEach(iuser => {
                        safeUsers.push({
                            id: iuser.id
                        });
                    });
                    session.users.forEach(iuser => {
                        if (iuser.id == user.id)
                            return;
                        iuser.ws.send(JSON.stringify({
                            "task": "UPDATE_USERS",
                            "payload": {
                                "users": safeUsers
                            }
                        }));
                    });
                }
            }
        }
    });
});
//# sourceMappingURL=index.js.map