<template>
    <div @mouseover="onMouseOver" id="page" style="position: relative; padding: 10px;">
        <div v-html="styling"></div>
        <div v-if="!characters" style="display:flex; align-items:center; justify-content:center; height: 100%;">
            <div class="well well-lg" style="width: 400px;">
                <h3 style="margin-top:0">{{l('title')}}</h3>
                <div class="alert alert-danger" v-show="error">
                    {{error}}
                </div>
                <div class="form-group">
                    <label class="control-label" for="account">{{l('login.account')}}</label>
                    <input class="form-control" id="account" v-model="account" @keypress.enter="login"/>
                </div>
                <div class="form-group">
                    <label class="control-label" for="password">{{l('login.password')}}</label>
                    <input class="form-control" type="password" id="password" v-model="password" @keypress.enter="login"/>
                </div>
                <div class="form-group" v-show="showAdvanced">
                    <label class="control-label" for="host">{{l('login.host')}}</label>
                    <input class="form-control" id="host" v-model="host" @keypress.enter="login"/>
                </div>
                <div class="form-group">
                    <label for="advanced"><input type="checkbox" id="advanced" v-model="showAdvanced"/> {{l('login.advanced')}}</label>
                </div>
                <div class="form-group">
                    <label for="save"><input type="checkbox" id="save" v-model="saveLogin"/> {{l('login.save')}}</label>
                </div>
                <div class="form-group" style="margin:0">
                    <button class="btn btn-primary" @click="login" :disabled="loggingIn">
                        {{l(loggingIn ? 'login.working' : 'login.submit')}}
                    </button>
                </div>
            </div>
        </div>
        <chat v-else :ownCharacters="characters" :defaultCharacter="defaultCharacter" ref="chat"></chat>
        <div ref="linkPreview" class="link-preview"></div>
        <modal :action="l('importer.importing')" ref="importModal" :buttons="false">
            {{l('importer.importingNote')}}
            <div class="progress" style="margin-top:5px">
                <div class="progress-bar" :style="{width: importProgress * 100 + '%'}"></div>
            </div>
        </modal>
        <modal :buttons="false" ref="profileViewer" dialogClass="profile-viewer">
            <character-page :authenticated="false" :hideGroups="true" :name="profileName" :image-preview="true"></character-page>
            <template slot="title">{{profileName}} <a class="btn fa fa-external-link" @click="openProfileInBrowser"></a></template>
        </modal>
    </div>
</template>

