(function (factory) {
    if (typeof module === "object" && typeof module.exports === "object") {
        var v = factory(require, exports);
        if (v !== undefined) module.exports = v;
    }
    else if (typeof define === "function" && define.amd) {
        define(["require", "exports", "../utils/arrays"], factory);
    }
})(function (require, exports) {
    /*---------------------------------------------------------------------------------------------
     *  Copyright (c) Microsoft Corporation. All rights reserved.
     *  Licensed under the MIT License. See License.txt in the project root for license information.
     *--------------------------------------------------------------------------------------------*/
    'use strict';
    Object.defineProperty(exports, "__esModule", { value: true });
    exports.Element = void 0;
    exports.default = calculateBoxModel;
    const arrays_1 = require("../utils/arrays");
    class Element {
        constructor(decl) {
            this.fullPropertyName = decl.getFullPropertyName().toLowerCase();
            this.node = decl;
        }
    }
    exports.Element = Element;
    function setSide(model, side, value, property) {
        const state = model[side];
        state.value = value;
        if (value) {
            if (!(0, arrays_1.includes)(state.properties, property)) {
                state.properties.push(property);
            }
        }
    }
    function setAllSides(model, value, property) {
        setSide(model, 'top', value, property);
        setSide(model, 'right', value, property);
        setSide(model, 'bottom', value, property);
        setSide(model, 'left', value, property);
    }
    function updateModelWithValue(model, side, value, property) {
        if (side === 'top' || side === 'right' ||
            side === 'bottom' || side === 'left') {
            setSide(model, side, value, property);
        }
        else {
            setAllSides(model, value, property);
        }
    }
    function updateModelWithList(model, values, property) {
        switch (values.length) {
            case 1:
                updateModelWithValue(model, undefined, values[0], property);
                break;
            case 2:
                updateModelWithValue(model, 'top', values[0], property);
                updateModelWithValue(model, 'bottom', values[0], property);
                updateModelWithValue(model, 'right', values[1], property);
                updateModelWithValue(model, 'left', values[1], property);
                break;
            case 3:
                updateModelWithValue(model, 'top', values[0], property);
                updateModelWithValue(model, 'right', values[1], property);
                updateModelWithValue(model, 'left', values[1], property);
                updateModelWithValue(model, 'bottom', values[2], property);
                break;
            case 4:
                updateModelWithValue(model, 'top', values[0], property);
                updateModelWithValue(model, 'right', values[1], property);
                updateModelWithValue(model, 'bottom', values[2], property);
                updateModelWithValue(model, 'left', values[3], property);
                break;
        }
    }
    function matches(value, candidates) {
        for (let candidate of candidates) {
            if (value.matches(candidate)) {
                return true;
            }
        }
        return false;
    }
    /**
     * @param allowsKeywords whether the initial value of property is zero, so keywords `initial` and `unset` count as zero
     * @return `true` if this node represents a non-zero border; otherwise, `false`
     */
    function checkLineWidth(value, allowsKeywords = true) {
        if (allowsKeywords && matches(value, ['initial', 'unset'])) {
            return false;
        }
        // a <length> is a value and a unit
        // so use `parseFloat` to strip the unit
        return parseFloat(value.getText()) !== 0;
    }
    function checkLineWidthList(nodes, allowsKeywords = true) {
        return nodes.map(node => checkLineWidth(node, allowsKeywords));
    }
    /**
     * @param allowsKeywords whether keywords `initial` and `unset` count as zero
     * @return `true` if this node represents a non-zero border; otherwise, `false`
     */
    function checkLineStyle(valueNode, allowsKeywords = true) {
        if (matches(valueNode, ['none', 'hidden'])) {
            return false;
        }
        if (allowsKeywords && matches(valueNode, ['initial', 'unset'])) {
            return false;
        }
        return true;
    }
    function checkLineStyleList(nodes, allowsKeywords = true) {
        return nodes.map(node => checkLineStyle(node, allowsKeywords));
    }
    function checkBorderShorthand(node) {
        const children = node.getChildren();
        // the only child can be a keyword, a <line-width>, or a <line-style>
        // if either check returns false, the result is no border
        if (children.length === 1) {
            const value = children[0];
            return checkLineWidth(value) && checkLineStyle(value);
        }
        // multiple children can't contain keywords
        // if any child means no border, the result is no border
        for (const child of children) {
            const value = child;
            if (!checkLineWidth(value, /* allowsKeywords: */ false) ||
                !checkLineStyle(value, /* allowsKeywords: */ false)) {
                return false;
            }
        }
        return true;
    }
    function calculateBoxModel(propertyTable) {
        const model = {
            top: { value: false, properties: [] },
            right: { value: false, properties: [] },
            bottom: { value: false, properties: [] },
            left: { value: false, properties: [] },
        };
        for (const property of propertyTable) {
            const value = property.node.value;
            if (typeof value === 'undefined') {
                continue;
            }
            switch (property.fullPropertyName) {
                case 'box-sizing':
                    // has `box-sizing`, bail out
                    return {
                        top: { value: false, properties: [] },
                        right: { value: false, properties: [] },
                        bottom: { value: false, properties: [] },
                        left: { value: false, properties: [] },
                    };
                case 'width':
                    model.width = property;
                    break;
                case 'height':
                    model.height = property;
                    break;
                default:
                    const segments = property.fullPropertyName.split('-');
                    switch (segments[0]) {
                        case 'border':
                            switch (segments[1]) {
                                case undefined:
                                case 'top':
                                case 'right':
                                case 'bottom':
                                case 'left':
                                    switch (segments[2]) {
                                        case undefined:
                                            updateModelWithValue(model, segments[1], checkBorderShorthand(value), property);
                                            break;
                                        case 'width':
                                            // the initial value of `border-width` is `medium`, not zero
                                            updateModelWithValue(model, segments[1], checkLineWidth(value, false), property);
                                            break;
                                        case 'style':
                                            // the initial value of `border-style` is `none`
                                            updateModelWithValue(model, segments[1], checkLineStyle(value, true), property);
                                            break;
                                    }
                                    break;
                                case 'width':
                                    // the initial value of `border-width` is `medium`, not zero
                                    updateModelWithList(model, checkLineWidthList(value.getChildren(), false), property);
                                    break;
                                case 'style':
                                    // the initial value of `border-style` is `none`
                                    updateModelWithList(model, checkLineStyleList(value.getChildren(), true), property);
                                    break;
                            }
                            break;
                        case 'padding':
                            if (segments.length === 1) {
                                // the initial value of `padding` is zero
                                updateModelWithList(model, checkLineWidthList(value.getChildren(), true), property);
                            }
                            else {
                                // the initial value of `padding` is zero
                                updateModelWithValue(model, segments[1], checkLineWidth(value, true), property);
                            }
                            break;
                    }
                    break;
            }
        }
        return model;
    }
});
