/* Copyright CloudLinux Zug GmbH 2010-2017 All Rights Reserved
 *
 * Licensed under CLOUD LINUX LICENSE AGREEMENT
 * http://cloudlinux.com/docs/LICENSE.TXT
 */

import { plainToClass } from 'class-transformer';

export class Helper  {

    private static formatUnit = {
        'byteinsec': {
            'next': 'kbinsec',
            'icon': 'B/s',
            'coefficient': 1024,
            'precision': 2,
        },
        'kbinsec': {
            'next': 'mbinsec',
            'icon': 'KB/s',
            'coefficient': 1024,
            'precision': 2,
        },
        'mbinsec': {
            'next': 'gbinsec',
            'icon': 'MB/s',
            'coefficient': 1024,
            'precision': 2,
        },
        'gbinsec': {
            'next': null,
            'icon': 'G/s',
            'coefficient': 1,
            'precision': 2,
        },
        'bytes': {
            'next': 'kilobytes',
            'icon': 'B',
            'coefficient': 1024,
            'precision': 2,
        },
        'kilobytes': {
            'next': 'megabytes',
            'icon': 'K',
            'coefficient': 1024,
            'precision': 2,
        },
        'megabytes': {
            'next': 'gigabytes',
            'icon': 'M',
            'coefficient': 1024,
            'precision': 2,
        },
        'gigabytes': {
            'next': null,
            'icon': 'G',
            'coefficient': 1,
            'precision': 2,
        },
        'percents': {
            'next': null,
            'icon': '%',
            'coefficient': 1,
            'precision': 0,
        }
    };

    public static timeout: Function = ms => new Promise(res => setTimeout(res, ms));

    public static isEmpty(obj){
        return (Object.getOwnPropertyNames(obj).length === 0);
    }

    // According to MDN web docs
    public static unicodeBase64ToString(base64String: string): string {
        return decodeURIComponent(Array.prototype.map.call(atob(base64String), c => {
            return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
        }).join(''));
    }

    /**
     * Checks for existing of property in object by full path to property.
     *
     * @param object
     * @param property string   Full property path separated by underscore.
     * @returns {boolean}
     */
    public static hasOwnPropetyRecursively(object, property) {
        let parts = property.split('_');
        let curObject = object;
        for (let part of parts) {
            if (curObject.hasOwnProperty(part)) {
                curObject = curObject[part];
            } else {
                return false;
            }
        }
        return true;
    }

    /**
     * Gets value of property in object by full path to property
     * if it exists.
     *
     * @param object
     * @param property   Full property path separated by underscore.
     * @returns {any}
     */
    public static getValueFromObjectRecursively(object, property) {
        if (Helper.hasOwnPropetyRecursively(object, property)) {
            let parts = property.split('_');
            let curObject = object;
            let lastValue: any;
            for (let part of parts) {
                curObject = curObject[part];
                if (typeof curObject != 'object') {
                    return curObject;
                }
            }
        } else {
            return null;
        }
    }

    /**
     * Checks equality of two arrays.
     *
     * @param array1
     * @param array2
     * @returns {boolean}
     */
    public static isArraysEqual(array1: any[], array2: any[]):boolean {
        if (array1.length !== array2.length) {
            return false;
        }
        for (let el of array1) {
            if (array2.indexOf(el) === -1) {
                return false;
            }
        }
        return true;
    }

    /**
     * Reformat value
     * @param rawValue
     * @param format
     */
    public static reformatValue(rawValue, format){
        if (!/^-?[\d.]+$/.test(rawValue) || typeof rawValue === 'undefined') {
            return { value: rawValue, rawValue: rawValue };
        }
        rawValue = Number(rawValue);
        let value = rawValue;
        if (value && typeof format !== 'undefined' && Helper.formatUnit.hasOwnProperty(format)){
            while (true) {
                let newValue = value / Helper.formatUnit[format]['coefficient'];
                if (Helper.formatUnit[format]['next'] === null || newValue < 1) {
                    break;
                }
                format = Helper.formatUnit[format]['next'];
                value = newValue;
            }
            value = parseFloat(value.toFixed(Helper.formatUnit[format]['precision'])) + (
                (parseFloat(value.toFixed(Helper.formatUnit[format]['precision'])) !== 0)
                    ? Helper.formatUnit[format]['icon'] : ''
            );
        }
        return {
            value,
            rawValue
        };
    }

    public static reformatToDirtyValue(value){
        var multiplier = 1;
        if(/K$/i.test(value) || /KB\/s$/i.test(value))
            multiplier = 1024;
        else if(/M$/i.test(value) || /MB\/s$/i.test(value))
            multiplier = 1048576;
        else if(/G$/i.test(value) || /G\/s$/i.test(value))
            multiplier = 1073741824;

        return parseFloat(String(value).replace(/[^\d.-]/g,'')) * multiplier;
    }

    public static isObject(o) {
        return 'object' === typeof o;
    }

    public static recursivelyAssign2(target, source){
        for (let field in target){
            if (Helper.isObject(target[field]) && Helper.isObject(source[field])){
                Helper.recursivelyAssign2(target[field], source[field]);
            }
        }
        for (let field in source){
            if (!Helper.isObject(target[field])){
                target[field] = source[field];
            }
        }
    }

    public static removeBranchFromObject(object, branch) {
        for (let key in branch) {
            if (!object.hasOwnProperty(key)) {
                continue;
            }
            if (typeof branch[key] === 'object') {
                Helper.removeBranchFromObject(object[key], branch[key]);
            } else {
                delete object[key];
            }
        }
    }

    public static isNumber(n: any) {
        return !isNaN(parseFloat(n)) && isFinite(n);
    }

    /**
     * Convert dictionary to Class.
     * @param object
     * @param className
     */
    public static convertDictToClass(object, className) {
        Object.keys(object).forEach((item) => {
            object[item] = plainToClass(className, object[item]);
        });
    }

    public static comparePlainObjects(a: Object, b: Object): boolean {
        let aProps = Object.getOwnPropertyNames(a);
        let bProps = Object.getOwnPropertyNames(b);
        if (aProps.length !== bProps.length) {
            return false;
        }
        for (let i = 0; i < aProps.length; i++) {
            let propName = aProps[i];
            if (a[propName] !== b[propName]) {
                return false;
            }
        }
        return true;
    }

    public static convertStringToHexRepresentation(string: string) {
        if (typeof string !== 'string') return string;
        return string.split('')
            .map(letter => {
                return '\\u' + letter.charCodeAt(0).toString(16)
                    .toUpperCase().padStart(4, '0');
            })
            .join('');
    }
}
