/*******************************************************************************

    uBlock Origin Lite - a comprehensive, MV3-compliant content blocker
    Copyright (C) 2014-present Raymond Hill

    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 {http://www.gnu.org/licenses/}.

    Home: https://github.com/gorhill/uBlock
*/

import { browser, i18n, sendMessage } from './ext.js';
import { dom, qs$ } from './dom.js';
import { hashFromIterable } from './dashboard.js';
import { renderFilterLists } from './filter-lists.js';

/******************************************************************************/

let cachedRulesetData = {};

/******************************************************************************/

function renderAdminRules() {
    const { disabledFeatures: forbid = [] } = cachedRulesetData;
    if ( forbid.length === 0 ) { return; }
    dom.body.dataset.forbid = forbid.join(' ');
    if ( forbid.includes('dashboard') ) {
        dom.body.dataset.pane = 'about';
    }
}

/******************************************************************************/

function renderWidgets() {
    if ( cachedRulesetData.firstRun ) {
        dom.cl.add(dom.body, 'firstRun');
    }

    renderDefaultMode();

    qs$('#autoReload input[type="checkbox"]').checked = cachedRulesetData.autoReload;

    {
        const input = qs$('#showBlockedCount input[type="checkbox"]');
        if ( cachedRulesetData.canShowBlockedCount ) {
            input.checked = cachedRulesetData.showBlockedCount;
        } else {
            input.checked = false;
            dom.attr(input, 'disabled', '');
        }
    }

    {
        const input = qs$('#strictBlockMode input[type="checkbox"]');
        const canStrictBlock = cachedRulesetData.hasOmnipotence;
        input.checked = canStrictBlock && cachedRulesetData.strictBlockMode;
        dom.attr(input, 'disabled', canStrictBlock ? null : '');
    }

    {
        const input = qs$('#popupBlockMode input[type="checkbox"]');
        input.checked = cachedRulesetData.popupBlockMode;
    }

    {
        const state = Boolean(cachedRulesetData.developerMode) &&
            cachedRulesetData.disabledFeatures?.includes('develop') !== true;
        dom.body.dataset.develop = `${state}`;
        dom.prop('#developerMode input[type="checkbox"]', 'checked', state);
    }
}

/******************************************************************************/

function renderDefaultMode() {
    const defaultLevel = cachedRulesetData.defaultFilteringMode;
    if ( defaultLevel !== 0 ) {
        qs$(`.filteringModeCard input[type="radio"][value="${defaultLevel}"]`).checked = true;
    } else {
        dom.prop('.filteringModeCard input[type="radio"]', 'checked', false);
    }
}

/******************************************************************************/

async function onFilteringModeChange(ev) {
    const input = ev.target;
    const newLevel = parseInt(input.value, 10);

    switch ( newLevel ) {
    case 1: {
        const actualLevel = await sendMessage({
            what: 'setDefaultFilteringMode',
            level: newLevel,
        });
        cachedRulesetData.defaultFilteringMode = actualLevel;
        break;
    }
    case 2:
    case 3: {
        const granted = await browser.permissions.request({
            origins: [ '<all_urls>' ],
        });
        if ( granted ) {
            const actualLevel = await sendMessage({
                what: 'setDefaultFilteringMode',
                level: newLevel,
            });
            cachedRulesetData.defaultFilteringMode = actualLevel;
            cachedRulesetData.hasOmnipotence = true;
        }
        break;
    }
    default:
        break;
    }
    renderFilterLists(cachedRulesetData);
    renderWidgets();
}

dom.on(
    '#defaultFilteringMode',
    'change',
    '.filteringModeCard input[type="radio"]',
    ev => { onFilteringModeChange(ev); }
);

/******************************************************************************/

async function backupSettings() {
    const api = await import('./backup-restore.js');
    const data = await api.backupToObject(cachedRulesetData);
    if ( data instanceof Object === false ) { return; }
    const json = JSON.stringify(data, null, 2)  + '\n';
    const a = document.createElement('a');
    a.href = `data:text/plain;charset=utf-8,${encodeURIComponent(json)}`;
    dom.attr(a, 'download', 'my-ubol-settings.json');
    dom.attr(a, 'type', 'application/json');
    a.click();
}

async function restoreSettings() {
    const promise = new Promise(resolve => {
        const input = qs$('section[data-pane="settings"] input[type="file"]');
        input.onchange = ev => {
            dom.cl.add(dom.body, 'busy');
            input.onchange = null;
            const file = ev.target.files[0];
            if ( file === undefined || file.name === '' ) { return resolve(); }
            const fr = new FileReader();
            fr.onload = ( ) => {
                fr.onload = null;
                if ( typeof fr.result !== 'string' ) { return resolve(); }
                let data;
                try {
                    data = JSON.parse(fr.result);
                } catch {
                }
                if ( data instanceof Object === false ) { return resolve(); }
                import('./backup-restore.js').then(api => {
                    resolve(api.restoreFromObject(data));
                });
            };
            fr.readAsText(file);
        };
        input.oncancel = ( ) => {
            resolve();
        };
        // Reset to empty string, this will ensure a change event is properly
        // triggered if the user pick a file, even if it's the same as the last
        // one picked.
        input.value = '';
        input.click();
    });
    await promise;
    dom.cl.remove(dom.body, 'busy');
}

