import { MenuAction, Helper, Dialog, Translator, Fragment, AbstractSite, InitPromise } from './pwa-lib.js';

class ConditionAction extends MenuAction
{
    constructor(title, callback, condition, showFor, order) {
        super(title, callback, showFor, order);
        this.condition = condition;
    }

    getVisible() {
        return (super.getVisible() && this.condition(this));
    }
}

class Constants{}
Constants.SCRIPTS = {
    CKEDITOR:"version/2/ckeditor/ckeditor.js",
    LIST_JS: "version/1/listjs/list.min.js"
};

class DataManager {
    static async load(url, isCachable, raw) {
        isCachable = Helper.nonNull(isCachable, false);
        raw = Helper.nonNull(raw, false);
        let fullUrl = (isCachable) ? Helper.basePath(DataManager.cachePath + url) : Helper.basePath(DataManager.dataPath + url);

        return this._load(fullUrl, raw);
    }

    static async _load(url, raw) {
        return fetch(url, {"credentials": "same-origin"}).then(function (res) {
            if (raw) {
                return res.text();
            }
            return res.json();
        }).catch(function (e) {
            if (!raw) {
                return {
                    "success": false,
                    "errors": [
                        "not-online"
                    ]
                }
            }
        });
    }

    static async loadStatic(url, raw) {
        raw = Helper.nonNull(raw, false);
        let fullUrl = Helper.basePath(url);

        return this._load(fullUrl, raw);
    }

    static async send(url, params) {
        let fullUrl = Helper.basePath(DataManager.dataPath + url);

        if (!(params instanceof FormData)) {
            let newParams = new FormData();
            for (let k in params) {
                newParams.append(k, params[k]);
            }
            params = newParams;
        }

        return fetch(fullUrl, {
            "credentials": "same-origin",
            "method": "POST",
            "body": params
        }).then(function (res) {
            return res.json();
        }).catch(function (e) {
            console.error("error", e);
            return {
                "success": false,
                "errors": [
                    "not-online"
                ]
            }
        });
    }

    static buildQuery(values) {
        return Helper.buildQuery(values);
    }
}

DataManager.dataPath = "data/";
DataManager.cachePath = "cached/";

class Form {
    constructor(formElem, url, method, isCachable) {
        this.formElem = formElem;
        this.method = Helper.nonNull(method, Helper.nonNull(formElem["method"], "POST"));
        this.isCachable = (Helper.nonNull(isCachable, this.method.toLowerCase() === "get") === true);

        this.isBusy = false;

        if (typeof url === "string")
        {
            this.submitHandler = function(values){
                if (self.method.toLowerCase() === "get") {
                    return (DataManager.load(url + DataManager.buildQuery(values), self.isCachable));
                }
                else {
                    return (DataManager.send(url, values));
                }
            };
        }
        else {
            this.submitHandler = url;
        }

        let self = this;

        this.submitCallback = null;
        this.errorCallback = async function (errors) {
            await self.setErrors(errors);
        };

        formElem.addEventListener("submit", async function (e) {
            console.log("submitting!", e);
            e.preventDefault();
            await self.doSubmit(e);
        });

        for (let i = 0, n = formElem.elements.length; i < n; i++) {
            let elem = formElem.elements[i];
            elem.addEventListener("change", function () {
                if (this.value.trim() !== "") {
                    this.classList.add("notEmpty");
                }
                else {
                    this.classList.remove("notEmpty");
                }
                this.setCustomValidity("");
            });
            elem.addEventListener("keydown", function () {
                this.setCustomValidity("");
            });
        }
    }

    onError(errorHandler, ownHandlerForOptimisticLocking){
        ownHandlerForOptimisticLocking = Helper.nonNull(ownHandlerForOptimisticLocking, true);
        let callback = null;

        if (ownHandlerForOptimisticLocking){
            callback = function(errors){
                if (Array.isArray(errors) && errors.indexOf("optimistic-locking-exception") >= 0){
                    let dialog = new Dialog("optimistic-locking-dialog", "optimistic-locking-dialog-title");
                    dialog.addDefaultButton();
                    dialog.show();
                }
                else
                {
                    errorHandler(errors);
                }
            };
        }
        else
        {
            callback = errorHandler;
        }
        this.errorCallback = callback;
    }

    doSubmit() {
        if (!this.isBusy) {
            let self = this;
            return this.submit().then(function (res) {
                if (res["success"]) {
                    if (self.submitCallback !== null) {
                        return self.submitCallback(res["result"]);
                    }
                }
                else if (Helper.isNotNull(self.errorCallback)) {
                    return self.errorCallback(res["errors"]);
                }
            });
        }
    }

