class Booyah {
    constructor() {
        this._abbrevations = {};
        this._routes = {};
        this._customValues = {};
        this.$ = {};

        // AJAX SETUP
        $.ajaxSetup({
            headers: {
                'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
            }
        });

        this.init();
    }

    init() {
        /************************************************************
         * THEMES
         */

        // Colors
        this.themeColors = {
            primary: null,
            secondary: null,
            success: null,
            info: null,
            warning: null,
            danger: null,
            light: null,
            dark: null
        };

        this.themeChartColorKeys = [
            'primary',
            'secondary',
            'info',
            'warning',
            'danger',
            'dark'
        ];

        this.initThemeColors();
    }

    filterObject(obj, toCheck) {
        let result = {};
        let keys = Object.keys(obj);
        for (let i = 0; i < keys.length; i ++) {
            let key = keys[i];
            if (toCheck instanceof Function) {
                if (toCheck(obj[key])) {
                    result[key] = obj[key];
                }
            } else {
                if (obj.hasOwnProperty(toCheck)) {
                    if (Boolean(obj[toCheck])) {
                        result[key] = obj[key];
                    }
                }
            }
        }
        return result;
    };

    addCustomValue(key, value, override = false) {
        if(typeof key !== "undefined" && typeof value !== "undefined") {
            if(typeof this._customValues[key] !== 'undefined') {
                if (override) {
                    this._customValues[key] = value;
                } else {
                    console.warn('Tried to override custom value \'' + key + '\'');
                }
            } else {
                this._customValues[key] = value;
            }
        }
    };

    getCustomValue(key, fallback = null) {
        if(typeof this._customValues[key] !== 'undefined') {
            return this._customValues[key];
        }

        return fallback;
    };

    /**************************************
     * THEMES & COLORS
     */
    initThemeColors() {
        const themeColorSpan = $('<span class="hidden theme-clr"></span>');
        $('body').append(themeColorSpan);
        let colorKeys = Object.keys(this.themeColors);
        for (let i = 0; i < colorKeys.length; i ++) {
            let key = colorKeys[i];
            themeColorSpan.addClass('theme-clr__' + key);
            this.themeColors[key] = themeColorSpan.css('color');
            themeColorSpan.removeClass('theme-clr__' + key);
        }
        themeColorSpan.remove();
    };

    addThemeColors(colors = {}, override = false) {
        let colorKeys = Object.keys(colors);
        for (let i = 0; i < colorKeys.length; i ++) {
            if(typeof this.themeColors[colorKeys[i]] !== 'undefined') {
                if (override) {
                    this.themeColors[colorKeys[i]] = colors[colorKeys[i]];
                } else {
                    console.warn('Tried to override themecolor \'' + colorKeys[i] + '\' => \'' + colors[colorKeys[i]] + '\'');
                }
            } else {
                this.themeColors[colorKeys[i]] = colors[colorKeys[i]];
            }
        }
    }

    getThemeColor(key) {
        if (typeof key === 'number') {
            if (typeof this.themeColors[this.getThemeColorKeys()[key]] !== 'undefined') {
                return this.themeColors[this.getThemeColorKeys()[key]];
            }
        } else if (typeof key !== 'undefined') {
            if (key in this.themeColors) {
                return this.themeColors[key];
            }
            return this.themeColors['primary'];
        }

        return this.themeColors;
    };

    get getThemeColorRand() {
        let keys = Object.keys(this.themeColors);
        let key = keys[Math.floor(Math.random() * (keys.length - 1))];
        return this.getThemeColor(key);
    };

    static getFormData(serializedArr, name, valueKey) {
        if (typeof name === 'undefined') {
            return null;
        }
        if (typeof valueKey === 'undefined') {
            valueKey = 'value';
        }
        if (typeof serializedArr[name] !== 'undefined') {
            return serializedArr[name];
        }
        let data = [];
        $.each(serializedArr, function (key, element) {
            if (element.name === name) {
                data.push(element[valueKey]);
            }
        });
        return data.join(',');
    };

    /**************************************
     * DATES
     */
    static dateWithOffset(datestring) {
        let date = new Date(datestring);
        let offset = new Date().getTimezoneOffset();

        return new Date((date.getTime()) - (offset * 60 * 1000));
    };

    static getDateString(date) {
        let month = date.getMonth() + 1;
        month = (month < 10) ? ('0' + month) : month;
        let day = date.getDate();
        day = (day < 10) ? ('0' + day) : day;
        return date.getFullYear() + '-' + month + '-' + day + ' 00:00:00';
    };

    /*************************************+
     * Abbrevations
     */

    addAbbrevations(toAdd) {
        this._abbrevations = Object.assign(this._abbrevations, toAdd);
    }

