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

class DelayPromise extends Promise {
    static async delay(delay) {
        return new Promise((resolve) => {
            setTimeout(resolve, delay);
        });
    }
}

class Matomo {

    static init() {
        Matomo.isTrackingPromise = new Promise(async (resolve) => {
            let shouldTrack = localStorage.getItem(Matomo.LOCAL_STORAGE_KEY);
                console.log(shouldTrack,Helper.isNull(shouldTrack));
            if (Helper.isNull(shouldTrack)) {
                shouldTrack = await Matomo._askIsTracking();
                localStorage.setItem(Matomo.LOCAL_STORAGE_KEY, shouldTrack);
            }
            else {
                console.log(shouldTrack, (shouldTrack === "1"));
                shouldTrack = (shouldTrack === "1");
                Matomo.setTrack(shouldTrack);
            }
            resolve(shouldTrack);
        });
        Matomo.isTrackingPromise.then(() => {
            Matomo.push(['trackPageView']);
            Matomo.push(['enableLinkTracking']);
            Matomo.push(['setTrackerUrl', Matomo.TRACK_SITE + '/piwik.php']);
            Matomo.push(['setSiteId', '1']);

            let d = document, g = d.createElement('script'), s = d.getElementsByTagName('head')[0];
            g.type = 'text/javascript';
            g.async = true;
            g.defer = true;
            g.src = Matomo.TRACK_SITE + '/piwik.js';
            s.appendChild(g);
        });
    }

    static async _askIsTracking() {
        console.log(Matomo.TRACK_SITE + Matomo.BASE_PATH + "isTracked");
        Matomo.isTrackingPromise = Matomo.query("isTracked")
            .then(xml => {
                let textContent = xml.firstChild.textContent;
                // localStorage.setItem(Matomo.LOCAL_STORAGE_KEY, textContent);
                return (textContent === "1")
            });
        return Matomo.isTrackingPromise;
    }

    static async query(method) {
        return fetch(Matomo.TRACK_SITE + Matomo.BASE_PATH + method, {
            // "method":"POST",
            "mode": "cors",
            "credentials": "include",
            // "headers": {
                // 'Access-Control-Allow-Origin': '*',
                // 'Access-Control-Allow-Credentials': 'true',
            // },
        }).then(res => res.text()).then(text => (new window.DOMParser()).parseFromString(text, "text/xml"));
    }

    static getTrackingPromise() {
        return Matomo.isTrackingPromise;
    }

    static async setTrack(shouldTrack) {
        Matomo.isTrackingPromise = Promise.resolve(shouldTrack);
        localStorage.setItem(Matomo.LOCAL_STORAGE_KEY, (shouldTrack === true) ? "1" : "0");
        return await Matomo.query((shouldTrack) ? "doTrack" : "doIgnore");
    }

    static async push(arr) {
        if (await Matomo.getTrackingPromise()) {
            window["_paq"].push(arr);
        }
    }
}

Matomo.LOCAL_STORAGE_KEY = "matomoShouldTrack";
Matomo.TRACK_SITE = "//matomo.silas.link";
Matomo.BASE_PATH = "/index.php?module=API&method=AjaxOptOut.";

InitPromise.addPromise(() => {
    window["_paq"] = window["_paq"] || [];

    Matomo.init();
});

class RotateHelper {
    rotate(element, degrees){
        let rotateText = element.innerText;
        element.removeAllChildren();

        let partDegree = degrees/rotateText.length;
        for(let i = 0; i < rotateText.length; i++){
            let child = document.createElement("span");
            child.innerText = rotateText.charAt(i);
            child.style.transform ="rotate("+(partDegree*i)+"deg)";
            child.classList.add("rotated");
            element.appendChild(child);
        }
    }
}