    load(url, isCached) {
        this.setValues(DataManager.load(url, isCached).then(function (values) {
            if (values["success"]) {
                return values["result"];
            }
            return {};
        }));
        return this;
    }

    setValues(valuePromise) {
        this.setIsBusy(true);

        let self = this;
        return Promise.resolve(valuePromise).then(function (values) {
            self.setIsBusy(false);
            for (let k in values) {
                if (Helper.isNotNull(self.formElem.elements[k])) {
                    if (Helper.isNotNull(self.formElem.elements[k].options) && Helper.isNotNull(values[k+"Options"]))
                    {
                        let options = self.formElem.elements[k].options;
                        for (let val in values[k+"Options"])
                        {
                            let option = document.createElement("option");
                            option.value = val;
                            option.innerText = values[k+"Options"][val];
                            options.add(option);
                        }
                    }

                    self.formElem.elements[k].value = Helper.htmlspecialcharsDecode(values[k]);
                    if (Helper.isNotNull(values[k]) && (""+values[k]).trim() !== "") {
                        self.formElem.elements[k].classList.add("notEmpty");
                    }
                    else {
                        self.formElem.elements[k].classList.remove("notEmpty");
                    }
                }
            }
            return self;
        });
    }

    async setErrors(errors) {
        let hasElem = false;
        let firstError = null;

        for (let k in errors) {
            if (Helper.isNotNull(this.formElem.elements[k]) && this.formElem.elements[k].type !== "hidden"
                && Helper.isNull(this.formElem.elements[k].readonly) && (
                    Helper.isNull(this.formElem.elements[k].disabled) || !this.formElem.elements[k].disabled)
            ) {
                this.formElem.elements[k].setCustomValidity(Translator.translate(Helper.nonNull(errors[k], "form-default-error")));
                hasElem = true;
            }
            if (Helper.isNull(firstError)) {
                firstError = Helper.nonNull(errors[k], "form-default-error");
            }
        }
        if (!hasElem && Helper.isNotNull(firstError)) {
            for (let k in this.formElem.elements) {
                if (this.formElem.elements[k].type !== "hidden") {
                    this.formElem.elements[k].setCustomValidity(Translator.translate(firstError));
                    hasElem = true;
                    break;
                }
            }
        }

        if (hasElem) {
            this.formElem.querySelector("input[type=submit]").click();
        }
    }

    setIsBusy(isBusy) {
        this.isBusy = isBusy;
        if (this.isBusy) {
            this.formElem.classList.add("sending");
        }
        else {
            this.formElem.classList.remove("sending");
        }
    }

    submit() {
        let self = this;
        return new Promise(function (resolve) {
            self.setIsBusy(true);
            let values = new FormData(self.formElem);
            resolve(self.submitHandler(values));
        }).then(function (data) {
            self.setIsBusy(false);
            return data;
        });
    }

    onSubmit(callback) {
        this.submitCallback = callback;
    }
}

class SettingsManager {
    static getInstance() {
        if (SettingsManager._instance === null) {
            SettingsManager._instance = new SettingsManager();
        }
        return SettingsManager._instance;
    }

    constructor() {
        this._settings = null;
        this._localStorageKey = "settings";
    }

    getSettings() {
        if (Helper.isNull(this._settings)) {
            this._loadSettings();
        }
        return this._settings;
    }

    getSetting(name, defaultValue) {
        const settings = this.getSettings();

        if (Helper.isNotNull(settings[name])) {
            return settings[name].value;
        }
        else {
            return defaultValue;
        }
    }

    deleteSetting(name) {
        this.getSettings();
        delete this._settings[name];
        this._saveSettings();
    }

    setSetting(name, value) {
        this.getSettings();
        this._settings[name] = {
            date: new Date().getTime(),
            value: value
        };
        this._saveSettings();
    }

    setSettings(settingsObject) {
        this.getSettings();
        for (const k in settingsObject) {
            this._settings[k] = settingsObject[k];
        }
        this._saveSettings();
    }

    hasSetting(name)
    {
        return Helper.nonNull(this._settings[name]);
    }

    _loadSettings() {
        this._settings = localStorage.getItem(this._localStorageKey);
        if (this._settings === null) {
            this._settings = {};
        }
        else {
            this._settings = JSON.parse(this._settings);
        }
    }

    _saveSettings() {
        if (this._settings !== null) {
            localStorage.setItem(this._localStorageKey, JSON.stringify(this._settings));
        }
    }
}

SettingsManager._instance = null;

