function compressSpaces(str) {
    return str.replace(/(?!\u3000)\s+/gm, ' ');
}

function wrapFontFamily(fontFamily) {
    const trimmed = fontFamily.trim();

    return /^('|")/.test(trimmed) ? trimmed : `"${trimmed}"`;
}

function prepareFontFamily(fontFamily) {
    return typeof process === 'undefined' ? fontFamily : fontFamily.trim().split(',').map(wrapFontFamily).join(',');
}

/**
 * https://developer.mozilla.org/en-US/docs/Web/CSS/font-style
 * @param fontStyle
 * @returns CSS font style.
 */
function prepareFontStyle(fontStyle) {
    if (!fontStyle) {
        return '';
    }

    const targetFontStyle = fontStyle.trim().toLowerCase();

    switch (targetFontStyle) {
        case 'normal':
        case 'italic':
        case 'oblique':
        case 'inherit':
        case 'initial':
        case 'unset':
            return targetFontStyle;

        default:
            if (/^oblique\s+(-|)\d+deg$/.test(targetFontStyle)) {
                return targetFontStyle;
            }

            return '';
    }
}

/**
 * https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight
 * @param fontWeight
 * @returns CSS font weight.
 */
function prepareFontWeight(fontWeight) {
    if (!fontWeight) {
        return '';
    }

    const targetFontWeight = fontWeight.trim().toLowerCase();

    switch (targetFontWeight) {
        case 'normal':
        case 'bold':
        case 'lighter':
        case 'bolder':
        case 'inherit':
        case 'initial':
        case 'unset':
            return targetFontWeight;

        default:
            if (/^[\d.]+$/.test(targetFontWeight)) {
                return targetFontWeight;
            }

            return '';
    }
}

export default class Font {
    constructor(fontStyle, fontVariant, fontWeight, fontSize, fontFamily, inherit) {
        const inheritFont = inherit ? (typeof inherit === 'string' ? Font.parse(inherit) : inherit) : {};

        this.fontFamily = fontFamily || inheritFont.fontFamily;
        this.fontSize = fontSize || inheritFont.fontSize;
        this.fontStyle = fontStyle || inheritFont.fontStyle;
        this.fontWeight = fontWeight || inheritFont.fontWeight;
        this.fontVariant = fontVariant || inheritFont.fontVariant;
    }

    toString() {
        return [
            prepareFontStyle(this.fontStyle),
            this.fontVariant,
            prepareFontWeight(this.fontWeight),
            this.fontSize,
            // Wrap fontFamily only on nodejs and only for canvas.ctx
            prepareFontFamily(this.fontFamily)
        ]
            .join(' ')
            .trim();
    }
}

Font.styles = 'normal|italic|oblique|inherit';
Font.variants = 'normal|small-caps|inherit';
Font.weights = 'normal|bold|bolder|lighter|100|200|300|400|500|600|700|800|900|inherit';

Font.parse = function (font = '', inherit) {
    let fontStyle = '';
    let fontVariant = '';
    let fontWeight = '';
    let fontSize = '';
    let fontFamily = '';
    const parts = compressSpaces(font).trim().split(' ');
    const set = {
        fontSize: false,
        fontStyle: false,
        fontWeight: false,
        fontVariant: false
    };

    parts.forEach((part) => {
        switch (true) {
            case !set.fontStyle && Font.styles.includes(part):
                if (part !== 'inherit') {
                    fontStyle = part;
                }

                set.fontStyle = true;
                break;

            case !set.fontVariant && Font.variants.includes(part):
                if (part !== 'inherit') {
                    fontVariant = part;
                }

                set.fontStyle = true;
                set.fontVariant = true;
                break;

            case !set.fontWeight && Font.weights.includes(part):
                if (part !== 'inherit') {
                    fontWeight = part;
                }

                set.fontStyle = true;
                set.fontVariant = true;
                set.fontWeight = true;
                break;

            case !set.fontSize:
                if (part !== 'inherit') {
                    fontSize = part.split('/')[0] || '';
                }

                set.fontStyle = true;
                set.fontVariant = true;
                set.fontWeight = true;
                set.fontSize = true;
                break;

            default:
                if (part !== 'inherit') {
                    fontFamily += part;
                }
        }
    });

    return new Font(fontStyle, fontVariant, fontWeight, fontSize, fontFamily, inherit);
};