    get getAbbrevations() {
        return this._abbrevations;
    }

    getAbbrevationTitle(key) {
        return Booyah.objectGet(key, this.getAbbrevations, '');
    };

    getAbbrHtml(abbreviation) {
        let title = this.getAbbrevationTitle(abbreviation);
        if (title.trim() !== '') {
            return '<abbr title="' + title + '">' + abbreviation + '</abbr>';
        }

        return abbreviation;
    };

    /*************************************
     * ROUTES (AJAX)
     */

    set setRoutes(routes) {
        this._routes = routes;
    }

    get getRoutes() {
        return this._routes;
    }

    addRoutes(routes = {}, override = false) {
        let routeKeys = Object.keys(routes);
        for (let i = 0; i < routeKeys.length; i ++) {
            if(typeof this.getRoutes[routeKeys[i]] !== 'undefined') {
                if (override) {
                    this._routes[routeKeys[i]] = routes[routeKeys[i]];
                } else {
                    console.warn('Tried to override route \'' + routeKeys[i] + '\' => \'' + routes[routeKeys[i]] + '\'');
                }
            } else {
                this._routes[routeKeys[i]] = routes[routeKeys[i]];
            }
        }
    }

    addRoute(name, route, override = false) {
        if (typeof name === 'string' && typeof route === 'string') {
            if (typeof this.getRoutes[name] !== 'undefined') {
                if (override) {
                    this._routes[name] = route;
                } else {
                    console.warn('Tried to override route \'' + name + '\' => \'' + route + '\'');
                }
            } else {
                this._routes[name] = route;
            }
        }
    }

    getRoute(key) {
        if (key in this.getRoutes) {
            return this.getRoutes[key];
        }

        return '/';
    };

    /*************************************
     * Layout Helper
     */
    thinsp(unit = '') {
        return '<span class="thinsp"><span>&nbsp;</span></span>' + this.getAbbrHtml(unit);
    };

    static roundNumber(value, decimals) {
        return Math.round(value * Math.pow(10, decimals)) / Math.pow(10, decimals);
    };

    static numberFormatEU(number, decimals) {
        return this.roundNumber(number, decimals).toString().replace('.', ',');
    };

    numberFormatAbbr(value = 0, unit = '', decimals = 2, decimalsFormat = 2) {
        let formats = {
            '1000000000000000': '<abbr title="Billiarden">Brd.</abbr>',
            '1000000000000': '<abbr title="Billionen">Bio.</abbr>',
            '1000000000': '<abbr title="Milliarde">Mrd.</abbr>',
            '1000000': '<abbr title="Millionen">Mio.</abbr>',
            '1000': '<abbr title="Tausend">Tsd.</abbr>'
        };
        let thinsp = this.thinsp();
        let formatedNumber = '';
        $.each(formats, function (key) {
            if (value >= parseInt(key)) {
                let rounded = Booyah.roundNumber((value / parseInt(key)), decimalsFormat);
                if (rounded > value / key) {
                    rounded -= Math.pow(10, - decimalsFormat);
                }

                formatedNumber = Booyah.numberFormatEU(rounded, decimals) + thinsp + formats[key];
            }
        });
        if (formatedNumber !== '') {
            return formatedNumber;
        }

        return Booyah.numberFormatEU(value, decimals);
    };

    static formatMelo(id = '') {
        if (id.length === 33) {
            let seperator = Booyah.locale === 'at' ? '&nbsp;' : '.';
            let formatedId = id.substr(0, 2) + seperator;
            formatedId += id.substr(2, 6) + seperator;
            formatedId += id.substr(8, 5) + seperator;
            formatedId += id.substr(13) + seperator;

            return formatedId;
        }
        return id
    }

    static get locale() {
        return $('html').attr('lang');
    }

    static get currentBreakpoint() {
        return getComputedStyle($('body').get(0)).getPropertyValue('--breakpoint');
    }

    static toggleCardLoader($card) {
        if ($card.is('.loading')) {
            $card.find('.card-loader').remove();
        } else {
            $card.append('<span class="card-loader"><i class="fa fa-spinner fa-spin fa-3x fa-fw"></i></span>');
        }
        $card.toggleClass('loading');
    };

    /**
     * IMAGE HELPER
     */
    static imageToBase64(img) {
        if (! img.length) {
            return '';
        }

        $('body').prepend('<canvas id="base64canvas"></canvas>');

        let canvas = document.getElementById("base64canvas");
        canvas.width = img.width();
        canvas.height = img.height();

        let ctx = canvas.getContext("2d");
        ctx.drawImage(img.get(0), 0, 0, img.width(), img.height());

        $('body').find('#base64canvas').remove();

        return canvas.toDataURL("image/png");
    };

