#include channels.js
#include common.js
#include parsing.js
#include title.js
#include txtrigs.js
#include url.js
#include users.js
#include utility.js
#include weeb.js
#include sound/umisound.js
#include ui/emotes.js

Umi.UI.Messages = (function() {
    let focusChannelName = '';

    const title = new MamiWindowTitle({
        getName: () => futami.get('title'),
    });

    window.addEventListener('focus', () => title.clear());

    const shouldDisplayAuthorInfo = (target, ref) => {
        if(!(target instanceof Element) || !(ref instanceof Element))
            return true;

        return target.dataset.tiny !== undefined
            || target.dataset.author !== ref.dataset.author
            || target.dataset.channel !== ref.dataset.channel
            || target.dataset.tiny !== ref.dataset.tiny;
    };

    const botMsgs = {
        'say': { text: '%0' },
        'join': { text: '%0 has joined.', action: 'has joined', sound: 'join' },
        'leave': { text: '%0 has disconnected.', action: 'has disconnected', avatar: 'greyscale', sound: 'leave' },
        'jchan': { text: '%0 has joined the channel.', action: 'has joined the channel', sound: 'join' },
        'lchan': { text: '%0 has left the channel.', action: 'has left the channel', avatar: 'greyscale', sound: 'leave' },
        'kick': { text: '%0 got bludgeoned to death.', action: 'got bludgeoned to death', avatar: 'invert', sound: 'kick' },
        'flood': { text: '%0 got kicked for flood protection.', action: 'got kicked for flood protection', avatar: 'invert', sound: 'flood' },
        'timeout': { text: '%0 exploded.', action: 'exploded', avatar: 'greyscale', sound: 'timeout' },
        'nick': { text: '%0 changed their name to %1.', action: 'changed their name to %1' },
        'ipaddr': { text: 'IP address of %0 is %1.' },
        'banlist': {
            text: 'Banned: %0',
            filter: args => {
                const bans = args[0].split(', ');
                for(const i in bans)
                    bans[i] = bans[i].slice(92, -4);

                args[0] = bans.join(', ');
                return args;
            },
        },
        'who': {
            text: 'Online: %0',
            filter: args => {
                const users = args[0].split(', ');
                for(const i in users) {
                    const isSelf = users[i].includes(' style="font-weight: bold;"');
                    users[i] = users[i].slice(isSelf ? 102 : 75, -4);
                    if(isSelf) users[i] += ' (You)';
                }

                args[0] = users.join(', ');
                return args;
            },
        },
        'whochan': {
            text: 'Online in %0: %1',
            filter: args => {
                const users = args[1].split(', ');
                for(const i in users) {
                    const isSelf = users[i].includes(' style="font-weight: bold;"');
                    users[i] = users[i].slice(isSelf ? 102 : 75, -4);
                    if(isSelf) users[i] += ' (You)';
                }

                args[1] = users.join(', ');
                return args;
            },
        },
    };

    const formatTemplate = (template, args) => {
        if(typeof template !== 'string')
            template = '';

        if(Array.isArray(args))
            for(let i = 0; i < args.length; ++i) {
                const arg = args[i] === undefined || args[i] === null ? '' : args[i].toString();
                template = template.replace(new RegExp(`%${i}`, 'g'), arg);
            }

        return template;
    };

    return {
        Add: function(msg) {
            const elementId = `message-${msg.id}`;

            if(msg.id !== '' && $i(elementId))
                return;

            let isTiny = false;
            let skipTextParsing = false;
            let msgText = '';
            let msgTextLong = '';
            let msgAuthor = msg.author;

            let soundIsLegacy = true;
            let soundName;
            let soundVolume;
            let soundRate;

            const eText = <div/>;
            const eAvatar = <div class="message__avatar"/>;
            const eUser = <div class="message__user"/>;
            const eMeta = <div class="message__meta">{eUser}</div>;
            const eTime = <div class="message__time">
                {msg.created.getHours().toString().padStart(2, '0')}
                :{msg.created.getMinutes().toString().padStart(2, '0')}
                :{msg.created.getSeconds().toString().padStart(2, '0')}
            </div>;
            const eContainer = <div class="message__container">{eMeta}</div>;
            const eBase = <div class="message">
                {eAvatar}
                {eContainer}
            </div>;

            if(msg.type.startsWith('message:')) {
                msgText = msgTextLong = msg.detail.body;
                soundName = msg.author?.self === true ? 'outgoing' : 'incoming';

                if(msg.type === 'message:action')
                    isTiny = true;

                if(mami.settings.get('playJokeSounds'))
                    try {
                        const trigger = mami.textTriggers.getTrigger(msgText);
                        if(trigger.isSoundType()) {
                            soundIsLegacy = false;
                            soundName = trigger.getRandomSoundName();
                            soundVolume = trigger.getVolume();
                            soundRate = trigger.getRate();
                        }
                    } catch(ex) {}
            } else {
                let bIsError = false;
                let bType;
                let bArgs;

                if(msg.type === 'user:join') {
                    bType = 'join';
                    bArgs = [msgAuthor.name];
                } else if(msg.type === 'user:leave') {
                    bType = msg.detail.reason;
                    bArgs = [msgAuthor.name];
                } else if(msg.type === 'channel:join') {
                    bType = 'jchan';
                    bArgs = [msgAuthor.name];
                } else if(msg.type === 'channel:leave') {
                    bType = 'lchan';
                    bArgs = [msgAuthor.name];
                } else if(msg.type.startsWith('legacy:')) {
                    bType = msg.type.substring(7);
                    bIsError = msg.detail.error;
                    bArgs = msg.detail.args;
                }

                soundName = bIsError ? 'error' : 'server';

                if(botMsgs.hasOwnProperty(bType)) {
                    const bmInfo = botMsgs[bType];

                    if(typeof bmInfo.filter === 'function')
                        bArgs = bmInfo.filter(bArgs);

                    if(typeof bmInfo.sound === 'string')
                        soundName = bmInfo.sound;

                    let actionSuccess = false;
                    if(typeof bmInfo.action === 'string')
                        if(msgAuthor) {
                            actionSuccess = true;
                            isTiny = true;
                            skipTextParsing = true;

                            msgText = formatTemplate(bmInfo.action, bArgs);
                            if(typeof bmInfo.avatar === 'string')
                                eAvatar.classList.add(`avatar-filter-${bmInfo.avatar}`);
                        }

                    msgTextLong = formatTemplate(bmInfo.text, bArgs);

                    if(!actionSuccess)
                        msgText = msgTextLong;
                } else
                    msgText = msgTextLong = `!!! Received unsupported message type: ${msg.type} !!!`;
            }

            if(msgAuthor !== null) {
                eUser.style.color = msgAuthor.colour;
                eUser.textContent = msgAuthor.name;
            }

            if(isTiny) {
                eText.classList.add('message-tiny-text');
                eBase.classList.add('message-tiny');

                if(msgText.indexOf("'") !== 0 || (msgText.match(/\'/g).length % 2) === 0)
                    msgText = "\xA0" + msgText;

                eMeta.append(eText, eTime);
            } else {
                eText.classList.add('message__text');
                eMeta.append(eTime);
                eContainer.append(eText);

                if(msgAuthor?.id === '136')
                    eBase.style.transform = 'scaleY(' + (0.76 + (0.01 * Math.max(0, Math.ceil(Date.now() / (7 * 24 * 60 * 60000)) - 2813))).toString() + ')';
            }

            eBase.classList.add(`message--user-${msgAuthor?.id ?? '-1'}`);

            if(focusChannelName !== '' && msg.channel !== '' && msg.channel !== focusChannelName)
                eBase.classList.add('hidden');

            if(msg.id !== '') {
                eBase.id = elementId;
                eBase.dataset.id = msg.id;
            }
            if(msgAuthor)
                eBase.dataset.author = msgAuthor.id;
            if(msg.channel !== '')
                eBase.dataset.channel = msg.channel;
            if(isTiny)
                eBase.dataset.tiny = '1';
            eBase.dataset.created = msg.created.toISOString();

            eBase.dataset.body = msgText;
            eText.innerText = msgText;

            if(!skipTextParsing) {
                Umi.UI.Emoticons.Parse(eText, msg);
                Umi.Parsing.Parse(eText, msg);

                const textSplit = eText.innerText.split(' ');
                for(const textPart of textSplit) {
                    const uri = Umi.URI.Parse(textPart);

                    if(uri !== null && uri.Slashes !== null) {
                        const anchorElem = <a class="markup__link" href={textPart} target="_blank" rel="nofollow noreferrer noopener">{textPart}</a>;
                        eText.innerHTML = eText.innerHTML.replace(textPart.replace(/&/g, '&amp;'), anchorElem.outerHTML);
                    }
                }

                if(mami.settings.get('weeaboo')) {
                    eUser.appendChild($t(Weeaboo.getNameSuffix(msgAuthor)));
                    eText.appendChild($t(Weeaboo.getTextSuffix(msgAuthor)));

                    const kaomoji = Weeaboo.getRandomKaomoji(true, msg);
                    if(kaomoji)
                        eText.append(` ${kaomoji}`);
                }
            }

            const avatarUrl = msgAuthor?.avatar?.[isTiny ? 'x40' : 'x80'] ?? '';
            if(avatarUrl.length > 0)
                eAvatar.style.backgroundImage = `url('${avatarUrl}')`;
            else
                eAvatar.classList.add('message__avatar--disabled');

            const msgsList = $i('umi-messages');

            let insertAfter = msgsList.lastElementChild;
            if(insertAfter instanceof Element) {
                while(insertAfter.dataset.created > eBase.dataset.created) {
                    if(!insertAfter.previousElementSibling || !insertAfter.previousElementSibling.dataset.channel)
                        break;

                    insertAfter = insertAfter.previousElementSibling;
                }

                eBase.classList.toggle('message--first', shouldDisplayAuthorInfo(eBase, insertAfter));

                if(eBase.dataset.tiny !== insertAfter.dataset.tiny)
                    eBase.classList.add(isTiny ? 'message-tiny-fix' : 'message-big-fix');

                insertAfter.after(eBase);

                if(eBase.nextElementSibling instanceof Element)
                    eBase.nextElementSibling.classList.toggle('message--first', shouldDisplayAuthorInfo(eBase.nextElementSibling, eBase));
            } else {
                eBase.classList.add('message--first');
                if(isTiny) eBase.classList.add('message-tiny-fix');
                msgsList.append(eBase);
            }

            if(!eBase.classList.contains('hidden')) {
                if(mami.settings.get('autoEmbedV1')) {
                    const callEmbedOn = eBase.querySelectorAll('a[onclick^="Umi.Parser.SockChatBBcode.Embed"]');
                    for(const embedElem of callEmbedOn)
                        if(embedElem.dataset.embed !== '1')
                            embedElem.click();
                }

                if(mami.settings.get('autoScroll'))
                    eBase.scrollIntoView({ inline: 'end' });
            }

            let isMentioned = false;
            const mentionTriggers = mami.settings.get('notificationTriggers').toLowerCase().split(' ');
            const currentUser = Umi.User.getCurrentUser();
            if(typeof currentUser === 'object' && typeof currentUser.name === 'string')
                mentionTriggers.push(currentUser.name.toLowerCase());

            const mentionText = ` ${msgTextLong} `.toLowerCase();
            for(const trigger of mentionTriggers) {
                if(trigger.trim() === '')
                    continue;

                if(mentionText.includes(` ${trigger} `)) {
                    isMentioned = true;
                    break;
                }
            }

            if(!isMentioned && mami.settings.get('onlySoundOnMention'))
                soundName = undefined;

            if(document.hidden) {
                if(mami.settings.get('flashTitle')) {
                    let titleText = msgAuthor?.name ?? msgTextLong;
                    if(focusChannelName !== '' && focusChannelName !== msg.channel)
                        titleText += ` @ ${msg.channel}`;

                    title.strobe([
                        `[ @] ${titleText}`,
                        `[@ ] ${titleText}`,
                    ]);
                }

                if(!msg.silent) {
                    Umi.UI.Channels.Unread(msg.channel);

                    if(mami.settings.get('enableNotifications') && isMentioned) {
                        const options = {};

                        options.body = 'Click here to see what they said.';
                        if(mami.settings.get('notificationShowMessage'))
                            options.body += "\n" + msgTextLong;

                        if(avatarUrl.length > 0)
                            options.icon = avatarUrl;

                        const notif = new Notification(`${msgAuthor.name} mentioned you!`, options);
                        notif.addEventListener('click', () => {
                            window.focus();
                        });
                        document.addEventListener('visibilitychange', () => {
                            if(document.visibilityState === 'visible')
                                notif.close();
                        });
                    }
                }
            }

            if(!msg.silent && soundName !== undefined) {
                if(soundIsLegacy)
                    soundName = Umi.Sound.Convert(soundName);

                mami.sound.library.play(soundName, soundVolume, soundRate);
            }

            mami.globalEvents.dispatch('umi:ui:message_add', { element: eBase });
        },
        SwitchChannel: channel => {
            if(typeof channel === 'object' && channel !== null && 'name' in channel)
                channel = channel.name;
            if(typeof channel !== 'string')
                return;

            focusChannelName = channel;

            const root = $i('umi-messages');
            for(const elem of root.children)
                elem.classList.toggle('hidden', elem.dataset.channel !== undefined && elem.dataset.channel !== focusChannelName);
        },
        Clear: retain => {
            if(typeof retain === 'string' && !isNaN(retain))
                retain = parseInt(retain);
            if(typeof retain !== 'number')
                return;

            const root = $i('umi-messages');

            // remove messages
            if(root.childElementCount > retain)
                for(let i = root.childElementCount - 1; i >= 0; --i) {
                    const elem = root.children[i];

                    if(!elem.dataset.channel || elem.classList.contains('hidden') || --retain > 0)
                        continue;

                    $r(elem);
                }

            // fix author display
            for(const elem of root.children) {
                elem.classList.toggle('message--first', shouldDisplayAuthorInfo(elem, elem.previousElementSibling));
                lastAuthor = elem.dataset.author;
            }
        },
        Remove: function(msgId) {
            if(typeof msgId === 'object' && msgId !== null) {
                if('getId' in msgId)
                    msgId = msgId.getId();
                else if('id' in msgId)
                    msgId = msgId.id;
            }
            if(typeof msgId !== 'string')
                msgId = msgId.toString();

            if(msgId === '')
                return;

            const elem = $i(`message-${msgId}`);
            if(!(elem instanceof Element))
                return;

            // todo: take channel into account
            if(elem.nextElementSibling && elem.nextElementSibling.dataset.author === elem.dataset.author)
                elem.nextElementSibling.classList.add('message--first');

            $r(elem);
        },
    };
})();
