/*
Copyright (c) 2014, Yahoo! Inc. All rights reserved.
Copyrights licensed under the New BSD License.
See the accompanying LICENSE file for terms.
*/
// https://github.com/ericf/css-mediaquery
import { Trace } from '../trace';
import { Length } from '../ui/styling/length-shared';
// -----------------------------------------------------------------------------
const RE_MEDIA_QUERY = /^(?:(only|not)?\s*([_a-z][_a-z0-9-]*)|(\([^\)]+\)))(?:\s*and\s*(.*))?$/i, RE_MQ_EXPRESSION = /^\(\s*([_a-z-][_a-z0-9-]*)\s*(?:\:\s*([^\)]+))?\s*\)$/, RE_MQ_FEATURE = /^(?:(min|max)-)?(.+)/, RE_LENGTH_UNIT = /(em|rem|px|cm|mm|in|pt|pc)?\s*$/, RE_RESOLUTION_UNIT = /(dpi|dpcm|dppx)?\s*$/;
export var MediaQueryType;
(function (MediaQueryType) {
    MediaQueryType["all"] = "all";
    MediaQueryType["print"] = "print";
    MediaQueryType["screen"] = "screen";
})(MediaQueryType || (MediaQueryType = {}));
export function matchQuery(mediaQuery, values) {
    const expressions = parseQuery(mediaQuery);
    return expressions.some((query) => {
        const { type, inverse, features } = query;
        // Either the parsed or specified `type` is "all", or the types must be
        // equal for a match.
        const typeMatch = query.type === 'all' || values.type === query.type;
        // Quit early when `type` doesn't match, but take "not" into account
        if ((typeMatch && inverse) || !(typeMatch || inverse)) {
            return false;
        }
        const expressionsMatch = features.every((feature) => {
            const value = values[feature.property];
            // Missing or falsy values don't match
            if (!value && value !== 0) {
                return false;
            }
            switch (feature.property) {
                case 'orientation':
                case 'prefers-color-scheme':
                    if (typeof value !== 'string') {
                        return false;
                    }
                    return value.toLowerCase() === feature.value.toLowerCase();
                default: {
                    // Numeric properties
                    let numVal;
                    if (typeof value !== 'number') {
                        Trace.write(`Unknown CSS media query feature property: '${feature.property}' on '${query}'`, Trace.categories.MediaQuery, Trace.messageType.warn);
                        return false;
                    }
                    switch (feature.property) {
                        case 'width':
                        case 'height':
                        case 'device-width':
                        case 'device-height': {
                            numVal = Length.toDevicePixels(Length.parse(feature.value), 0);
                            break;
                        }
                        default:
                            Trace.write(`Unknown CSS media query feature property: '${feature.property}' on '${query}'`, Trace.categories.MediaQuery, Trace.messageType.warn);
                            break;
                    }
                    switch (feature.modifier) {
                        case 'min':
                            return value >= numVal;
                        case 'max':
                            return value <= numVal;
                        default:
                            return value === numVal;
                    }
                    break;
                }
            }
        });
        return (expressionsMatch && !inverse) || (!expressionsMatch && inverse);
    });
}
export function parseQuery(mediaQuery) {
    const mediaQueryStrings = mediaQuery.split(',');
    return mediaQueryStrings.map((query) => {
        query = query.trim();
        const captures = query.match(RE_MEDIA_QUERY);
        // Media query must be valid
        if (!captures) {
            throw new SyntaxError(`Invalid CSS media query: '${query}'`);
        }
        const modifier = captures[1];
        const type = captures[2];
        const featureString = ((captures[3] || '') + (captures[4] || '')).trim();
        const expression = {
            inverse: !!modifier && modifier.toLowerCase() === 'not',
            type: MediaQueryType[type ? type.toLowerCase() : 'all'] ?? 'all',
            features: [],
        };
        // Check for media query features
        if (!featureString) {
            return expression;
        }
        // Split features string into a list
        const features = featureString.match(/\([^\)]+\)/g);
        // Media query must be valid
        if (!features) {
            throw new SyntaxError(`Invalid CSS media query features: '${featureString}' on '${query}'`);
        }
        for (const feature of features) {
            const captures = feature.match(RE_MQ_EXPRESSION);
            // Media query must be valid
            if (!captures) {
                throw new SyntaxError(`Invalid CSS media query feature: '${feature}' on '${query}'`);
            }
            const featureData = captures[1].toLowerCase().match(RE_MQ_FEATURE);
            expression.features.push({
                modifier: featureData[1],
                property: featureData[2],
                value: captures[2],
            });
        }
        return expression;
    });
}
//# sourceMappingURL=index.js.map