class LocalStorageSettingsFragment extends Fragment {
    onFirstStart() {
        let res = super.onFirstStart();
        let settings = this.findBy(".setting", true);
        const settingsManager = SettingsManager.getInstance();

        for (let i = 0; i < settings.length; i++) {
            let setting = settings[i];
            const name = setting.name;
            let value;
            if (!setting["dataset"]["raw"]) {
                value = settingsManager.getSetting(name);
            } else {
                value = localStorage.getItem(name);
            }

            let isCheckable = false;
            if (setting instanceof HTMLInputElement && (setting.type === 'checkbox' || setting.type === 'radio')) {
                isCheckable = true;
            }
            if (((!setting["dataset"]["raw"] && !settingsManager.hasSetting(name)) || (setting["dataset"]["raw"] && value === null))
                && Helper.isNotNull(settings[i]["dataset"]["default"])) {
                value = setting["dataset"]["default"];
                if (Helper.isNotNull(setting["dataset"]["defaultTranslateable"])) {

                    setting["dataset"]["translation"] = "";
                    setting["dataset"]["translationValue"] = value;
                    value = Translator.translate(value);
                }
            }

            if (Helper.isNotNull(value)) {
                if (isCheckable) {
                    setting.checked = (value === setting.value);
                }
                else {
                    setting.value = value;
                }
                if (value !== "") {
                    setting.classList.add("notEmpty");
                }
            }

            setting.addEventListener("change", function () {
                let value = this.value;
                if (isCheckable && !this.checked) {
                    value = null;
                }
                if (!setting["dataset"]["raw"]) {
                    settingsManager.setSetting(name, value);
                } else {
                    localStorage.setItem(name, value);
                }
                delete setting["dataset"]["translationValue"];
                delete setting["dataset"]["translation"];
            });
        }
        return res;
    }

    onStart() {
        let res = super.onStart();
        let settings = this.findBy(".setting", true);
        const settingsManager = SettingsManager.getInstance();

        for (let i = 0; i < settings.length; i++) {
            let setting = settings[i];
            const name = setting.name;
            let value;
            if (!setting["dataset"]["raw"]) {
                value = settingsManager.getSetting(name);
            } else {
                value = localStorage.getItem(name);
            }

            let isCheckable = false;
            if (setting instanceof HTMLInputElement && (setting.type === 'checkbox' || setting.type === 'radio')) {
                isCheckable = true;
            }

            if (Helper.isNotNull(value)) {
                if (isCheckable) {
                    setting.checked = (value === setting.value);
                }
                else {
                    setting.value = value;
                }
                if (value !== "") {
                    setting.classList.add("notEmpty");
                }
            }
        }
        return res;
    }
}

class SmartColumn{
    constructor(name, label, translateable){
        this._name = name;
        this._label = label;
        this._translateable = Helper.nonNull(translateable, true);
        this._sortable = true;

        this._index = -1;

        this._clickListener = null;
    }

    setClickListener(listener)
    {
        this._clickListener = listener;
        return this;
    }

    setIndex(index)
    {
        this._index = index;
    }

    getName()
    {
        return this._name;
    }

    getLabel()
    {
        return this._label;
    }

    getHeadElement()
    {
        const headElement = document.createElement("th");
        headElement.appendChild((this._translateable)?Translator.makePersistentTranslation(this._label):document.createTextNode(this._label));

        if (this._sortable)
        {
            headElement.classList.add("sort");
            headElement["dataset"]["sort"] = this._name;
        }

        headElement["dataset"]["column"] = this._index;

        this._headElement = headElement;
        return this._headElement;
    }

    getValueName(){
        return this._name;
    }
    
    prepareData(myData, rowData)
    {
        return myData;
    }

    getItemElement(){
        const element = document.createElement("td");
        element.classList.add(this._name);
        element["dataset"]["column"] = this._index;

        if (Helper.isNotNull(this._clickListener))
        {
            element.classList.add("clickable");
        }

        return element;
    }

    click(tableCell, table, event){
        if (Helper.isNotNull(this._clickListener))
        {
            this._clickListener(tableCell, table, event);
        }
    }
}

class ImgSmartColumn extends SmartColumn{
    getValueName() {
        return {
            "name":this._name,
            "attr":"src"
        };
    }

    getItemElement() {
        const element = super.getItemElement();
        element.classList.remove(this._name);

        const imgElement = document.createElement("img");
        imgElement.classList.add(this._name);

        element.appendChild(imgElement);
        return element;
    }
}

