Source: core/Msgs.js

const fs        = require('fs-extra');
const path      = require('path');
const Utils     = require('../Utils.js');

/**
 * Messages that the server listens for from clients.
 * Includes try-catch and notification (via console) of receipt.
 * Corresponds to client/internal/clients/admin/shared/msgsToServer.js
 */
class Msgs {

    /*
     * Returns a new instance of this class.
     *
     * @param  {type} jt description
     * @return {type}    description
     */
    constructor(jt) {
        this.jt = jt;
    }

    /*
     * addParticipants - Adds the specified number of participants to the session.
     *
     * @param  {type} d      an object with the following fields,
     * @param {string} d.sId the id of the sessions
     * @param {number} d.number the number of participants to add.
     * @param  {Socket} socket the client socket who sent the message.
     */
    addParticipants(d, socket) {
        var session = this.jt.data.getSession(d.sId);
        var num = parseInt(d.number);
        session.addParticipants(num);
    }

    appSaveFileContents(d, socket) {
        var app = this.jt.data.app(d.aId, d.options);
        app.setFileContents(d.content);
        app = app.reload();
        this.jt.data.appsMetaData[d.aId] = app.metaData();
    }

    getClients(data, socket) {
        let clients = this.jt.data.getClients(data.sessionId);
        var message = {cb: data.cb, clients: clients};
        socket.emit('dataMessage', message);
    }

    /**
     * getAppMetadatas - Get an array of the app metadatas found on the server.
     *
     * @param  {String} cb     The callback to execute when returning the metadatas to the client.
     * @param  {Socket} socket The socket which asked for the data.
     * @return {Message}        A message object, which includes callback (cb) and data fields.
     */
    getAppMetadatas(cb, socket) {
        var apps = this.jt.data.getApps();
        var metadatas = [];
        for (var i in apps) {
            metadatas.push(apps[i].metaData());
        }
        var message = {cb: cb, metadatas: metadatas};
        this.jt.io.to('socket_' + socket.id).emit('dataMessage', message);
    }

    /**
     * getApp - Return the app with identifier data.appId.
     *
     * @param  {String} data.appId   Identifier of the app.
     * @param  {Socket} socket The socket which asked for the app.
     * @return {Message}        A message object, which includes callback (cb) and data fields.
     */
    getApp(data, socket) {
        data.app = this.jt.data.getApp(data.appPath).shellWithChildren();
        this.jt.io.to('socket_' + socket.id).emit('dataMessage', data);
    }

    setAppContents(data, socket) {
        fs.writeFileSync(path.join(this.jt.path, data.appPath), data.content);
        var outMessage = {};
        outMessage.appPath = data.appPath;
        outMessage.cb   = data.cb;
        outMessage.app  = this.jt.data.getApp(data.appPath).shellWithChildren();
        this.jt.io.to('socket_' + socket.id).emit('dataMessage', outMessage);
    }

    setSessionId(data, socket) {
        var session = this.jt.data.getSession(data.oldId);
        session.setId(data.newId);
        this.jt.io.to('socket_' + socket.id).emit('setSessionId', data);
    }

    renameApp(data, socket) {
        fs.renameSync(data.originalId, data.newId);
        this.jt.data.appsMetaData[data.newId] = this.jt.data.appsMetaData[data.originalId];
        delete this.jt.data.appsMetaData[data.originalId];
        this.jt.data.appsMetaData[data.newId].appPath = data.newId;
        this.jt.data.appsMetaData[data.newId].id = data.newId;
        let sep = '\\';
        if (data.newId.includes('/')) {
            sep = '/';
        }
        let lastFolderChar = data.newId.lastIndexOf(sep);
        this.jt.data.appsMetaData[data.newId].shortId = data.newId.substring(lastFolderChar+1);
    }

    /*
     * setNumParticipants - Sets the specified number of participants to the session.
     *
     * @param  {type} d      an object with the following fields,
     * @param {string} d.sId the id of the sessions
     * @param {number} d.number the number of participants for the session.
     * @param  {Socket} socket the client socket who sent the message.
     */
    setNumParticipants(d, socket) {
        var session = this.jt.data.getSession(d.sId);
        var num = parseInt(d.number);
        session.setNumParticipants(num);
    }

    // setSessionOption(d, socket) {
    //     let session = this.jt.data.getSession(d.sessionId);
        
    // }

    resetSession(d, socket) {
        let session = this.jt.data.getSession(d.sId);
        session.reset();
        this.jt.io.to('socket_' + socket.id).emit('openSession', session.shellWithChildren());
        this.jt.data.lastOpenedSession = session;
    }

    appAddStage(d, socket) {
        var session = this.jt.data.getApp(d.aId);
    }

    deleteParticipant(d, socket) {
        var session = this.jt.data.getSession(d.sId);
        session.deleteParticipant(d.pId);
    }

    saveOutput(sId, socket) {
        var session = this.jt.data.getSession(sId);
        session.saveOutput();
    }