    /**
     * COMMON HELPER
     */
    static get randomId() {
        let tmpId;
        do {
            tmpId = Math.random().toString(12).substr(2);
        }
        while ($('#' + tmpId).length > 0);

        return tmpId;
    };

    static get getActiveTab() {
        if (typeof window.location.hash !== 'undefined') {
            return window.location.hash.substr(1);
        }

        return '';
    };

    static elementInViewport(element) {
        let el = $(element)[0];
        let rect = el.getBoundingClientRect();
        return (
            rect.top >= 0
            && rect.left >= 0
            && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight || $(window).height())
            && rect.right <= (window.innerWidth || document.documentElement.clientWidth || $(window).width())
        );
    };

    static toClipboard(copyText = '') {
        $('body').prepend('<textarea id="booyah__to-clipboard--field">' + copyText + '</textarea>');
        $('#booyah__to-clipboard--field').get(0).select();
        document.execCommand("copy");
        $('#booyah__to-clipboard--field').remove();
    }

    static getAllDaysOfMonth(date, format) {
        let lastDay = parseInt(date.clone().endOf('month').format('D'));
        let dateArray = [];
        for (let day = 1; day <= lastDay; day ++) {
            date.date(day);
            dateArray.push(date.format(format));
        }

        return dateArray;
    };

    static getAllDaysOfYear(date, format) {
        let dateArray = [];
        for (let month = 0; month <= 11; month ++) {
            date.month(month);
            let daysOfMonth = parseInt(date.clone().endOf('month').format('D'));
            for (let day = 1; day <= daysOfMonth; day ++) {
                date.date(day);
                dateArray.push(date.format(format));
            }
        }

        return dateArray;
    };

    static undefinedToZero(val) {
        if (typeof val === 'undefined') return 0;
        return val;
    };

    static ifIsInTo(val, arr, to) {
        if ($.inArray(val, arr) !== - 1) return to;
        return val;
    };

    static strBeginsWith(str, search) {
        return str.trim().indexOf(search.trim()) === 0;
    };

    static colorIsHex(colorString) {
        return Booyah.strBeginsWith(colorString.trim(), '#');
    };

    static colorIsRgb(colorString) {
        return Booyah.strBeginsWith(colorString.trim(), 'rgb');
    };

    static redundantArray(item, count) {
        var reduntantArr = [];
        for (var i = 0; i < count; i ++) {
            reduntantArr.push(item);
        }
        return reduntantArr;
    };

    static ucFirst(word) {
        return word.charAt(0).toUpperCase() + word.slice(1);
    };

    static objectsEqual() {
        let equals = true;
        if (arguments.length < 2) {
            throw new TypeError('Cannot compare nothing.');
        }
        for (let i = 0; i < arguments.length; i ++) {
            if (typeof arguments[i] !== 'object' || arguments[i] === null) {
                throw new TypeError('Cannot compare when one of the objects is null or undefined.');
            }
        }
        let oldObject = arguments[0];
        for (let i = 1; i < arguments.length; i ++) {
            let objetToCompareWithOld = arguments[i];
            let keysObj = Object.keys(oldObject);
            let keysObj2 = Object.keys(objetToCompareWithOld);
            let equalKeys = keysObj.toString() === keysObj2.toString();
            if (equalKeys) {
                for (let i = 0; i < keysObj.length; i ++) {
                    if (typeof oldObject[keysObj[i]] !== typeof objetToCompareWithOld[keysObj[i]]) {
                        equals = false;
                    } else {
                        if (typeof oldObject[keysObj[i]] === 'object') {
                            equals = Booyah.objectsEqual(oldObject[keysObj[i]], objetToCompareWithOld[keysObj[i]]);
                        } else {
                            equals = oldObject[keysObj[i]] === objetToCompareWithOld[keysObj[i]];
                        }
                    }
                    if (! equals) {
                        break;
                    }
                }
                if (! equals) {
                    break;
                }
            } else {
                equals = false;
                break;
            }
            oldObject = objetToCompareWithOld;
        }

        return equals;
    }

    static objectGet(searchVal, object, defaultVal) {
        let result = object;
        let keys = [];
        if (typeof searchVal === 'string') {
            keys = searchVal.split('.');
        } else if (typeof searchVal === 'object') {
            keys = searchVal;
        }
        for (let i = 0; i < keys.length; i ++) {
            if (typeof result[keys[i]] === 'undefined') {
                result = defaultVal;
                break;
            }
            result = result[keys[i]];
        }

        return result;
    }
}

var booyah = new Booyah();