class AliasImgSmartColumn extends ImgSmartColumn{
    constructor(name, label, translatable, aliases) {

        if (typeof translatable === 'object' && Helper.isNull(aliases))
        {
            aliases = translatable;
            translatable = null;
        }

        super(name, label, translatable);
        this.aliases = aliases;
        this.reverseAliases = Helper.invertKeyValues(aliases);
    }

    prepareData(myData)
    {
        if (Helper.isNotNull(this.aliases[myData]))
        {
            return this.aliases[myData];
        }
        return myData;
    }

    getAlias(value)
    {
        if (Helper.isNotNull(this.reverseAliases[value]))
        {
            return this.reverseAliases[value];
        }
        return null;
    }
}

class ConstSmartColumn extends SmartColumn{
    constructor(name, label, translatable, valueTranslatable) {
        super(name, label, translatable);
        this._sortable = false;
        this._valueTranslatable = Helper.nonNull(valueTranslatable, false);
    }

    getValueName() {
        return null;
    }

    getItemElement(){
        const element = super.getItemElement();
        element.classList.remove(this._name);
        element.appendChild((this._valueTranslatable)?Translator.makePersistentTranslation(this._name):document.createTextNode(this._name));
        return element;
    }
}

class DataSmartColumn extends SmartColumn{
    constructor(name, label, translateable) {
        translateable = Helper.nonNull(translateable, false);
        super(name, label, translateable);
    }

    getHeadElement() {
        return document.createTextNode("");
    }

    getValueName() {
        return {
            "data":[this._name]
        };
    }

    getItemElement() {
        return document.createTextNode("");
    }
}

class ImgConstSmartColumn extends ConstSmartColumn{
    constructor(name, label, translateable) {
        super(name, label, translateable);
        this._valueTranslatable = false;
    }

    getItemElement() {
        const element = super.getItemElement();
        const imgElement = document.createElement("img");
        imgElement["src"] = this._name;

        element.removeAllChildren().appendChild(imgElement);
        return element;
    }
}

class ListHelper {
    constructor(id, options, values) {
        this._tableElement = id;
        this._options = Helper.nonNull(options, {});
        this._values = values;

        if (typeof this._tableElement === "string") {
            this._tableElement = document.getElementById(this._tableElement);
        }

        this._columns = [];
        if (Array.isArray(options)) {
            this._columns = options;
        }
        else if (Helper.isNotNull(options["columns"])) {
            this._columns = options["columns"];
        }
    }

    prepareData(data) {
        console.log("prepareData", data);
        if (Helper.isNotNull(data)) {
            for (let i = 0, n = data.length; i < n; i++) {
                data[i] = this.prepareDataset(data[i]);
            }
        }
        return data;
    }

    prepareDataset(dataset) {
        console.log("prepareDataset", dataset);
        for (let i = 0, n = this._columns.length; i < n; i++) {
            if (Helper.isNotNull(dataset[this._columns[i].getName()])) {
                dataset[this._columns[i].getName()] = this._columns[i].prepareData(dataset[this._columns[i].getName()], dataset);
            }
        }
        return dataset;
    }

    createTable() {
        if (Helper.isNotNull(this._columns)) {
            this.updateColumns();
        }

        let id = this._tableElement;
        let options = this._options;
        let values = this._values;

        options["item"] = Helper.nonNull(options["item"], id["id"] + "-template-item");
        options["page"] = Helper.nonNull(options["page"], 5);
        options["pagination"] = Helper.nonNull(options["pagination"], {
            "outerWindow": 1,
            "innerWindow": 1
        });

        let template = document.getElementById(options["item"]);
        if (template) {
            options["item"] = template.outerHTML;
            template.remove();
        }

        values = this.prepareData(values);

        const list = new List(id, options, values);
        let self = this;
        id.querySelector("." + Helper.nonNull(options["listClass"], "list")).addEventListener("click", function (e) {
            let columnElem = e.target.closest("td[data-column]");
            const column = parseInt(columnElem["dataset"]["column"]);
            if (self._columns.length > column) {
                self._columns[column].click(columnElem, list, e);
            }
        });
        this.list = list;

        return list;
    }

    updateColumns() {
        const head = document.createElement("tr");
        const item = document.createElement("tr");
        const valueNames = [];

        for (let i = 0, n = this._columns.length; i < n; i++) {
            this._columns[i].setIndex(i);

            head.appendChild(this._columns[i].getHeadElement());
            item.appendChild(this._columns[i].getItemElement());

            const valueName = this._columns[i].getValueName();
            if (Helper.isNotNull(valueName)) {
                valueNames.push(valueName);
            }
        }
        const header = this._tableElement.querySelector("thead");
        const footer = this._tableElement.querySelector("tfoot");

        if (Helper.isNotNull(header)) {
            header.removeAllChildren().appendChild(head);
        }
        if (Helper.isNotNull(footer)) {
            footer.removeAllChildren().appendChild(Helper.cloneNode(head));
        }


        this._options["item"] = item.outerHTML;
        this._options["valueNames"] = valueNames;
    }

