/*
    This script is MUTATED and EXECUTED after DOM has loaded
    It is wrapped into a `(() => {})();` to prevent it from polluting its surroundings.

    Avoid using array functions, such as `arr.forEach`, as some websites override them with incompatible functions

    Do not remove the `SETTINGS_START` and `SETTINGS_END` markers below,
    they are used for dynamically injecting settings from Electron.
*/

const sizePairs = [
    ['naturalWidth', 'naturalHeight'],
    ['videoWidth', 'videoHeight'],
    ['width', 'height'],
];


class FListImagePreviewDomMutator {
    constructor(settings) {
        /* ## SETTINGS_START ## */
        this.settings = settings || {
            selectors: ['video', 'img'],
            debug: true,
            skipElementRemove: false,
            safeTags: [],
            injectStyle: false
        };
        /* ## SETTINGS_END ## */

        this.startTime = Date.now();

        this.selectors = this.settings.selectors;
        this.skipElementRemove = this.settings.skipElementRemove;
        this.safeTags = this.settings.safeTags;

        this.body = document.querySelector('body');
        this.html = document.querySelector('html');

        this.ipcRenderer = {
            sendToHost: ((window) && (window.rising) && (window.rising.sendToHost))
                ? window.rising.sendToHost
                : (...args) => (this.debug('MOCK.ipc.sendToHost', ...args))
        };

        this.preprocess();

        this.img = this.detectImage(this.selectors, this.body);
        this.wrapper = this.createWrapperElement();
        this.style = this.createStyleElement();
    }


    preprocess() {
        for (const el of document.querySelectorAll('header, .header, nav, .nav, .navbar, .navigation')) {
            try {
                el.remove();
            } catch (err) {
                this.error('preprocess', err);
            }
        }
    }


    detectImage(selectors, body) {
        let selected = [];

        for (const selector of selectors) {
            const selectedElements = (Array.from(document.querySelectorAll(selector)).filter((i) => ((i.width !== 1) && (i.height !== 1))));
            selected = selected.concat(selectedElements);
        }

        this.debug('detectImage.selected', selectors, selected);

        const img = selected.filter(el => (el !== body)).shift();

        this.debug('detectImage.found', !!img, img);

        return img;
    }


    run() {
        if (!this.img) {
            return;
        }

        this.updateImgSize(this.img, 'pre');

        this.attachImgToWrapper(this.img, this.wrapper);
        this.attachWrapperToBody(this.wrapper, this.body);
        this.attachStyleToWrapper(this.style, this.wrapper);

        this.forceElementStyling(this.html, this.body, this.wrapper, this.img);

        this.resolveVideoSrc(this.img);

        this.setEventListener('DOMContentLoaded', this.img);
        this.setEventListener('load', this.img);
        this.setEventListener('loadstart', this.img);

        this.attemptPlay(this.img, true);

        this.updateImgSizeTimer(this.img);

        this.cleanDom(this.body);
    }


    cleanDom(body) {
        if (this.skipElementRemove) {
            return;
        }

        const removeList = [];
        const safeIds = ['flistWrapper', 'flistError', 'flistHider', 'flistStyle'];
        const safeTags = this.safeTags;

        for (const el of body.childNodes) {
            try {
                if (
                    (safeIds.indexOf(el.id) < 0)
                    && ((!el.tagName) || (safeTags.indexOf(el.tagName.toLowerCase())) < 0)
                ) {
                    removeList.push(el);
                }
            } catch (err) {
                this.error('cleanDom find nodes', err);
            }
        }

        for (const el of removeList) {
            try {
                el.remove();
            } catch (err) {
                this.error('cleanDom remove element', err);
            }
        }
    }


    updateImgSizeTimer(img) {
        const result = this.updateImgSize(img, 'timer');

        if (!result) {
            setTimeout(() => this.updateImgSizeTimer(img), 100);
        }
    }


    resolveVideoSrc(img) {
        if ((img.src) || (!img.tagName) || ((img.tagName) && (img.tagName.toUpperCase() !== 'VIDEO'))) {
            return;
        }

        this.debug('resolveVideoSrc', 'Needs a content URL', img);

        const contentUrls = document.querySelectorAll('meta[itemprop="contentURL"]');

        if ((contentUrls) && (contentUrls.length > 0)) {
            this.debug('Found content URLs', contentUrls);

            const cu = contentUrls[0];

            if ((cu.attributes) && (cu.attributes.content) && (cu.attributes.content.value)) {
                this.debug('Content URL', cu.attributes.content.value);

                img.src = cu.attributes.content.value;
            }
        }
    }


