import injectSheetJss, {createTheming} from 'react-jss';

import theme from '../style/theme.json';

//
// Setup Theming and Initial JSS Theming Parts

const theming = createTheming('__FLOOD_ADMIN__');

const {withTheme, ThemeProvider} = theming;

/**
 * Provides injectSheet with current theming as context.
 *
 * Can be used as static or dynamic, with listening to theme changes - whereas withTheme is purely dynamic.
 *
 * static: wrapping a component on definition writes the style one time
 * dynamic - render: wrap a component in `render` changes the already created <style> but will also triggers re-rendering and can not be used in e.g. input (as they rely on onchange throughput without re-rendering) todo: find correct namings / refine text
 * dynamic - theme: wrap a component in injectSheet and that with withTheme also listens on theme changing
 *
 * @param styles
 * @return function
 */
const injectSheet = (styles) => {
    return injectSheetJss(styles, {theming})
};

const magnificationSteps = [
    '16px',
    '18px',
    '21px',
    '24px',
];

//
// Theme Switching Logic

const store_item = 'theme_switch__choosen';

// fetch user-choosen preset
let store_theme = localStorage.getItem(store_item);

let initialTheme = theme.initial;

// only apply user-choosen preset when existing in defined transitions
if(store_theme && theme.transition[store_theme]) {
    initialTheme = store_theme;
}

/**
 * Define transitions of themes, key is the `active` and value the `next` theme key. This defines all available themes and from which theme to what theme it should be switched
 *
 * @example
 * You have a theme { dark: {}, light: {}}, the theme switcher has `dark` active, when user want to switch the theme should go from `dark` to `light` and back to `dark` when `light` is active.
 *
 * {
 *   // 'active': 'next'
 *   'dark': 'light',
 *   'light': 'dark',
 * }
 *
 * @type {{}}
 */
const themeTransition = theme.transition;

// to have one instance everywhere
let cached_val = {
    switchExec: null
};

/**
 * The global theme got the themeprovider in BaseApp, the state of it controls the applied theme, to be able to change the theme from where ever, `provide` the state setting function and add `exec` on e.g. a button onClick
 * @type {{provide: themeSwitcher.provide, exec: themeSwitcher.exec}}
 */
const themeSwitcher = {
    provide: (handler) => {
        cached_val.switchExec = handler;
    },
    exec: (evt) => {
        if('function' === typeof cached_val.switchExec) {
            cached_val.switchExec();
        }
    }
};

/**
 * Merge `theme.json` Dynamic Theme Styles with default Style, be careful: shallow merge!
 *
 * @param name
 * @return {{}|undefined}
 */
const styleMerge = (name) => {
    if(theme.style['_'] && theme.style[name]) {
        return Object.assign(theme.style['_'], theme.style[name]);
    } else {
        throw new Error('BaseApp: styleMerge not possible for theme: ' + name);
    }
};

/**
 * Handles and Calculates a Style Change Request
 *
 * @param name
 * @return {*}
 */
const changeStyle = (name) => {
    if(themeTransition[name]) {

        // updating localstorage for persistent theme on reload
        localStorage.setItem(store_item, themeTransition[name]);

        // return the next theme name and styles for that theme
        return {
            theme: themeTransition[name],
            style: styleMerge(themeTransition[name])
        };
    } else {
        console.error('ThemeSwitch: wanted theme transition not defined with active: ' + name);
        return false;
    }
};

const initialStyle = styleMerge(initialTheme);

//
// Magnification Steps

const magnificationConsumer = {};

const addMagnificationConsumer = (name, evt) => {
    magnificationConsumer[name] = evt;
};

const rmMagnificationConsumer = (name) => {
    delete magnificationConsumer[name];
};

const triggerMagnificationConsumer = (lvl) => {
    for(let name in magnificationConsumer) {
        if(magnificationConsumer.hasOwnProperty(name)) {
            setTimeout(() => {
                magnificationConsumer[name](lvl);
            }, 0);
        }
    }
};

const initMagnification = () => {
    let fontSize = window.localStorage.getItem('fontSize');
    if(null !== fontSize && '' !== fontSize.trim()) {
        let node = document.querySelector('html');
        node.style.fontSize = fontSize;
    }
    triggerMagnificationConsumer(getMagnificationLvl(fontSize));
};

const getMagnificationLvl = (nodeFontSize) => {
    let fontSize = '16px';
    if(nodeFontSize) {
        fontSize = nodeFontSize;
    }

    const currentLvl = magnificationSteps.indexOf(fontSize);

    if(-1 === currentLvl) {
        return 0;
    }

    return currentLvl;
};

const switchMagnification = () => {
    let node = document.querySelector('html');

    let nextLvl = getMagnificationLvl(node.style.fontSize);
    
    if(nextLvl < (magnificationSteps.length - 1)) {
        nextLvl++;
    } else {
        nextLvl = 0;
    }
    
    node.style.fontSize = magnificationSteps[nextLvl];
    window.localStorage.setItem('fontSize', magnificationSteps[nextLvl]);

    triggerMagnificationConsumer(nextLvl);

    return nextLvl;
};

//
// Utility

/**
 * Combine multiple style objects (which should be used as props), using `primary` as the first one and shallow merging `second` and `more`, if set, into it returning the combined value or only `primary`
 *
 * @param primary
 * @param second
 * @param more
 */
const combineStyle = (primary, second, ...more) => {
    let style = primary;
    if(second) {
        style = Object.assign(primary, second, ...more);
    }
    return style;
};

//
// Exports

// Themed HOC functions from JSS
export {withTheme, injectSheet, ThemeProvider};

// initial values for themeProvider and switching logic
export {initialStyle, initialTheme};

export {combineStyle};

// theme switching binding and handling
export {themeSwitcher, changeStyle};

//
export {initMagnification, switchMagnification, getMagnificationLvl, addMagnificationConsumer, rmMagnificationConsumer};