class ScaleHelper {
    async scaleTo(scale, fontElement, container, ignoreHeight, ignoreWidth, margin, fontWeight, animationDelay, addListener) {

        addListener = Helper.nonNull(addListener, true);
        animationDelay = Helper.nonNull(animationDelay, 0);

        let newFontSize = await this._getNewFontSize(scale, fontElement, container, ignoreHeight, ignoreWidth, margin, fontWeight, animationDelay === 0);

        if (animationDelay > 0) {
            await new Promise(r => {
                setTimeout(r, animationDelay);
                fontElement.style.fontSize = newFontSize + "px";
            });
        }

        let self = this;
        let listener = function () {
            return new Promise(resolve => {
                let timeout = (typeof addListener === 'number') ? addListener : 255;
                setTimeout(() => {
                    resolve(self.scaleTo(scale, fontElement, container, ignoreHeight, ignoreWidth, margin, fontWeight, animationDelay, false));
                }, timeout);
            });
        };
        if (addListener !== false) {
            window.addEventListener("resize", listener);
        }
        return listener;
    }

    async scaleToFull(fontElement, container, ignoreHeight, ignoreWidth, margin, fontWeight, animDelay, addListener) {
        return this.scaleTo(1, fontElement, container, ignoreHeight, ignoreWidth, margin, fontWeight, animDelay, addListener);
    }

    async _getNewFontSize(scale, fontElement, container, ignoreHeight, ignoreWidth, margin, fontWeight, setFontSize) {
        margin = Helper.nonNull(margin, 10);
        ignoreHeight = Helper.nonNull(ignoreHeight, false);
        ignoreWidth = Helper.nonNull(ignoreWidth, false);
        fontWeight = Helper.nonNull(fontWeight, fontElement.innerHTML.length);
        setFontSize = Helper.nonNull(setFontSize, true);

        let hasNoTransitionClass = container.classList.contains("no-transition");

        if (!hasNoTransitionClass) {
            container.classList.add("no-transition");
        }

        let beforeFontSize = fontElement.style.fontSize;
        let currentFontSize = 1;
        let diff = 0;
        let widthDiff = 0;
        let heightDiff = 0;
        let containerWidth = 0;
        let containerHeight = 0;
        do {
            currentFontSize += diff / (fontWeight + 1);
            fontElement.style.fontSize = currentFontSize + 'px';

            let containerStyle = window.getComputedStyle(container);

            containerWidth = containerStyle.getPropertyValue("width").replace('px', '');
            containerHeight = containerStyle.getPropertyValue("height").replace('px', '');

            widthDiff = containerWidth - fontElement.offsetWidth;
            heightDiff = containerHeight - fontElement.offsetHeight;

            let newDiff = (ignoreWidth ? heightDiff : (ignoreHeight ? widthDiff : Math.min(widthDiff, heightDiff)));
            if (newDiff === diff) {
                break;
            }
            diff = newDiff;
        } while ((widthDiff > (1 - scale) * containerWidth || ignoreWidth) && (heightDiff > (1 - scale) * containerHeight || ignoreHeight));

        currentFontSize -= margin;
        fontElement.style.fontSize = ((setFontSize) ? currentFontSize + "px" : beforeFontSize);

        if (!hasNoTransitionClass) {
            await new Promise((r) => {
                setTimeout(r, 50);
            });
            container.classList.remove("no-transition");
        }

        return currentFontSize;
    }
}

class SoundManager {
    static getInstance() {
        if (Helper.isNull(SoundManager.instance)) {
            SoundManager.instance = new SoundManager();
        }
        return SoundManager.instance;
    }

    constructor() {
        this.channels = {};
        this.context = new AudioContext();
    }

    set(options, channel) {
        channel = Helper.nonNull(channel, SoundManager.CHANNELS.DEFAULT);
        let audioObject = Helper.nonNull(this.channels[channel], {});

        if (typeof options === "string") {
            options = {audio: options};
        }

        let audio = options.audio;
        if (Helper.isNotNull(audio)) {
            audioObject.loadedPromise = fetch(audio).then(res => res.arrayBuffer()).then(arrayBuffer => this.context.decodeAudioData(arrayBuffer));
            this.stop(channel);
        }
        audioObject.muted = Helper.nonNull(options.muted, audioObject.muted, false);
        audioObject.volume = Helper.nonNull(options.volume, audioObject.volume, 1);
        audioObject.loop = Helper.nonNull(options.loop, audioObject.loop, false);
        audioObject.timeOffset = Helper.nonNull(options.timeOffset, audioObject.timeOffset, 0);
        this.channels[channel] = audioObject;

        if (audioObject.muted){
            this.stop(channel);
        }

        return this.channels[channel];
    }

