#include sockchat/utils.js

const SockChatS2CPong = ctx => {
    const lastPong = Date.now();
    const lastPing = ctx.lastPing;

    ctx.lastPing = undefined;
    if(lastPing === undefined)
        throw 'unexpected pong received??';

    ctx.pingPromise?.resolve({
        ping: lastPing,
        pong: lastPong,
        diff: lastPong - lastPing,
    });
};

const SockChatS2CBanKick = (ctx, type, expiresTime) => {
    ctx.wasKicked = true;

    const bakaInfo = {
        session: { success: false },
        baka: {
            type: type === '0' ? 'kick' : 'ban',
        },
    };

    if(bakaInfo.baka.type === 'ban') {
        bakaInfo.baka.perma = expiresTime === '-1';
        bakaInfo.baka.until = expiresTime === '-1' ? undefined : new Date(parseInt(expiresTime) * 1000);
    }

    ctx.dispatch('session:term', bakaInfo);
};

const SockChatS2CContextClear = (ctx, mode) => {
    if(mode === '0' || mode === '3' || mode === '4')
        ctx.dispatch('msg:clear');

    if(mode === '1' || mode === '3' || mode === '4')
        ctx.dispatch('user:clear');

    if(mode === '2' || mode === '4')
        ctx.dispatch('chan:clear');
};

const SockChatS2CChannelPopulate = (ctx, count, ...args) => {
    count = parseInt(count);
    ctx.dispatch('chan:clear');

    for(let i = 0; i < count; ++i) {
        const offset = 3 * i;

        ctx.dispatch('chan:add', {
            channel: {
                name: args[offset],
                hasPassword: args[offset + 1] !== '0',
                isTemporary: args[offset + 2] !== '0',
                isCurrent: args[offset] === ctx.channelName,
            },
        });
    }

    ctx.dispatch('chan:focus', {
        channel: { name: ctx.channelName },
    });
};

const SockChatS2CChannelAdd = (ctx, name, hasPass, isTemp) => {
    ctx.dispatch('chan:add', {
        channel: {
            name: name,
            hasPassword: hasPass !== '0',
            isTemporary: isTemp !== '0',
        },
    });
};

const SockChatS2CChannelUpdate = (ctx, prevName, name, hasPass, isTemp) => {
    ctx.dispatch('chan:update', {
        channel: {
            previousName: prevName,
            name: name,
            hasPassword: hasPass !== '0',
            isTemporary: isTemp !== '0',
        },
    });
};

const SockChatS2CChannelRemove = (ctx, name) => {
    ctx.dispatch('chan:remove', {
        channel: { name: name },
    });
};

const SockChatS2CUserPopulate = (ctx, count, ...args) => {
    count = parseInt(count);
    ctx.dispatch('user:clear');

    for(let i = 0; i < count; ++i) {
        const offset = 5 * i;
        const statusInfo = SockChatParseStatusInfo(args[offset + 1]);

        ctx.dispatch('user:add', {
            user: {
                id: args[offset],
                self: args[offset] === ctx.userId,
                name: statusInfo.name,
                status: statusInfo.status,
                colour: SockChatParseUserColour(args[offset + 2]),
                perms: SockChatParseUserPerms(args[offset + 3]),
                hidden: args[offset + 4] !== '0',
            },
        });
    }
};

const SockChatS2CUserAdd = (ctx, timeStamp, userId, userName, userColour, userPerms, msgId) => {
    const statusInfo = SockChatParseStatusInfo(userName);

    ctx.dispatch('user:add', {
        msg: {
            id: msgId,
            time: new Date(parseInt(timeStamp) * 1000),
            channel: ctx.channelName,
            botInfo: {
                type: 'join',
                args: [statusInfo.name],
            },
        },
        user: {
            id: userId,
            self: userId === ctx.userId,
            name: statusInfo.name,
            status: statusInfo.status,
            colour: SockChatParseUserColour(userColour),
            perms: SockChatParseUserPerms(userPerms),
        },
    });
};

const SockChatS2CUserUpdate = (ctx, userId, userName, userColour, userPerms) => {
    const statusInfo = SockChatParseStatusInfo(userName);

    ctx.dispatch('user:update', {
        user: {
            id: userId,
            self: userId === ctx.userId,
            name: statusInfo.name,
            status: statusInfo.status,
            colour: SockChatParseUserColour(userColour),
            perms: SockChatParseUserPerms(userPerms),
        },
    });
};