    getList() {
        return this.list;
    }

    updateItem(valueName, value, newValues) {
        const items = this.list.get(valueName, value);
        if (Helper.isNotNull(items) && items.length >= 1) {
            newValues = this.prepareDataset(newValues);
            items[0].values(newValues);
        }
    }

    setBusy(isBusy) {
        if (isBusy) {
            this._tableElement.classList.add("sending");
        }
        else {
            this._tableElement.classList.remove("sending");
        }
    }
}

class SettingsSite extends AbstractSite {
    constructor(siteManager) {
        super(siteManager, SettingsSite.template, "settings");
        for (let k in SettingsSite.settingsFragments) {
            this.addSettingsFragment(k, new SettingsSite.settingsFragments[k](this));
        }
        this.active = null;
    }

    addSettingsFragment(name, settingsFragment) {
        this.addFragment("#settings-fragments", settingsFragment);
        delete this.fragments["#settings-fragments"];
        this.fragments[name] = settingsFragment;
    }

    onStart() {
        let res = super.onStart();
        if (Helper.isNotNull(this.active) && !this.fragments[this.active].isActive()) {
            this.setActive(null);
        }

        this.buildList();
        return res;
    }

    setActive(name) {
        if (Helper.isNotNull(this.active)) {
            this.fragments[this.active].inflatePromise.then(function (view) {
                view.classList.remove("active");
            });
            this.findBy("#show-fragment-" + this.active).classList.remove("active");
        }
        this.active = name;
        if (Helper.isNotNull(this.active)) {
            this.fragments[this.active].inflatePromise.then(function (view) {
                view.classList.add("active");
            });
            this.findBy("#show-fragment-" + this.active).classList.add("active");
        }
    }

    buildList() {
        let listNameElem = this.findBy("#settings-fragment-list");
        listNameElem.removeAllChildren();

        let self = this;
        for (let k in this.fragments) {
            if (this.fragments[k].isActive()) {

                let liElement = document.createElement("li");
                liElement.id = "show-fragment-" + k;
                liElement.appendChild(Translator.makePersistentTranslation(k, null, "a"));
                liElement.addEventListener("click", function () {
                    self.setActive(k);
                });
                listNameElem.appendChild(liElement);

                if (Helper.isNull(this.active)) {
                    this.setActive(k);
                }
            }
        }
    }

    static addSettingsFragment(name, settingsFragment) {
        SettingsSite.settingsFragments[name] = settingsFragment;
    }

    static setAddSettingsSite(addLink) {
        SettingsSite.shouldAddSettingsSite = addLink;
    }

    static setTemplate(template) {
        SettingsSite.template = template;
    }
}

SettingsSite.template = 'core/html/settings.html';
SettingsSite.settingsFragments = {};
SettingsSite.shouldAddSettingsSite = true;
SettingsSite.settingsAction = null;
SettingsSite.shouldAddSettingsAction = true;

InitPromise.addPromise(function (app) {
    if (SettingsSite.shouldAddSettingsSite) {
        app.addDeepLink("settings", SettingsSite);

        if (Helper.isNull(SettingsSite.settingsAction)) {
            let settingsAction = new MenuAction("settings", async () => {
                let currentSite = app.getCurrentSite();
                if (currentSite instanceof SettingsSite) {
                    currentSite.finish();
                }
                else {
                    let settingsSite = await app.findSite((site) => {
                        return (site instanceof SettingsSite);
                    });
                    if (Helper.isNotNull(settingsSite)) {
                        settingsSite.toForeground();
                    }
                    else {
                        app.startSite(SettingsSite);
                    }
                }
            }, MenuAction.SHOW_FOR_LARGE, 10000);
            settingsAction.setIcon("img/settings.png");
            SettingsSite.settingsAction = settingsAction;
        }
        if (SettingsSite.shouldAddSettingsAction) {
            app.addDefaultAction(SettingsSite.settingsAction);
        }
    }
});

export { ConditionAction, Constants, DataManager, Form, LocalStorageSettingsFragment, SettingsManager, AliasImgSmartColumn, ConstSmartColumn, DataSmartColumn, ImgConstSmartColumn, ImgSmartColumn, ListHelper, SmartColumn, SettingsSite };
