# JSDoc: Source: app/channel/activeChannel.js

# Source: app/channel/activeChannel.js

    /*Canopy - The next generation of stoner streaming software
    Copyright (C) 2024-2025 Rainbownapkin and the TTN Community

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.*/

    //local imports
    const connectedUser = require('./connectedUser');
    const chatBuffer = require('./chatBuffer');
    const queue = require('./media/queue');
    const channelModel = require('../../schemas/channel/channelSchema');
    const playlistHandler = require('./media/playlistHandler')

    /**
     * Class representing a single active channel
     */
    module.exports = class{

        /**
         * Instantiates an activeChannel object
         * @param {channelManager} server - Parent Server Object
         * @param {Mongoose.Document} chanDB  - chanDB to rehydrate buffer from
         */
        constructor(server, chanDB){
            this.server = server;
            this.name = chanDB.name;
            this.tokeCommands = chanDB.tokeCommands;
            //Keeping these in a map was originally a vestige but it's more preformant than an array or object so :P
            this.userList = new Map();
            this.queue = new queue(server, chanDB, this);
            this.playlistHandler = new playlistHandler(server, chanDB, this);
            //Define the chat buffer
            this.chatBuffer = new chatBuffer(server, chanDB, this);
        }

        /**
         * Handles server-side initialization for new connections to the channel
         * @param {Mongoose.Document} userDB - User Document Passthrough to save on DB Access
         * @param {Mongoose.Document} chanDB - Channnel Document Passthrough to save on DB Access
         * @param {Socket} socket - Requesting Socket
         */
        async handleConnection(userDB, chanDB, socket){
            //get current user object from the userlist
            var userObj = this.userList.get(userDB.user);
            //get channel rank for current user
            const chanRank = await chanDB.getChannelRankByUserDoc(userDB);

            //If user is already connected
            if(userObj){
                //Add this socket on to the userobject
                userObj.sockets.push(socket.id);
            //If the user is joining the channel
            }else{
                //Grab flair
                await userDB.populate('flair');
                //Set user object
                userObj = new connectedUser(userDB, chanRank, this, socket);
            }

            //Set user entry in userlist
            this.userList.set(userDB.user, userObj);

            //if everything looks good, admit the connection to the channel
            socket.join(socket.chan);

            //Define per-channel event listeners
            this.queue.defineListeners(socket);
            this.playlistHandler.defineListeners(socket);

            //Hand off the connection initiation to it's user object
            await userObj.handleConnection(userDB, chanDB, socket)

            //Send out the userlist
            this.broadcastUserList(socket.chan);
        }

        /**
         * Handles server-side initialization for disconnecting from the channel
         * @param {Socket} socket - Requesting Socket
         */
        handleDisconnect(socket){
            //If we have more than one active connection
            if(this.userList.get(socket.user.user).sockets.length > 1){
                //temporarily store userObj
                var userObj = this.userList.get(socket.user.user);

                //Filter out disconnecting socket from socket list, and set as current socket list for user
                userObj.sockets = userObj.sockets.filter((id) => {
                    return id != socket.id;
                });

                //Update the userlist
                this.userList.set(socket.user.user, userObj);
            }else{
                //If this is the last connection for this user, remove them from the userlist
                this.userList.delete(socket.user.user);
            }

            //and send out the filtered list
            this.broadcastUserList(socket.chan);
        }

        /**
         * Broadcasts user list to all users
         */
        broadcastUserList(){
            //Create a userlist object with the tokebot user pre-loaded
            var userList = [{
                user: "Tokebot",
                flair: "classic",
                highLevel: "∞",
            }];

            this.userList.forEach((userObj, user) => {
                userList.push({
                    user: user,
                    flair: userObj.flair,
                    highLevel: userObj.highLevel
                });
            });

            this.server.io.in(this.name).emit("userList", userList);
        }

        /**
         * Broadcasts channel emote list to connected users
         * @param {Mongoose.Document} chanDB - Channnel Document Passthrough to save on DB Access
         */
        async broadcastChanEmotes(chanDB){
            //if we wherent handed a channel document
            if(chanDB == null){
                //Pull it based on channel name
                chanDB = await channelModel.findOne({name: this.name});
            }

            //Get emote list from channel document
            const emoteList = chanDB.getEmotes();

            //Broadcast that sumbitch
            this.server.io.in(this.name).emit('chanEmotes', emoteList);
        }
    }