/*
 * 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/>.
 */


// Private

let _ankiDataPopulated = false;


function _ankiSpinnerShow(show) {
    const spinner = $('#anki-spinner');
    if (show) {
        spinner.show();
    } else {
        spinner.hide();
    }
}

function _ankiSetError(error) {
    const node = document.querySelector('#anki-error');
    const node2 = document.querySelector('#anki-invalid-response-error');
    if (error) {
        const errorString = `${error}`;
        if (node !== null) {
            node.hidden = false;
            node.textContent = errorString;
            _ankiSetErrorData(node, error);
        }

        if (node2 !== null) {
            node2.hidden = (errorString.indexOf('Invalid response') < 0);
        }
    } else {
        if (node !== null) {
            node.hidden = true;
            node.textContent = '';
        }

        if (node2 !== null) {
            node2.hidden = true;
        }
    }
}

function _ankiSetErrorData(node, error) {
    const data = error.data;
    let message = '';
    if (typeof data !== 'undefined') {
        message += `${JSON.stringify(data, null, 4)}\n\n`;
    }
    message += `${error.stack}`.trimRight();

    const button = document.createElement('a');
    button.className = 'error-data-show-button';

    const content = document.createElement('div');
    content.className = 'error-data-container';
    content.textContent = message;
    content.hidden = true;

    button.addEventListener('click', () => content.hidden = !content.hidden, false);

    node.appendChild(button);
    node.appendChild(content);
}

function _ankiSetDropdownOptions(dropdown, optionValues) {
    const fragment = document.createDocumentFragment();
    for (const optionValue of optionValues) {
        const option = document.createElement('option');
        option.value = optionValue;
        option.textContent = optionValue;
        fragment.appendChild(option);
    }
    dropdown.textContent = '';
    dropdown.appendChild(fragment);
}

async function _ankiDeckAndModelPopulate(options) {
    const termsDeck = {value: options.anki.terms.deck, selector: '#anki-terms-deck'};
    const kanjiDeck = {value: options.anki.kanji.deck, selector: '#anki-kanji-deck'};
    const termsModel = {value: options.anki.terms.model, selector: '#anki-terms-model'};
    const kanjiModel = {value: options.anki.kanji.model, selector: '#anki-kanji-model'};
    try {
        _ankiSpinnerShow(true);
        const [deckNames, modelNames] = await Promise.all([utilAnkiGetDeckNames(), utilAnkiGetModelNames()]);
        deckNames.sort();
        modelNames.sort();
        termsDeck.values = deckNames;
        kanjiDeck.values = deckNames;
        termsModel.values = modelNames;
        kanjiModel.values = modelNames;
        _ankiSetError(null);
    } catch (error) {
        _ankiSetError(error);
    } finally {
        _ankiSpinnerShow(false);
    }

    for (const {value, values, selector} of [termsDeck, kanjiDeck, termsModel, kanjiModel]) {
        const node = document.querySelector(selector);
        _ankiSetDropdownOptions(node, Array.isArray(values) ? values : [value]);
        node.value = value;
    }
}

function _ankiCreateFieldTemplate(name, value, markers) {
    const template = document.querySelector('#anki-field-template').content;
    const content = document.importNode(template, true).firstChild;

    content.querySelector('.anki-field-name').textContent = name;

    const field = content.querySelector('.anki-field-value');
    field.dataset.field = name;
    field.value = value;

    content.querySelector('.anki-field-marker-list').appendChild(ankiGetFieldMarkersHtml(markers));

    return content;
}

async function _ankiFieldsPopulate(tabId, options) {
    const tab = document.querySelector(`.tab-pane[data-anki-card-type=${tabId}]`);
    const container = tab.querySelector('tbody');
    const markers = ankiGetFieldMarkers(tabId);

    const fragment = document.createDocumentFragment();
    const fields = options.anki[tabId].fields;
    for (const name of Object.keys(fields)) {
        const value = fields[name];
        const html = _ankiCreateFieldTemplate(name, value, markers);
        fragment.appendChild(html);
    }

    container.textContent = '';
    container.appendChild(fragment);

    for (const node of container.querySelectorAll('.anki-field-value')) {
        node.addEventListener('change', (e) => onFormOptionsChanged(e), false);
    }
    for (const node of container.querySelectorAll('.marker-link')) {
        node.addEventListener('click', (e) => _onAnkiMarkerClicked(e), false);
    }
}

function _onAnkiMarkerClicked(e) {
    e.preventDefault();
    const link = e.currentTarget;
    const input = $(link).closest('.input-group').find('.anki-field-value')[0];
    input.value = `{${link.textContent}}`;
    input.dispatchEvent(new Event('change'));
}

async function _onAnkiModelChanged(e) {
    const node = e.currentTarget;
    let fieldNames;
    try {
        const modelName = node.value;
        fieldNames = await utilAnkiGetModelFieldNames(modelName);
        _ankiSetError(null);
    } catch (error) {
        _ankiSetError(error);
        return;
    } finally {
        _ankiSpinnerShow(false);
    }

    const tabId = node.dataset.ankiCardType;
    if (tabId !== 'terms' && tabId !== 'kanji') { return; }

    const fields = {};
    for (const name of fieldNames) {
        fields[name] = '';
    }

    const optionsContext = getOptionsContext();
    const options = await getOptionsMutable(optionsContext);
    options.anki[tabId].fields = utilBackgroundIsolate(fields);
    await settingsSaveOptions();

    await _ankiFieldsPopulate(tabId, options);
}


// Public

function ankiErrorShown() {
    const node = document.querySelector('#anki-error');
    return node && !node.hidden;
}

function ankiFieldsToDict(elements) {
    const result = {};
    for (const element of elements) {
        result[element.dataset.field] = element.value;
    }
    return result;
}


function ankiGetFieldMarkersHtml(markers) {
    const template = document.querySelector('#anki-field-marker-template').content;
    const fragment = document.createDocumentFragment();
    for (const marker of markers) {
        const markerNode = document.importNode(template, true).firstChild;
        markerNode.querySelector('.marker-link').textContent = marker;
        fragment.appendChild(markerNode);
    }
    return fragment;
}

function ankiGetFieldMarkers(type) {
    switch (type) {
        case 'terms':
            return [
                'audio',
                'cloze-body',
                'cloze-prefix',
                'cloze-suffix',
                'dictionary',
                'expression',
                'furigana',
                'furigana-plain',
                'glossary',
                'glossary-brief',
                'reading',
                'screenshot',
                'sentence',
                'tags',
                'url'
            ];
        case 'kanji':
            return [
                'character',
                'dictionary',
                'glossary',
                'kunyomi',
                'onyomi',
                'screenshot',
                'sentence',
                'tags',
                'url'
            ];
        default:
            return [];
    }
}


function ankiInitialize() {
    for (const node of document.querySelectorAll('#anki-terms-model,#anki-kanji-model')) {
        node.addEventListener('change', (e) => _onAnkiModelChanged(e), false);
    }
}

async function onAnkiOptionsChanged(options) {
    if (!options.anki.enable) {
        _ankiDataPopulated = false;
        return;
    }

    if (_ankiDataPopulated) { return; }

    await _ankiDeckAndModelPopulate(options);
    _ankiDataPopulated = true;
    await Promise.all([_ankiFieldsPopulate('terms', options), _ankiFieldsPopulate('kanji', options)]);
}