const Umi = { UI: {} };

#include animate.js
#include common.js
#include context.js
#include emotes.js
#include messages.js
#include mszauth.js
#include server.js
#include settings.js
#include utility.js
#include ui/chat-layout.js
#include ui/domaintrans.jsx
#include ui/elems.js
#include ui/hooks.js
#include ui/input-menus.js
#include ui/loading-overlay.jsx
#include ui/markup.js
#include ui/menus.js
#include ui/view.js
#include ui/settings.js
#include ui/title.js
#include ui/toggles.js
#include ui/uploads.js
#include audio/context.js
#include eeprom/eeprom.js

(async () => {
    const ctx = new MamiContext(document.body),
        views = ctx.getViews();

    Object.defineProperty(window, 'mami', {
        value: ctx,
        writable: false,
    });

    const sndLib = ctx.getSoundLibrary(),
        sndPacks = ctx.getSoundPacks();

    const lo = new Umi.UI.LoadingOverlay('spinner', 'Loading...');
    await views.push(lo);

    lo.setMessage('Loading environment...');
    try {
        window.futami = await FutamiCommon.load();
        localStorage.setItem('mami:common', JSON.stringify(window.futami));
    } catch(ex) {
        try {
            const cached = JSON.parse(localStorage.getItem('mami:common'));
            if(cached === null)
                throw 'Cached data is null.';
            window.futami = new FutamiCommon(cached);
        } catch(ex) {
            console.error(ex);
            lo.setIcon('cross');
            lo.setHeader('Failed!');
            lo.setMessage('Failed to load common settings!');
            return;
        }
    }


    lo.setMessage('Fetching credentials...');
    try {
        const auth = await MamiMisuzuAuth.update();
        if(!auth.ok)
            throw 'Authentication failed.';
    } catch(ex) {
        console.error(ex);
        location.assign(futami.get('login'));
        return;
    }

    setInterval(() => {
        MamiMisuzuAuth.update()
            .then(auth => {
                if(!auth.ok)
                    location.assign(futami.get('login'));
            })
    }, 600000);


    lo.setMessage('Getting element references...');

    // should be dynamic when possible
    const layout = new Umi.UI.ChatLayout;
    await views.unshift(layout);

    Umi.UI.Elements.Chat = layout.getElement();

    Umi.UI.Elements.Messages = $i('umi-messages');
    Umi.UI.Elements.Menus = $i('umi-menus');
    Umi.UI.Elements.Icons = $i('umi-menu-icons');
    Umi.UI.Elements.Toggles = $i('umi-toggles');
    Umi.UI.Elements.MessageContainer = $i('umi-msg-container');
    Umi.UI.Elements.MessageInput = $i('umi-msg-text');
    Umi.UI.Elements.MessageSend = $i('umi-msg-send');
    Umi.UI.Elements.MessageMenus = $i('umi-msg-menu');


    lo.setMessage('Loading sounds...');
    try {
        const sounds = await futami.getJson('sounds2');
        if(Array.isArray(sounds.library))
            sndLib.loadSounds(sounds.library, true);
        if(Array.isArray(sounds.packs))
            sndPacks.loadPacks(sounds.packs, true);
    } catch(ex) {
        console.error(ex);
    }


    lo.setMessage('Loading emoticons...');
    try {
        const emotes = await futami.getJson('emotes');
        MamiEmotes.loadLegacy(emotes);
    } catch(ex) {
        console.error(ex);
    }


    lo.setMessage('Loading settings...');

    Umi.Settings = new Umi.Settings(UmiSettings.settings);

    if(!await MamiDetectAutoPlay()) {
        Umi.Settings.set('soundEnable', false);
        Umi.Settings.virtualise('soundEnable');
    }

    const meta = UmiSettings.settings;
    for(const setting of UmiSettings.settings)
        if(setting.watcher)
            Umi.Settings.watch(setting.id, setting.watcher);


    if(!Umi.Settings.get('tmpSkipDomainPopUpThing'))
        await (() => {
            return new Promise((resolve) => {
                views.push(new MamiDomainTransition(() => {
                    for(const setting of UmiSettings.settings)
                        if(setting.id === 'settingsImport') {
                            setting.click();
                            break;
                        }
                }, () => {
                    Umi.Settings.set('tmpSkipDomainPopUpThing', true);
                    views.pop();
                    resolve();
                }));
            });
        })();


    const onHashChange = () => {
        if(location.hash === '#reset') {
            for(const setting of UmiSettings.settings)
                if(setting.emergencyReset)
                    Umi.Settings.remove(setting.id);

            location.assign('/');
        }
    };

    window.addEventListener('hashchange', onHashChange);
    onHashChange();

    window.addEventListener('keydown', ev => {
        if(ev.altKey && ev.shiftKey && (ev.key === 'R' || ev.key === 'r')) {
            Umi.Server.close();
            location.hash = 'reset';
        }
    });


    lo.setMessage('Preparing UI...');

    Umi.UI.Title.Set(futami.get('title'));
    Umi.UI.View.AccentReload();
    Umi.UI.Hooks.AddMessageHooks();
    Umi.UI.Hooks.AddUserHooks();
    Umi.UI.Hooks.AddChannelHooks();
    Umi.UI.Hooks.AddTextHooks();

    const mcPortalSnd = 'minecraft:nether:enter';
    if(Umi.Settings.get('minecraft') !== 'no' && ctx.hasSound() && sndLib.hasSound(mcPortalSnd))
        ctx.getSound().load(mcPortalSnd, sndLib.getSound(mcPortalSnd).getSources(), function(success, buffer) {
            if(success)
                buffer.createSource().play();
        });


    lo.setMessage('Loading EEPROM...');
    try {
        await MamiEEPROM.init();
        ctx.createEEPROM(
            futami.get('eeprom'),
            function() { return 'Misuzu ' + MamiMisuzuAuth.getAuthToken(); }
        );
    } catch(ex) {
        console.error(ex);
    }


    lo.setMessage('Building menus...');

    Umi.UI.Menus.Add('users', 'Users');
    Umi.UI.Menus.Add('channels', 'Channels', !Umi.Settings.get('showChannelList'));
    Umi.UI.Menus.Add('settings', 'Settings');

    let sidebarAnimation = null;

    Umi.UI.Settings.Init();
    Umi.UI.Toggles.Add('menu-toggle', {
        'click': function() {
            const sidebar = $c('sidebar')[0],
                toggle = Umi.UI.Toggles.Get('menu-toggle'),
                toggleOpened = 'sidebar__selector-mode--menu-toggle-opened',
                toggleClosed = 'sidebar__selector-mode--menu-toggle-closed',
                isClosed = toggle.classList.contains(toggleClosed);

            if(sidebarAnimation !== null) {
                sidebarAnimation.cancel();
                sidebarAnimation = null;
            }

            if(isClosed) {
                toggle.classList.add(toggleOpened);
                toggle.classList.remove(toggleClosed);
            } else {
                toggle.classList.add(toggleClosed);
                toggle.classList.remove(toggleOpened);
            }

            let update;
            if(isClosed)
                update = function(t) {
                    sidebar.style.width = (40 + (220 * t)).toString() + 'px';
                };
            else
                update = function(t) {
                    sidebar.style.width = (260 - (220 * t)).toString() + 'px';
                };

            sidebarAnimation = MamiAnimate({
                duration: 500,
                easing: 'easeOutExpo',
                update: update,
            });
        }
    }, 'Toggle Sidebar');

    Umi.UI.Toggles.Get('menu-toggle').classList.add('sidebar__selector-mode--menu-toggle-opened');

    Umi.UI.Toggles.Add('scroll', {
        'click': function() {
            Umi.Settings.toggle('autoScroll');
        }
    }, 'Autoscroll');
    Umi.Settings.watch('autoScroll', function(value) {
        Umi.UI.Toggles.Get('scroll').classList[value ? 'remove' : 'add']('sidebar__selector-mode--scroll-off');
    });

    if(window.innerWidth < 768)
        Umi.UI.Toggles.Get('menu-toggle').click();

    Umi.UI.Toggles.Add('audio', {
        'click': function() {
            Umi.Settings.toggle('soundEnable');
        }
    }, 'Sounds');
    Umi.Settings.watch('soundEnable', function(value) {
        Umi.UI.Toggles.Get('audio').classList[value ? 'remove' : 'add']('sidebar__selector-mode--audio-off');
    });

    Umi.UI.Toggles.Add('unembed', {
        'click': function() {
            const buttons = $qa('[data-embed="1"]');
            for(const button of buttons)
                button.click();
        }
    }, 'Unembed any embedded media');

    Umi.UI.Toggles.Add('clear', {
        'click': function() {
            if(confirm('ARE YOU SURE ABOUT THAT???')) {
                const limit = Umi.Settings.get('explosionRadius');
                const explode = $e({
                    tag: 'img',
                    attrs: {
                        src: '//static.flash.moe/images/explode.gif',
                        alt: '',
                        style: {
                            position: 'absolute',
                            zIndex: 9001,
                            bottom: 0,
                            right: 0,
                            pointerEvents: 'none',
                        },
                        onLoad: function() {
                            setTimeout(function(){
                                $r(explode);
                            }, 1700);

                            ctx.playLibrarySound('misc:explode');
                        },
                    },
                });

                document.body.appendChild(explode);

                let backLog = Umi.Messages.All();
                backLog = backLog.slice(Math.max(backLog.length - limit, 0));

                Umi.Messages.Clear();

                for(const blMsg of backLog)
                    Umi.Messages.Add(blMsg);
            }
        }
    }, 'Clear Logs');


    if(ctx.hasEEPROM()) {
        Umi.UI.Menus.Add('uploads', 'Upload History', !FUTAMI_DEBUG);

        const doUpload = function(file) {
            const uploadEntry = Umi.UI.Uploads.create(file.name),
                uploadTask = ctx.getEEPROM().createUpload(file);

            uploadTask.onProgress = function(progressInfo) {
                uploadEntry.setProgress(progressInfo.total, progressInfo.loaded);
            };

            uploadTask.onFailure = function(errorInfo) {
                if(!errorInfo.userAborted) {
                    let errorText = 'Was unable to upload file.';

                    switch(errorInfo.error) {
                        case EEPROM.ERR_INVALID:
                            errorText = 'Upload request was invalid.';
                            break;
                        case EEPROM.ERR_AUTH:
                            errorText = 'Upload authentication failed, refresh and try again.';
                            break;
                        case EEPROM.ERR_ACCESS:
                            errorText = 'You\'re not allowed to upload files.';
                            break;
                        case EEPROM.ERR_GONE:
                            errorText = 'Upload client has a configuration error or the server is gone.';
                            break;
                        case EEPROM.ERR_DMCA:
                            errorText = 'This file has been uploaded before and was removed for copyright reasons, you cannot upload this file.';
                            break;
                        case EEPROM.ERR_SERVER:
                            errorText = 'Upload server returned a critical error, try again later.';
                            break;
                        case EEPROM.ERR_SIZE:
                            if(errorInfo.maxSize < 1)
                                errorText = 'Selected file is too large.';
                            else {
                                const _t = ['bytes', 'KB', 'MB', 'GB', 'TB'],
                                    _i = parseInt(Math.floor(Math.log(errorInfo.maxSize) / Math.log(1024))),
                                    _s = Math.round(errorInfo.maxSize / Math.pow(1024, _i), 2);

                                errorText = 'Upload may not be larger than %1 %2.'.replace('%1', _s).replace('%2', _t[_i]);
                            }
                            break;
                    }

                    alert(errorText);
                }

                uploadEntry.remove();
            };

            uploadTask.onComplete = function(fileInfo) {
                uploadEntry.hideOptions();
                uploadEntry.clearOptions();
                uploadEntry.removeProgress();

                uploadEntry.addOption('Open', fileInfo.url);
                uploadEntry.addOption('Insert', function() {
                    Umi.UI.Markup.InsertRaw(insertText, '');
                });
                uploadEntry.addOption('Delete', function() {
                    ctx.getEEPROM().deleteUpload(fileInfo).start();
                    uploadEntry.remove();
                });

                let insertText = location.protocol + fileInfo.url;

                if(fileInfo.isImage()) {
                    insertText = '[img]' + fileInfo.url + '[/img]';
                    uploadEntry.setThumbnail(fileInfo.thumb);
                } else if(fileInfo.isAudio() || fileInfo.type === 'application/x-font-gdos') {
                    insertText = '[audio]' + fileInfo.url + '[/audio]';
                    uploadEntry.setThumbnail(fileInfo.thumb);
                } else if(fileInfo.isVideo()) {
                    insertText = '[video]' + fileInfo.url + '[/video]';
                    uploadEntry.setThumbnail(fileInfo.thumb);
                }


                if(Umi.Settings.get('eepromAutoInsert'))
                    Umi.UI.Markup.InsertRaw(insertText, '');
            };

            uploadEntry.addOption('Cancel', function() {
                uploadTask.abort();
            });

            uploadTask.start();
        };

        const uploadForm = $e({
            tag: 'input',
            attrs: {
                type: 'file',
                multiple: true,
                style: {
                    display: 'none',
                },
                onchange: function(ev) {
                    for(let i = 0; i < ev.target.files.length; ++i)
                        doUpload(ev.target.files[i]);
                },
            },
        });
        document.body.appendChild(uploadForm);

        Umi.UI.InputMenus.AddButton('upload', 'Upload', function() {
            uploadForm.click();
        });

        Umi.UI.Elements.MessageInput.onpaste = function(ev) {
            if(ev.clipboardData && ev.clipboardData.files.length > 0)
                for(const file of ev.clipboardData.files)
                    doUpload(file);
        };

        document.body.ondragenter = function(ev) {
            ev.preventDefault();
            ev.stopPropagation();
        };
        document.body.ondragover = function(ev) {
            ev.preventDefault();
            ev.stopPropagation();
        };
        document.body.ondragleave = function(ev) {
            ev.preventDefault();
            ev.stopPropagation();
        };
        document.body.ondrop = function(ev) {
            ev.preventDefault();
            ev.stopPropagation();
            if(ev.dataTransfer && ev.dataTransfer.files.length > 0)
                for(const file of ev.dataTransfer.files) {
                    if(file.name.slice(-5) === '.mami'
                        && confirm('This file appears to be a settings export. Do you want to import it?')) {
                        Umi.Settings.importFile(file);
                        return;
                    }

                    doUpload(file);
                }
        };
    }

    Umi.UI.InputMenus.Add('markup', 'BB Code');
    Umi.UI.InputMenus.Add('emotes', 'Emoticons');

    window.addEventListener('beforeunload', function(ev) {
        if(Umi.Settings.get('closeTabConfirm')) {
            ev.preventDefault();
            return ev.returnValue = 'Are you sure you want to close the tab?';
        }
    });


    lo.setMessage('Connecting...');
    Umi.Server.open(views);

    if(window.dispatchEvent)
        window.dispatchEvent(new Event('umi:connect'));
})();

#include compat.js