async function resetSettings() {
    const response = self.confirm(i18n.getMessage('resetToDefaultConfirm'));
    if ( response !== true ) { return; }
    dom.cl.add(dom.body, 'busy');
    const api = await import('./backup-restore.js');
    await api.restoreFromObject({});
    dom.cl.remove(dom.body, 'busy');
}

/******************************************************************************/

dom.on('#autoReload input[type="checkbox"]', 'change', ev => {
    sendMessage({
        what: 'setAutoReload',
        state: ev.target.checked,
    });
});

dom.on('#showBlockedCount input[type="checkbox"]', 'change', ev => {
    sendMessage({
        what: 'setShowBlockedCount',
        state: ev.target.checked,
    });
});

dom.on('#strictBlockMode input[type="checkbox"]', 'change', ev => {
    sendMessage({
        what: 'setStrictBlockMode',
        state: ev.target.checked,
    });
});

dom.on('#popupBlockMode input[type="checkbox"]', 'change', ev => {
    sendMessage({
        what: 'setPopupBlockMode',
        state: ev.target.checked,
    });
});

dom.on('#developerMode input[type="checkbox"]', 'change', ev => {
    const state = ev.target.checked;
    sendMessage({ what: 'setDeveloperMode', state });
    dom.body.dataset.develop = `${state}`;
});

dom.on('section[data-pane="settings"] [data-i18n="backupButton"]', 'click', ( ) => {
    backupSettings();
});

dom.on('section[data-pane="settings"] [data-i18n="restoreButton"]', 'click', ( ) => {
    restoreSettings();
});

dom.on('section[data-pane="settings"] [data-i18n="resetToDefaultButton"]', 'click', ( ) => {
    resetSettings();
});

/******************************************************************************/

function listen() {
    const bc = new self.BroadcastChannel('uBOL');
    bc.onmessage = listen.onmessage;
}

listen.onmessage = ev => {
    const message = ev.data;
    if ( message instanceof Object === false ) { return; }
    const local = cachedRulesetData;
    let render = false;

    if ( message.hasOmnipotence !== undefined ) {
        if ( message.hasOmnipotence !== local.hasOmnipotence ) {
            local.hasOmnipotence = message.hasOmnipotence;
            render = true;
        }
    }

    if ( message.defaultFilteringMode !== undefined ) {
        if ( message.defaultFilteringMode !== local.defaultFilteringMode ) {
            local.defaultFilteringMode = message.defaultFilteringMode;
            render = true;
        }
    }

    if ( message.autoReload !== undefined ) {
        if ( message.autoReload !== local.autoReload ) {
            local.autoReload = message.autoReload;
            render = true;
        }
    }

    if ( message.showBlockedCount !== undefined ) {
        if ( message.showBlockedCount !== local.showBlockedCount ) {
            local.showBlockedCount = message.showBlockedCount;
            render = true;
        }
    }

    if ( message.strictBlockMode !== undefined ) {
        if ( message.strictBlockMode !== local.strictBlockMode ) {
            local.strictBlockMode = message.strictBlockMode;
            render = true;
        }
    }

    if ( message.popupBlockMode !== undefined ) {
        if ( message.popupBlockMode !== local.popupBlockMode ) {
            local.popupBlockMode = message.popupBlockMode;
            render = true;
        }
    }

    if ( message.developerMode !== undefined ) {
        if ( message.developerMode !== local.developerMode ) {
            local.developerMode = message.developerMode;
            render = true;
        }
    }

    if ( message.adminRulesets !== undefined ) {
        if ( hashFromIterable(message.adminRulesets) !== hashFromIterable(local.adminRulesets) ) {
            local.adminRulesets = message.adminRulesets;
            render = true;
        }
    }

    if ( message.enabledRulesets !== undefined ) {
        local.enabledRulesets = message.enabledRulesets;
        render = true;
    }

    if ( render === false ) { return; }
    renderFilterLists(cachedRulesetData);
    renderWidgets();
};

/******************************************************************************/

sendMessage({
    what: 'getOptionsPageData',
}).then(data => {
    if ( !data ) { return; }
    cachedRulesetData = data;
    if ( data.supportsUserScripts ) {
        dom.body.dataset.supports = 'user-scripts';
    }
    try {
        renderAdminRules();
        renderFilterLists(cachedRulesetData);
        renderWidgets();
    } catch(reason) {
        console.error(reason);
    } finally {
        dom.cl.remove(dom.body, 'loading');
    }
    listen();
}).catch(reason => {
    console.error(reason);
});

/******************************************************************************/
