import { semanticColors } from '../../semanticColors.js';
import { isHTMLElement, ref } from './util.js';
const colorCache = {
    default: '',
    transparent: 'rgba(0, 0, 0, 0)',
};
/**
 * Attach a ref callback to an element that ensures the assigned color is set and
 * attempts to use CSS variables for colors as a fallback
 *
 * @param style The sanitized StyleInfo
 * @param attr The attribute name to validate
 */
export function resolveStyleColor(style, attr) {
    const color = style[attr];
    const propertyName = attr.replace(/[A-Z]/g, (sub) => `-${sub.toLowerCase()}`);
    return ref((el) => {
        if (isHTMLElement(el)) {
            let success = false;
            if (color && typeof color === 'string') {
                success =
                    applyAsCssColor(el, propertyName, color) ||
                        applyAsColorPrimitive(el, propertyName, color) ||
                        applyAsComputedColor(el, propertyName, color);
            }
            if (!success) {
                // Remove the property so stale styles aren't persisted by mistake
                el.style.removeProperty(propertyName);
            }
        }
    });
}
/**
 * Applies the color directly if the color is a #hex, rgb()/rgba(), or hsl()/hsla() color
 * @param el Element to apply the color
 * @param propertyName Javascript property name (camel case) to recieve the color
 * @param color Requested color
 */
function applyAsCssColor(el, propertyName, color) {
    if (color.match(/^(#[0-9a-fA-F]{3,8}|((rgb|hsl)(a?)\()).*/)) {
        el.style.setProperty(propertyName, color);
        return true;
    }
    return false;
}
/**
 * Looks up the color in the RPL color primitive table and applies the color if found
 * @param el Element to apply the color
 * @param propertyName Javascript property name (camel case) to recieve the color
 * @param color Requested color
 */
function applyAsColorPrimitive(el, propertyName, color) {
    const colorLower = color.toLowerCase();
    const colors = semanticColors;
    const colorPrimitive = colors[colorLower];
    if (colorPrimitive) {
        el.style.setProperty(propertyName, colorPrimitive);
        return true;
    }
    return false;
}
/**
 * Applies the color as either an HTML color name or as a color variable from the current theme
 * @param el Element to apply the color
 * @param propertyName Javascript property name (camel case) to recieve the color
 * @param color Requested color
 */
function applyAsComputedColor(el, propertyName, color) {
    const computedColor = getComputedColor(color);
    if (computedColor) {
        // If the value is not already the color, attempt to set it
        if (el.style.getPropertyValue(propertyName) !== computedColor) {
            el.style.setProperty(propertyName, computedColor);
        }
        // If it still doesn't match then the color is invalid
        return el.style.getPropertyValue(propertyName) === computedColor;
    }
    return false;
}
/**
 * Borrowed from: https://gist.github.com/w3core/e3d9b5b6d69a3ba8671cc84714cca8a4?permalink_comment_id=3125287#gistcomment-3125287
 * Calculate brightness value by RGB or HEX color.
 * @param color (String) The color value in RGB or HEX (for example: #000000 || #000 || rgb(0,0,0) || rgba(0,0,0,0))
 * @returns (Number) The brightness value (dark) 0 ... 255 (light)
 */
export function brightnessByColor(color) {
    const isHEX = color.indexOf('#') === 0;
    const isRGB = color.indexOf('rgb') === 0;
    let r;
    let g;
    let b;
    if (isHEX) {
        const hasFullSpec = color.length === 7;
        const m = color.substring(1).match(hasFullSpec ? /(\S{2})/g : /(\S)/g);
        if (m) {
            r = parseInt(m[0] + (hasFullSpec ? '' : m[0]), 16);
            g = parseInt(m[1] + (hasFullSpec ? '' : m[1]), 16);
            b = parseInt(m[2] + (hasFullSpec ? '' : m[2]), 16);
        }
    }
    if (isRGB) {
        // eslint-disable-next-line security/detect-unsafe-regex
        const m = color.match(/(\d+){3}/g);
        if (m) {
            r = parseInt(m[0]);
            g = parseInt(m[1]);
            b = parseInt(m[2]);
        }
    }
    if (r !== undefined && g !== undefined && b !== undefined) {
        return (r * 299 + g * 587 + b * 114) / 1000;
    }
    return 0;
}
/**
 * Determines the computed value of a color by applying it to a temporary element
 * attached to the document in order to utilize `window.getComputedStyle()` which
 * returns an `rgba()` string representing the displayed color.
 *
 * If the color resolves as an HTML color the value is cached to prevent unnecessary
 * repeated lookups for unchanging values.
 *
 * If the color is a CSS variable it will not be cached to prevent stale colors from
 * persisting between light and dark modes or app themes.
 *
 * @param element Element to apply the final color
 * @param color Requested color
 * @returns (String) The calculated color, or an empty string if the color was not resolved
 */
function getComputedColor(color) {
    if (colorCache[color]) {
        return colorCache[color];
    }
    const doc = document.body;
    const div = document.createElement('div');
    doc.appendChild(div);
    if (!colorCache.default) {
        // cache the default color once
        colorCache.default = window.getComputedStyle(div).backgroundColor;
    }
    // try it as an HTML color name
    div.style.backgroundColor = color;
    let computed = window.getComputedStyle(div).backgroundColor;
    let resolved = div.style.getPropertyValue('background-color');
    if (!validColor(resolved, computed)) {
        // not an HTMl color, try a CSS variable
        div.style.backgroundColor = `var(--color-${color})`;
        computed = window.getComputedStyle(div).backgroundColor;
        resolved = div.style.getPropertyValue('background-color');
    }
    doc.removeChild(div);
    // The initial value of backgroundColor is defined as 'transparent' in the W3C spec
    // so if the computed value is transparent then the color was invalid.
    // Note: 'transparent' is pre-seeded in the color cache to avoid unnecessary lookups
    if (validColor(resolved, computed)) {
        if (!resolved.startsWith('var(')) {
            colorCache[color] = computed;
        }
        return computed;
    }
    else {
        return '';
    }
}
function validColor(resolved, computed) {
    return !!resolved && computed !== colorCache.default && computed !== colorCache.transparent;
}
//# sourceMappingURL=color.js.map