import { Model } from 'front';


// uuid generator (SO: /questions/105034/create-guid-uuid-in-javascript)
export function uuidgen() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        const r = Math.random() * 16 | 0,
            v = c === 'x' ? r : (r & 0x3 | 0x8);

        return v.toString(16);
    });
}


// Transforms proper kebab case ('some-word') to proper case ('Some Word')
export function kebabToProperCase(str) {
    return str
        .split('-')
        .map((word) => word[0].toUpperCase() + word.slice(1))
        .join(' ');
}

// Transforms proper snake case ('some_word') to camel case ('someWord')
export function snakeToCamelCase(str) {
    return str.replace(/(_\w)/g, (m) => m[1].toUpperCase());
}

// returns a new object/array with all keys of objects renamed according to the
// callback - will also rename keys of nested objects.
export function renameKeys(obj, cb, deep = false) {
    if (typeof cb !== 'function') {
        throw new Error('renameKeys requires a valid callback.');
    }

    if (!obj || typeof obj !== 'object') {
        return obj;
    }

    if (Array.isArray(obj)) {
        return obj.map((el) => renameKeys(el, cb));
    } else {
        return Object.keys(obj).reduce((renamed, key) => {
            renamed[cb(key)] = deep ? renameKeys(obj[key], cb) : obj[key];
            return renamed;
        }, {});
    }
}


// returns a function that accepts a key and returns the first matching value
// if it exists in any of the mappings. otherwise, simply returns the fallback
// each mapping should be a hash; the last argument passed in is the fallback
export function matcher(...mappings) {
    const fallback = mappings.pop();

    return function(key) {
        for (const mapping of mappings) {
            /* istanbul ignore next: no one cares */
            if (!mapping || typeof mapping !== 'object') {
                continue;
            }

            if (mapping.hasOwnProperty(key)) {
                return mapping[key];
            }
        }

        return fallback;
    };
}


// recursively fill a model so that nested models are also filled
// instead of newly instantiated. This should be copied to front
/* istanbul ignore next: because this belongs somewhere else */
export function smartfill(model, src) {
    let _src = src;

    if (!(model instanceof Model)) {
        return;
    }

    if (_src === null) {
        model.clear();
        return;
    }

    if (_src instanceof Model) {
        _src = _src.out();
    }

    for (const key in _src) {
        if (!model.hasOwnProperty(key)) {
            continue;
        }

        if (model[key] instanceof Model) {
            const old = model[key].out();

            if (_src[key] instanceof Model) {
                _src[key] = _src[key].out();
            }

            smartfill(model[key], _src[key]);
            model._changeThrottle.add(key, { old, new: _src[key] });

            continue;
        }

        model[key] = _src[key];
    }
}