    setDefaultAdminPwd(d, sock) {
        this.jt.settings.setDefaultAdminPwd(d.curPwd, d.newPwd);
    }

    setUsersMode(d, sock) {
        this.jt.settings.setMultipleUsers(d);
    }

    createSessionAndAddApp(msgData, sock) {
        var appPath = msgData.appId;
        var options = msgData.options;
        var session = this.jt.data.createSession(msgData.userId);
        session.resume();
        this.jt.data.sessions.push(session);
        var d = {sId: session.id, appPath: appPath, options: options};
        this.sessionAddApp(d);
        this.openSession(session.id, sock);
    }

    createApp(appId, sock) {
        var app = this.jt.data.createApp(appId);
        if (app !== null) {
            this.jt.socketServer.emitToAdmins('createApp', app);
        }
        return app;
    }

    createAppFromFile(data, sock) {
        var app = this.jt.data.createApp(data.fn);
        app.setContents(data.contents);
        if (app !== null) {
            this.jt.socketServer.emitToAdmins('createApp', app);
        }
        return app;
    }

    updateAppPreview(d, socket) {
        var app = this.jt.data.app(d.appId, d.options);
        this.jt.io.to('socket_' + socket.id).emit('updateAppPreview', app.shellWithChildren());
    }

    startSessionFromQueue(data, sock) {
        var session = this.jt.data.createSession(data.userId);
        session.resume();
        this.jt.data.sessions.push(session);
        var queue = this.jt.data.queue(data.qId);
        session.queue = queue;
        let options = data.options;
        for (let i in options) {
            session[i] = options[i];
        }
        // if (queue.code == null) {
        //     for (var a in queue.apps) {
        //         var app = queue.apps[a];
        //         var d = {sId: session.id, appPath: app.appId, options: app.options};
        //         this.sessionAddApp(d);
        //     }
        // } else {
            session.queuePath = path.dirname(queue.id);
            eval(queue.code);
        // }
        this.openSession(session.id, sock);
    }

    createRoom(id, sock) {
        var room = this.jt.data.createRoom(id);
        if (room !== null) {
            this.jt.socketServer.emitToAdmins('createRoom', room.shell());
        }
        return room;
    }

    createUser(data, sock) {
        var user = this.jt.data.createUser(data.id, data.type);
        if (user !== null) {
            this.jt.socketServer.emitToAdmins('createUser', user.shell());
        }
        return user;
    }

    createQueue(id, sock) {
        var queue = this.jt.data.createQueue(id);
        if (queue !== null) {
            this.jt.socketServer.emitToAdmins('createQueue', queue.shell());
        }
        return queue;
    }

    saveRoom(room, sock) {
        this.jt.data.saveRoom(room);
//        this.jt.socketServer.emitToAdmins('saveRoom', room);a
    }

    deleteQueue(id, sock) {
        this.jt.data.deleteQueue(id);
        this.jt.socketServer.emitToAdmins('deleteQueue', id);
    }

    deleteAppFromQueue(qId, aId, appIndex) {
        var queue = this.jt.data.queue(qId);
        queue.deleteApp(aId, appIndex);
        this.jt.socketServer.emitToAdmins('deleteAppFromQueue', {qId: qId, aId: aId, appIndex: appIndex});
    }

    deleteApp(id, sock) {
        this.jt.data.deleteApp(id);
        this.jt.socketServer.emitToAdmins('deleteApp', id);
    }

    saveAppHTML(app, sock) {
        this.jt.data.saveApp(app);
        var appInfo = this.jt.data.apps[app.id];
        appInfo.origId = app.origId;
        this.jt.socketServer.emitToAdmins('appSaved', appInfo);
    }

    deleteSession(d) {
        var mypath = path.join(this.jt.path, this.jt.settings.sessionsFolder, d);
        for (var i in this.jt.data.sessions) {
            var session = this.jt.data.sessions[i];
            if (d === session.id) {
                if (session.autoSaveTimer !== null) {
                    clearInterval(session.autoSaveTimer);
                }
                if (session.fileStream !== null) {
                    session.fileStream.end();
                }
                this.jt.data.sessions.splice(i, 1);
                break;
            }
        }
        // TODO: Does not delete on Windows.
        // Workaround: delete empty folders on Data.loadSessions.
        try {
            if (fs.existsSync(mypath)) {
                fs.removeSync(mypath);
            }
        } catch (err) {}

        this.jt.socketServer.emitToAdmins('deleteSession', d);
    }

    messages(data, sock) {
        for (var i=0; i<data.length; i++) {
            var msgName = data[i].msgName;
            var msgData = data[i].msgData;
            this.jt.log('received message ' + msgName + ': ' + JSON.stringify(msgData));
            eval('this.' + msgName + "(msgData, sock)");
        }
    }