<script lang="ts">
    import Axios from 'axios';
    import * as electron from 'electron';
    import * as fs from 'fs';
    import * as path from 'path';
    import * as qs from 'querystring';
    import * as Raven from 'raven-js';
    import {promisify} from 'util';
    import Vue, {ComponentOptions} from 'vue';
    import Component from 'vue-class-component';
    import Chat from '../chat/Chat.vue';
    import {Settings} from '../chat/common';
    import core, {init as initCore} from '../chat/core';
    import l from '../chat/localize';
    import {init as profileApiInit} from '../chat/profile_api';
    import Socket from '../chat/WebSocket';
    import Modal from '../components/Modal.vue';
    import Connection from '../fchat/connection';
    import CharacterPage from '../site/character_page/character_page.vue';
    import {nativeRequire} from './common';
    import {GeneralSettings, getGeneralSettings, Logs, setGeneralSettings, SettingsStore} from './filesystem';
    import * as SlimcatImporter from './importer';
    import {createAppMenu, createContextMenu} from './menu';
    import Notifications from './notifications';
    import * as spellchecker from './spellchecker';

    const webContents = electron.remote.getCurrentWebContents();
    webContents.on('context-menu', (_, props) => {
        const menuTemplate = createContextMenu(<Electron.ContextMenuParams & {editFlags: {[key: string]: boolean}}>props);
        if(props.misspelledWord !== '') {
            const corrections = spellchecker.getCorrections(props.misspelledWord);
            if(corrections.length > 0) {
                menuTemplate.unshift({type: 'separator'});
                menuTemplate.unshift(...corrections.map((correction: string) => ({
                    label: correction,
                    click: () => webContents.replaceMisspelling(correction)
                })));
            }
        }

        if(menuTemplate.length > 0) electron.remote.Menu.buildFromTemplate(menuTemplate).popup();
    });

    const defaultTrayMenu = [
        {label: l('action.open'), click: () => mainWindow!.show()},
        {
            label: l('action.quit'),
            click: () => {
                isClosing = true;
                mainWindow!.close();
                mainWindow = undefined;
                electron.remote.app.quit();
            }
        }
    ];
    let trayMenu = electron.remote.Menu.buildFromTemplate(defaultTrayMenu);

    let isClosing = false;
    let mainWindow: Electron.BrowserWindow | undefined = electron.remote.getCurrentWindow();
    //tslint:disable-next-line:no-require-imports
    const tray = new electron.remote.Tray(path.join(__dirname, <string>require('./build/tray.png')));
    tray.setToolTip(l('title'));
    tray.on('click', (_) => mainWindow!.show());
    tray.setContextMenu(trayMenu);

    /*tslint:disable:no-any*///because this is hacky
    const keyStore = nativeRequire<{
        getPassword(account: string): Promise<string>
        setPassword(account: string, password: string): Promise<void>
        deletePassword(account: string): Promise<void>
        [key: string]: (...args: any[]) => Promise<any>
    }>('keytar/build/Release/keytar.node');
    for(const key in keyStore) keyStore[key] = promisify(<(...args: any[]) => any>keyStore[key].bind(keyStore, 'fchat'));
    //tslint:enable

    profileApiInit();

    @Component({
        components: {chat: Chat, modal: Modal, characterPage: CharacterPage}
    })
    export default class Index extends Vue {
        //tslint:disable:no-null-keyword
        showAdvanced = false;
        saveLogin = false;
        loggingIn = false;
        account: string;
        password = '';
        host: string;
        characters: string[] | null = null;
        error = '';
        defaultCharacter: string | null = null;
        settings = new SettingsStore();
        l = l;
        currentSettings: GeneralSettings;
        isConnected = false;
        importProgress = 0;
        profileName = '';

        constructor(options?: ComponentOptions<Index>) {
            super(options);
            let settings = getGeneralSettings();
            if(settings === undefined) {
                try {
                    if(SlimcatImporter.canImportGeneral() && confirm(l('importer.importGeneral')))
                        settings = SlimcatImporter.importGeneral();
                } catch {
                    alert(l('importer.error'));
                }
                settings = settings !== undefined ? settings : new GeneralSettings();
            }
            this.account = settings.account;
            this.host = settings.host;
            this.currentSettings = settings;
        }

        created(): void {
            if(this.currentSettings.account.length > 0) {
                keyStore.getPassword(this.currentSettings.account)
                    .then((value: string) => this.password = value, (err: Error) => this.error = err.message);
                this.saveLogin = true;
            }
            window.onbeforeunload = () => {
                if(process.env.NODE_ENV !== 'production' || isClosing || !this.isConnected) {
                    tray.destroy();
                    return;
                }
                if(!this.currentSettings.closeToTray)
                    return setImmediate(() => {
                        if(confirm(l('chat.confirmLeave'))) {
                            isClosing = true;
                            mainWindow!.close();
                        }
                    });
                mainWindow!.hide();
                return false;
            };

            const appMenu = createAppMenu();
            const themes = fs.readdirSync(path.join(__dirname, 'themes')).filter((x) => x.substr(-4) === '.css').map((x) => x.slice(0, -4));
            const setTheme = (theme: string) => {
                this.currentSettings.theme = theme;
                setGeneralSettings(this.currentSettings);
            };
            const spellcheckerMenu = new electron.remote.Menu();
            //tslint:disable-next-line:no-floating-promises
            this.addSpellcheckerItems(spellcheckerMenu);
            appMenu[0].submenu = [
                {
                    label: l('settings.closeToTray'), type: 'checkbox', checked: this.currentSettings.closeToTray,
                    click: (item: Electron.MenuItem) => {
                        this.currentSettings.closeToTray = item.checked;
                        setGeneralSettings(this.currentSettings);
                    }
                }, {
                    label: l('settings.profileViewer'), type: 'checkbox', checked: this.currentSettings.profileViewer,
                    click: (item: Electron.MenuItem) => {
                        this.currentSettings.profileViewer = item.checked;
                        setGeneralSettings(this.currentSettings);
                    }
                },
                {label: l('settings.spellcheck'), submenu: spellcheckerMenu},
                {
                    label: l('settings.theme'),
                    submenu: themes.map((x) => ({
                        checked: this.currentSettings.theme === x,
                        click: () => setTheme(x),
                        label: x,
                        type: <'radio'>'radio'
                    }))
                },
                {type: 'separator'},
                {role: 'minimize'},
                {
                    accelerator: process.platform === 'darwin' ? 'Cmd+Q' : undefined,
                    label: l('action.quit'),
                    click(): void {
                        isClosing = true;
                        mainWindow!.close();
                    }
                }
            ];
            electron.remote.Menu.setApplicationMenu(electron.remote.Menu.buildFromTemplate(appMenu));

            let hasUpdate = false;
            electron.ipcRenderer.on('updater-status', (_: Event, status: string) => {
                if(status !== 'update-downloaded' || hasUpdate) return;
                hasUpdate = true;
                const menu = electron.remote.Menu.getApplicationMenu();
                menu.append(new electron.remote.MenuItem({
                    label: l('action.updateAvailable'),
                    submenu: electron.remote.Menu.buildFromTemplate([{
                        label: l('action.update'),
                        click: () => {
                            if(!this.isConnected || confirm(l('chat.confirmLeave'))) {
                                isClosing = true;
                                electron.ipcRenderer.send('install-update');
                            }
                        }
                    }, {
                        label: l('help.changelog'),
                        click: () => electron.shell.openExternal('https://wiki.f-list.net/F-Chat_3.0#Changelog')
                    }])
                }));
                electron.remote.Menu.setApplicationMenu(menu);
            });
            electron.ipcRenderer.on('open-profile', (_: Event, name: string) => {
                if(this.currentSettings.profileViewer) {
                    const profileViewer = <Modal>this.$refs['profileViewer'];
                    this.profileName = name;
                    profileViewer.show();
                } else electron.remote.shell.openExternal(`https://www.f-list.net/c/${name}`);
            });
        }

        async addSpellcheckerItems(menu: Electron.Menu): Promise<void> {
            const dictionaries = await spellchecker.getAvailableDictionaries();
            const selected = this.currentSettings.spellcheckLang;
            menu.append(new electron.remote.MenuItem({
                type: 'radio',
                label: l('settings.spellcheck.disabled'),
                click: this.setSpellcheck.bind(this, undefined)
            }));
            for(const lang of dictionaries)
                menu.append(new electron.remote.MenuItem({
                    type: 'radio',
                    label: lang,
                    checked: lang === selected,
                    click: this.setSpellcheck.bind(this, lang)
                }));
            electron.webFrame.setSpellCheckProvider('', false, {spellCheck: spellchecker.check});
            await spellchecker.setDictionary(selected);
        }

        async setSpellcheck(lang: string | undefined): Promise<void> {
            this.currentSettings.spellcheckLang = lang;
            setGeneralSettings(this.currentSettings);
            await spellchecker.setDictionary(lang);
        }

        async login(): Promise<void> {
            if(this.loggingIn) return;
            this.loggingIn = true;
            try {
                if(!this.saveLogin) await keyStore.deletePassword(this.account);
                const data = <{ticket?: string, error: string, characters: string[], default_character: string}>
                    (await Axios.post('https://www.f-list.net/json/getApiTicket.php',
                        qs.stringify({account: this.account, password: this.password, no_friends: true, no_bookmarks: true}))).data;
                if(data.error !== '') {
                    this.error = data.error;
                    return;
                }
                if(this.saveLogin) {
                    this.currentSettings.account = this.account;
                    await keyStore.setPassword(this.account, this.password);
                    this.currentSettings.host = this.host;
                    setGeneralSettings(this.currentSettings);
                }
                Socket.host = this.host;
                const connection = new Connection(Socket, this.account, this.password);
                connection.onEvent('connecting', async() => {
                    if((await this.settings.get('settings')) === undefined && SlimcatImporter.canImportCharacter(core.connection.character)) {
                        if(!confirm(l('importer.importGeneral'))) return this.settings.set('settings', new Settings());
                        (<Modal>this.$refs['importModal']).show(true);
                        await SlimcatImporter.importCharacter(core.connection.character, (progress) => this.importProgress = progress);
                        (<Modal>this.$refs['importModal']).hide();
                    }
                });
                connection.onEvent('connected', () => {
                    this.isConnected = true;
                    tray.setToolTip(document.title = `FChat 3.0 - ${core.connection.character}`);
                    Raven.setUserContext({username: core.connection.character});
                    trayMenu.insert(0, new electron.remote.MenuItem({label: core.connection.character, enabled: false}));
                    trayMenu.insert(1, new electron.remote.MenuItem({type: 'separator'}));
                    tray.setContextMenu(trayMenu);
                });
                connection.onEvent('closed', () => {
                    this.isConnected = false;
                    tray.setToolTip(document.title = 'FChat 3.0');
                    Raven.setUserContext();
                    tray.setContextMenu(trayMenu = electron.remote.Menu.buildFromTemplate(defaultTrayMenu));
                });
                initCore(connection, Logs, SettingsStore, Notifications);
                this.characters = data.characters.sort();
                this.defaultCharacter = data.default_character;
            } catch(e) {
                this.error = l('login.error');
                if(process.env.NODE_ENV !== 'production') throw e;
            } finally {
                this.loggingIn = false;
            }
        }

        onMouseOver(e: MouseEvent): void {
            const preview = (<HTMLDivElement>this.$refs.linkPreview);
            if((<HTMLElement>e.target).tagName === 'A') {
                const target = <HTMLAnchorElement>e.target;
                if(target.hostname !== '') {
                    //tslint:disable-next-line:prefer-template
                    preview.className = 'link-preview ' +
                        (e.clientX < window.innerWidth / 2 && e.clientY > window.innerHeight - 150 ? ' right' : '');
                    preview.textContent = target.href;
                    preview.style.display = 'block';
                    return;
                }
            }
            preview.textContent = '';
            preview.style.display = 'none';
        }

        openProfileInBrowser(): void {
            electron.remote.shell.openExternal(`https://www.f-list.net/c/${this.profileName}`);
        }

        get styling(): string {
            try {
                return `<style>${fs.readFileSync(path.join(__dirname, `themes/${this.currentSettings.theme}.css`))}</style>`;
            } catch(e) {
                if((<Error & {code: string}>e).code === 'ENOENT' && this.currentSettings.theme !== 'default') {
                    this.currentSettings.theme = 'default';
                    return this.styling;
                }
                throw e;
            }
        }
    }
</script>

<style>
    html, body, #page {
        height: 100%;
    }
</style>