const SockChatS2CUserRemove = (ctx, userId, userName, reason, timeStamp, msgId) => {
    const statusInfo = SockChatParseStatusInfo(userName);

    ctx.dispatch('user:remove', {
        leave: { type: reason },
        msg: {
            id: msgId,
            time: new Date(parseInt(timeStamp) * 1000),
            channel: ctx.channelName,
            botInfo: {
                type: reason,
                args: [statusInfo.name],
            },
        },
        user: {
            id: userId,
            self: userId === ctx.userId,
            name: statusInfo.name,
            status: statusInfo.status,
        },
    });
};

const SockChatS2CUserChannelJoin = (ctx, userId, userName, userColour, userPerms, msgId) => {
    const statusInfo = SockChatParseStatusInfo(userName);

    ctx.dispatch('chan:join', {
        user: {
            id: userId,
            self: userId === ctx.userId,
            name: statusInfo.name,
            status: statusInfo.status,
            colour: SockChatParseUserColour(userColour),
            perms: SockChatParseUserPerms(userPerms),
        },
        msg: {
            id: msgId,
            channel: ctx.channelName,
            botInfo: {
                type: 'jchan',
                args: [statusInfo.name],
            },
        },
    });
};

const SockChatS2CUserChannelLeave = (ctx, userId, msgId) => {
    ctx.dispatch('chan:leave', {
        user: {
            id: userId,
            self: userId === ctx.userId,
        },
        msg: {
            id: msgId,
            channel: ctx.channelName,
            botInfo: {
                type: 'lchan',
                args: [userId],
            },
        },
    });
};

const SockChatS2CUserChannelFocus = (ctx, name) => {
    ctx.channelName = name;

    ctx.dispatch('chan:focus', {
        channel: { name: ctx.channelName },
    });
};

const SockChatS2CMessagePopulate = (ctx, timeStamp, userId, userName, userColour, userPerms, msgText, msgId, msgNotify, msgFlags) => {
    const mFlags = SockChatParseMsgFlags(msgFlags);
    const statusInfo = SockChatParseStatusInfo(userName);
    const info = {
        msg: {
            id: msgId,
            time: new Date(parseInt(timeStamp) * 1000),
            channel: ctx.channelName,
            sender: {
                id: userId,
                self: userId === ctx.userId,
                name: statusInfo.name,
                status: statusInfo.status,
                colour: SockChatParseUserColour(userColour),
                perms: SockChatParseUserColour(userPerms),
            },
            isBot: userId === '-1',
            silent: msgNotify === '0',
            flags: mFlags,
            flagsRaw: msgFlags,
            text: SockChatUnfuckText(msgText, mFlags.isAction),
        },
    };

    const msgIdFirst = info.msg.id.charCodeAt(0);
    if(msgIdFirst < 48 || msgIdFirst > 57)
        info.msg.id = (Math.round(Number.MIN_SAFE_INTEGER * Math.random())).toString();

    if(info.msg.isBot) {
        const botParts = msgText.split("\f");
        info.msg.botInfo = {
            isError: botParts[0] === '1',
            type: botParts[1],
            args: botParts.slice(2),
        };

        // i think this is more Inaccurate Behaviour on the server side
        if(info.msg.botInfo.type === 'say')
            info.msg.botInfo.args[0] = SockChatUnfuckText(info.msg.botInfo.args[0]);
    }

    ctx.dispatch('msg:add', info);
};

const SockChatS2CMessageAdd = (ctx, timeStamp, userId, msgText, msgId, msgFlags) => {
    const mFlags = SockChatParseMsgFlags(msgFlags);
    let mText = SockChatUnfuckText(msgText, mFlags.isAction);
    let mChannelName = ctx.channelName;

    if(msgFlags[4] !== '0') {
        if(userId === ctx.userId) {
            const mTextParts = mText.split(' ');
            mChannelName = `@${mTextParts.shift()}`;
            mText = mTextParts.join(' ');
        } else {
            mChannelName = `@~${userId}`;
        }
    }

    const msgInfo = {
        msg: {
            id: msgId,
            time: new Date(parseInt(timeStamp) * 1000),
            channel: mChannelName,
            sender: {
                id: userId,
                self: userId === ctx.userId,
            },
            flags: mFlags,
            flagsRaw: msgFlags,
            isBot: userId === '-1',
            text: mText,
        },
    };

    if(msgInfo.msg.isBot) {
        const botParts = msgText.split("\f");
        msgInfo.msg.botInfo = {
            isError: botParts[0] === '1',
            type: botParts[1],
            args: botParts.slice(2),
        };
    }

    ctx.dispatch('msg:add', msgInfo);
};

const SockChatS2CMessageRemove = (ctx, msgId) => {
    ctx.dispatch('msg:remove', {
        msg: {
            id: msgId,
            channel: ctx.channelName,
        },
    });
};
