/*
 * Copyright (C) 2019-2020  Alex Yatskov <alex@foosoft.net>
 * Author: Alex Yatskov <alex@foosoft.net>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

/*global apiOptionsGet, Popup, PopupProxyHost, Frontend, TextSourceRange*/

class SettingsPopupPreview {
    constructor() {
        this.frontend = null;
        this.apiOptionsGetOld = apiOptionsGet;
        this.popup = null;
        this.popupSetCustomOuterCssOld = null;
        this.popupShown = false;
        this.themeChangeTimeout = null;
        this.textSource = null;
        this._targetOrigin = chrome.runtime.getURL('/').replace(/\/$/, '');
    }

    static create() {
        const instance = new SettingsPopupPreview();
        instance.prepare();
        return instance;
    }

    async prepare() {
        // Setup events
        window.addEventListener('message', (e) => this.onMessage(e), false);

        const themeDarkCheckbox = document.querySelector('#theme-dark-checkbox');
        if (themeDarkCheckbox !== null) {
            themeDarkCheckbox.addEventListener('change', () => this.onThemeDarkCheckboxChanged(themeDarkCheckbox), false);
        }

        // Overwrite API functions
        window.apiOptionsGet = (...args) => this.apiOptionsGet(...args);

        // Overwrite frontend
        const popupHost = new PopupProxyHost();
        await popupHost.prepare();

        this.popup = popupHost.getOrCreatePopup();
        this.popup.setChildrenSupported(false);

        this.popupSetCustomOuterCssOld = this.popup.setCustomOuterCss;
        this.popup.setCustomOuterCss = (...args) => this.popupSetCustomOuterCss(...args);

        this.frontend = new Frontend(this.popup);

        this.frontend.setEnabled = () => {};
        this.frontend.searchClear = () => {};

        await this.frontend.prepare();

        // Update search
        this.updateSearch();
    }

    async apiOptionsGet(...args) {
        const options = await this.apiOptionsGetOld(...args);
        options.general.enable = true;
        options.general.debugInfo = false;
        options.general.popupWidth = 400;
        options.general.popupHeight = 250;
        options.general.popupHorizontalOffset = 0;
        options.general.popupVerticalOffset = 10;
        options.general.popupHorizontalOffset2 = 10;
        options.general.popupVerticalOffset2 = 0;
        options.general.popupHorizontalTextPosition = 'below';
        options.general.popupVerticalTextPosition = 'before';
        options.scanning.selectText = false;
        return options;
    }

    async popupSetCustomOuterCss(...args) {
        // This simulates the stylesheet priorities when injecting using the web extension API.
        const result = await this.popupSetCustomOuterCssOld.call(this.popup, ...args);

        const node = document.querySelector('#client-css');
        if (node !== null && result !== null) {
            node.parentNode.insertBefore(result, node);
        }

        return result;
    }

    onMessage(e) {
        if (e.origin !== this._targetOrigin) { return; }

        const {action, params} = e.data;
        const handler = SettingsPopupPreview._messageHandlers.get(action);
        if (typeof handler !== 'function') { return; }

        handler(this, params);
    }

    onThemeDarkCheckboxChanged(node) {
        document.documentElement.classList.toggle('dark', node.checked);
        if (this.themeChangeTimeout !== null) {
            clearTimeout(this.themeChangeTimeout);
        }
        this.themeChangeTimeout = setTimeout(() => {
            this.themeChangeTimeout = null;
            this.frontend.popup.updateTheme();
        }, 300);
    }

    setText(text) {
        const exampleText = document.querySelector('#example-text');
        if (exampleText === null) { return; }

        exampleText.textContent = text;
        this.updateSearch();
    }

    setInfoVisible(visible) {
        const node = document.querySelector('.placeholder-info');
        if (node === null) { return; }

        node.classList.toggle('placeholder-info-visible', visible);
    }

    setCustomCss(css) {
        if (this.frontend === null) { return; }
        this.frontend.popup.setCustomCss(css);
    }

    setCustomOuterCss(css) {
        if (this.frontend === null) { return; }
        this.frontend.popup.setCustomOuterCss(css, false);
    }

    async updateSearch() {
        const exampleText = document.querySelector('#example-text');
        if (exampleText === null) { return; }

        const textNode = exampleText.firstChild;
        if (textNode === null) { return; }

        const range = document.createRange();
        range.selectNode(textNode);
        const source = new TextSourceRange(range, range.toString(), null, null);

        try {
            await this.frontend.onSearchSource(source, 'script');
            this.frontend.setCurrentTextSource(source);
        } finally {
            source.cleanup();
        }
        this.textSource = source;
        await this.frontend.showContentCompleted();

        if (this.frontend.popup.isVisibleSync()) {
            this.popupShown = true;
        }

        this.setInfoVisible(!this.popupShown);
    }
}

SettingsPopupPreview._messageHandlers = new Map([
    ['setText', (self, {text}) => self.setText(text)],
    ['setCustomCss', (self, {css}) => self.setCustomCss(css)],
    ['setCustomOuterCss', (self, {css}) => self.setCustomOuterCss(css)]
]);

SettingsPopupPreview.instance = SettingsPopupPreview.create();