    openSession(sId, socket) {
        var session = Utils.findByIdWOJQ(this.jt.data.sessions, sId);
        if (session !== null && session !== undefined) {
            socket.join(session.roomId());
            this.jt.io.to('socket_' + socket.id).emit('openSession', session.shellWithChildren());
            this.jt.data.lastOpenedSession = session;
            // this.reloadClients();
            // Called from client-side.
        }
    }

    reloadApps(msg, socket) {
        this.jt.data.reloadApps();
        if (msg == null) {
            msg = {
                userId: ''
            }
        }
        this.jt.socketServer.refreshAdmin(null, 'socket_' + socket.id, msg.userId);
    }

    reloadClients() {
        const clients = this.jt.data.participantClients;
        for (let i=0; i<clients.length; i++) {
            try {
                clients[i].reload();
            } catch (err) {}
        }
    }


    /**
     * sessionAddApp - Add an app to the given session.
     *
     * @param  {Object} d An object containing the session ID (sId), the app id (appPath), and options for the app (options).
     * @return {type}
     */
    sessionAddApp(data, socket) {
        if (data.appPath == null) {
            data.appPath = data.appId;
        }
        this.jt.data.getSession(data.sId).addApp(data.appPath, data.options);
    }

    sessionAddUser(d) {
        this.jt.data.getSession(d.sId).addUser(d.uId);
    }

    sessionAddQueue(d) {
        this.jt.data.getSession(d.sId).addQueue(d.qId);
    }

    roomAddApp(d) {
        this.jt.data.room(d.roomId).addApp(d.appId);
    }

    queueAddApp(d) {
        this.jt.data.queue(d.queueId).addApp(d.appId, d.options);
    }

    /*
     * Advance all participants who have not started the session yet. Calls {@link Session#advanceSlowest}.
     *
     * @param  {string} id The id of the session.
     */
    sessionStart(id) {
        var session = Utils.findByIdWOJQ(this.jt.data.sessions, id);
        if (session !== null) {
            session.start();
        }
    }

    /*
     * Advances the slowest participants in the given session. Calls {@link Session#advanceSlowest}.
     *
     * @param  {string} id The id of the session.
     */
    sessionAdvanceSlowest(id) {
        var session = Utils.findByIdWOJQ(this.jt.data.sessions, id);
        if (session !== null) {
            session.advanceSlowest();
        }
        session.emitParticipantUpdates();
    }

    sessionCreate(userId, sock) {
        var session = this.jt.data.createSession(userId);
        session.resume();
        this.jt.data.sessions.push(session);
        this.openSession(session.id, sock);
    }

    sessionDeleteApp(d) {
        var session = Utils.findByIdWOJQ(this.jt.data.sessions, d.sId);
        if (session !== null) {
            session.deleteApp(d);
        }
    }

    setAllowAdminPlay(d, socket) {
        let session = Utils.findByIdWOJQ(this.jt.data.sessions, d.sessionId);
        if (session != null) {
            session.setAllowAdminPlay(d.val);
        }
    }

    setSessionAppOption(d, socket) {
        var session = Utils.findByIdWOJQ(this.jt.data.sessions, d.sId);
        if (session !== null) {
            session.setOption(d.name, d.value);
        }
    }

    setSessionAppOptions(d, socket) {
        var session = Utils.findByIdWOJQ(this.jt.data.sessions, d.sId);
        if (session !== null) {
            for (var i in d.options) {
                session.setAppOption(d.appId, d.index, i, d.options[i]);
            }
        }
    }

    sessionPause(id, sock) {
        var session = Utils.findByIdWOJQ(this.jt.data.sessions, id);
        if (session !== null) {
            session.pause();
        }
    }

    sessionResume(id, sock) {
        var session = Utils.findByIdWOJQ(this.jt.data.sessions, id);
        if (session !== null) {
            session.resume();
        }
    }

    setAllowNewParts(d, socket) {
        var session = this.jt.data.getSession(d.sId);
        session.setAllowNewParts(d.value);
    }

    setCaseSensitiveLabels(d, socket) {
        var session = this.jt.data.getSession(d.sId);
        session.setCaseSensitiveLabels(d.value);
    }

    setAutoplay(data) {
        let session = this.jt.data.session(data.sId);
        if (session !== null) {
            let part = session.participants[data.pId];
            if (part !== undefined) {
                part.emit('setAutoplay', {val: data.val});
            } else {
                // console.log('Msgs.setAutoplay: undefined participant ' + data.pId);
            }
        }
    }

    setAutoplayForAll(data) {
        let session = this.jt.data.session(data.sId);
        if (session !== null) {
            for (let i in session.participants) {
                session.participants[i].emit('setAutoplay', {val: data.val});
            }
        }
    }

    setAutoplayDelay(data) {
        let session = this.jt.data.session(data.sId);
        if (session !== null) {
            for (let i in session.participants) {
                session.participants[i].emit('setAutoplayDelay', {val: data.val});
            }
        }
    }

}

var exports = module.exports = {};
exports.new = Msgs;