    async play(channel, audioOrOptions) {
        channel = Helper.nonNull(channel, SoundManager.CHANNELS.DEFAULT);
        if (Helper.isNull(audioOrOptions)) {
            audioOrOptions = {};
        } else if (!(typeof audioOrOptions === "object")) {
            audioOrOptions = {
                audio: audioOrOptions
            };
        }
        audioOrOptions.timeOffset = Helper.nonNull(audioOrOptions.timeOffset, 0);

        this.stop(channel);
        this.set(audioOrOptions, channel);

        if (!this.channels[channel].muted) {
            let buffer = await this.channels[channel].loadedPromise;
            let source = this.context.createBufferSource();
            let gain = this.context.createGain();

            source.buffer = buffer;
            source.loop = this.channels[channel].loop;
            gain.gain.value = this.channels[channel].volume;

            //to prevent gap in mp3-files
            source.loopStart = 0.3;
            source.loopEnd = buffer.duration-0.3;

            source.connect(gain);
            gain.connect(this.context.destination);
            source.start(0);

            this.channels[channel].source = source;
        }
        return this.channels[channel];
    }

    stop(channel) {
        channel = Helper.nonNull(channel, SoundManager.CHANNELS.DEFAULT);

        let oldAudio = this.channels[channel];
        if (Helper.nonNull(oldAudio) && Helper.nonNull(oldAudio.source)) {
            oldAudio.source.stop();
        }
    }
    get(channel) {
        channel = Helper.nonNull(channel, SoundManager.CHANNELS.DEFAULT);
        return this.channels[channel];
    }
}

SoundManager.CHANNELS = {
    MUSIC: "music",
    SOUND: "sound",
    DEFAULT: "default"
};

class TabbedFragment extends Fragment {
    constructor(site) {
        super(site, 'pwaAssets/html/fragment/tabbedFragment.html');
        this.fragments = {};
        this.tabHeadingTemplate = null;
        this.tabHeadingContainer = null;
        this.tabContainer = null;
        this.tabKeys = [];
        this.currentTabIndex = 0;
    }

    onFirstStart() {
        super.onFirstStart();
        this.tabContainer = this.findBy(".tab-container");
        this.showTab(0);
    }

    addFragment(name, fragment, translationArgs, isTranslatable) {
        isTranslatable = Helper.nonNull(isTranslatable, true);
        if (translationArgs === false) {
            isTranslatable = false;
        }

        this.fragments[name] = fragment;
        const tabIndex = this.tabKeys.length;
        this.tabKeys.push(name);

        const _self = this;
        this.inflatePromise = this.inflatePromise.then(function (siteContent) {
            if (Helper.isNull(_self.tabHeadingTemplate)) {
                _self.tabHeadingTemplate = siteContent.querySelector(".tab-header-template");
            }
            const newTabHeader = Helper.cloneNode(_self.tabHeadingTemplate);
            newTabHeader.classList.add("tab-" + tabIndex);
            newTabHeader.querySelector(".tab-name").appendChild((isTranslatable) ? Translator.makePersistentTranslation(name, translationArgs) : document.createTextNode(name));
            newTabHeader.addEventListener("click", function(){
                _self.showTab(tabIndex);
            });

            if (Helper.isNull(_self.tabHeadingContainer)) {
                _self.tabHeadingContainer = siteContent.querySelector(".tab-header-container");
            }
            _self.tabHeadingContainer.appendChild(newTabHeader);
            return siteContent;
        });
    }

    showTab(index) {
        if (index >= 0 && index < this.tabKeys.length) {
            this.findBy(".tab-" + this.currentTabIndex).classList.remove("active");
            this.findBy(".tab-" + index).classList.add("active");
            this.tabContainer.removeAllChildren().appendChild(Helper.createLoadingSymbol());

            this.currentTabIndex = index;

            const _self = this;
            this.fragments[this.tabKeys[index]].inflatePromise.then(tabView => {
                if (_self.currentTabIndex === index) {
                    _self.tabContainer.removeAllChildren().appendChild(tabView);
                }
            });
        }
    }
}

export { DelayPromise, Matomo, RotateHelper, ScaleHelper, SoundManager, TabbedFragment };