    setEventListener(eventName, img) {
        document.addEventListener(eventName, (event) => {
            this.debug('event', eventName, event);

            this.updateImgSize(img, `event.${eventName}`);
            this.attemptPlay(img, false);
        });
    }


    attemptPlay(img, lessStrict) {
        try {
            if (
                (img.play)
                && (
                    (lessStrict)
                    || ((!lessStrict) && (!img.paused) && (!img.ended) && (!(img.currentTime > 0)))
                )
            )
            {
                img.muted = true;
                img.loop = true;
                img.play();
            }
        } catch (err) {
            this.error('attemptPlay', err, img, lessStrict);
        }
    }


    forceElementStyling(html, body, wrapper, img) {
        try {
            body.class = '';
            img.class = '';
            wrapper.class = '';
            html.class = '';

            body.removeAttribute('class');
            img.removeAttribute('class');
            wrapper.removeAttribute('class');
            html.removeAttribute('class');

            img.removeAttribute('width');
            img.removeAttribute('height');
        } catch (err) {
            this.error('forceElementStyling remove class', err);
        }

        html.style = this.getWrapperStyleOverrides();
        body.style = this.getWrapperStyleOverrides();
        img.style = this.getImageStyleOverrides();
    }


    attachWrapperToBody(wrapper, body) {
        body.append(wrapper);
    }


    attachStyleToWrapper(style, wrapper) {
        try {
            wrapper.append(style);
        } catch (err) {
            this.error('attach style', err);
        }
    }


    attachImgToWrapper(img, wrapper) {
        try {
            img.remove();
        } catch(err) {
            this.error('attachImgToWrapper', 'remove()', err);

            try {
                img.parentNode.removeChild(img);
            } catch(err2) {
                console.error('attachImgToWrapper', 'removeChild()', err2);
            }
        }

        wrapper.append(img);
    }


    createWrapperElement() {
        const el = document.createElement('div');
        el.id = 'flistWrapper';

        el.style = this.getWrapperStyleOverrides()
            + 'z-index: 100000 !important;'
            + 'background-color: black !important;'
            + 'background-size: contain !important;'
            + 'background-repeat: no-repeat !important;'
            + 'background-position: top left !important;';

        return el;
    }


    createStyleElement() {
        if (!!this.settings.skipInjectStyle) {
            return document.createElement('i');
        }

        const el = document.createElement('style');

        el.id = 'flistStyle';

        el.textContent = `
            html {
                ${this.getWrapperStyleOverrides()}
            }
            
            body {
                ${this.getWrapperStyleOverrides()}
            }
        
            #flistWrapper img, #flistWrapper video {
                ${this.getImageStyleOverrides()}
            }
        `;

        return el;
    }


    resolveImgSize(img) {
        const solved = {};

        for (let ri = 0; ri < sizePairs.length; ri++) {
            const val = sizePairs[ri];

            if ((img[val[0]]) && (img[val[1]])) {
                solved.width = img[val[0]];
                solved.height = img[val[1]];
                break;
            }
        }

        return solved;
    }


    updateImgSize(img, stage) {
        const imSize = this.resolveImgSize(img);

        if ((imSize.width) && (imSize.height)) {
            this.debug('IPC webview.img', imSize, stage);

            this.ipcRenderer.sendToHost('webview.img', imSize.width, imSize.height, stage);
            return true;
        }

        return false;
    }


    getBasicStyleOverrides() {
        return 'border: 0 !important;'
            + 'padding: 0 !important;'
            + 'margin: 0 !important;'
            + 'width: 100% !important;'
            + 'height: 100% !important;'
            + 'opacity: 1 !important;'
            + 'min-width: initial !important;'
            + 'min-height: initial !important;'
            + 'max-width: initial !important;'
            + 'max-height: initial !important;'
            + 'display: block !important;'
            + 'visibility: visible !important;';
    }


    getWrapperStyleOverrides() {
        return this.getBasicStyleOverrides()
            + 'overflow: hidden !important;'
            + 'top: 0 !important;'
            + 'left: 0 !important;'
            + 'position: absolute !important;';
    }


    getImageStyleOverrides() {
        return this.getBasicStyleOverrides()
            + 'object-position: top left !important;'
            + 'object-fit: contain !important;';
    }


    debug(...args) {
        if (this.settings.debug) {
            console.log('DOM Mutator:', ...args, `${(Date.now() - this.startTime)/1000}s`);
        }
    }

    error(...args) {
        console.error('DOM Mutator:', ...args, `${(Date.now() - this.startTime)/1000}s`);
    }
}

/* ## EXECUTION_START ## */
const flistImagePreviewMutator = new FListImagePreviewDomMutator();
flistImagePreviewMutator.run();
/* ## EXECUTION_END ## */