import { isNothing, isSome } from "@jet/environment/types/optional";
const HEX_PARSER = new RegExp("#?([0-9,a-f,A-F][0-9,a-f,A-F])([0-9,a-f,A-F][0-9,a-f,A-F])([0-9,a-f,A-F][0-9,a-f,A-F])([0-9,a-f,A-F][0-9,a-f,A-F])?");
export function fromHex(hexString) {
    if (isNothing(hexString) || hexString === "") {
        return null;
    }
    const results = HEX_PARSER.exec(hexString);
    if (results === null || !(results.length === 4 || results.length === 5)) {
        return null;
    }
    const red = parseInt(results[1], 16) / 255;
    const green = parseInt(results[2], 16) / 255;
    const blue = parseInt(results[3], 16) / 255;
    const alpha = isSome(results[4]) ? parseInt(results[4], 16) / 255 : undefined;
    const newColor = {
        red: red,
        green: green,
        blue: blue,
        alpha: alpha,
        type: "rgb",
    };
    return newColor;
}
export const black = rgbWith(0, 0, 0);
export const white = rgbWith(1, 1, 1);
export function rgbWith(red, green, blue, alpha) {
    const newColor = {
        red: red,
        green: green,
        blue: blue,
        alpha: alpha,
        type: "rgb",
    };
    return newColor;
}
export function luminanceFrom(rgbColor) {
    // Note: This is lifted from UIColor_Private
    // Using RGB color components, calculates and returns (0.2126 * r) + (0.7152 * g) + (0.0722 * b).
    return rgbColor.red * 0.2126 + rgbColor.green * 0.7152 + rgbColor.blue * 0.0722;
}
/**
 * Determines whether the given color is considered dark.
 * @param color The color to test.
 * @param darkColorCutoff The cutoff value, which ought to be between 0 and 100.
 */
export function isDarkColor(color, darkColorCutoff = 84) {
    if (isNothing(color)) {
        return false;
    }
    switch (color.type) {
        case "rgb":
            // Multiplying 100 because the rest of the codebase now works off darkColorCutoff 0-100.
            // Before this change it used to be darkColorCutoff 0.0-1.0
            return luminanceFrom(color) * 100 < darkColorCutoff;
        case "named":
            return isNamedColorDarkColor(color);
        default:
            return false;
    }
}
function isNamedColorDarkColor(namedColor) {
    switch (namedColor.name) {
        case "black":
            return true;
        default:
            return false;
    }
}
export function named(name) {
    const newColor = {
        name: name,
        type: "named",
    };
    return newColor;
}
export function dynamicWith(lightColor, darkColor) {
    const newColor = {
        lightColor: lightColor,
        darkColor: darkColor,
        type: "dynamic",
    };
    return newColor;
}
/**
 * A convenience for comparing two colors to see if they are equal.
 * @param firstColor The first color to compare
 * @param secondColor The second color to compare
 * @returns True if the colors are considered equal
 */
export function isColorEqualToColor(firstColor, secondColor) {
    if (isNothing(firstColor) || isNothing(secondColor)) {
        return false;
    }
    if (firstColor.type === "rgb" && secondColor.type === "rgb") {
        return (firstColor.red === secondColor.red &&
            firstColor.green === secondColor.green &&
            firstColor.blue === secondColor.blue &&
            firstColor.alpha === secondColor.alpha);
    }
    else if (firstColor.type === "named" && secondColor.type === "named") {
        return firstColor.name === secondColor.name;
    }
    else if (firstColor.type === "dynamic" && secondColor.type === "dynamic") {
        return (isColorEqualToColor(firstColor.lightColor, secondColor.lightColor) &&
            isColorEqualToColor(firstColor.darkColor, secondColor.darkColor));
    }
    return false;
}
/**
 * Type guard to check if a value is a ColorBucketSaturationBrightnessCriteria.
 * @param value The value to check.
 * @returns True if the value is a ColorBucketSaturationBrightnessCriteria, false otherwise.
 */
function isSaturationBrightnessCriteria(value) {
    if (typeof value !== "object" || value === null) {
        return false;
    }
    const obj = value; // Cast for easier property access
    // Check if all required properties exist and have the correct types
    return ("colorHex" in obj &&
        typeof obj.colorHex === "string" &&
        "maxSaturation" in obj &&
        typeof obj.maxSaturation === "number" &&
        "maxBrightness" in obj &&
        typeof obj.maxBrightness === "number");
}
/**
 * Finds the closest matching color from a list of hex colors using LAB color space
 * for more perceptually accurate matching
 */
export function findColorBucketForColor(targetRGB, saturationBrightnessBasedBuckets, hueBasedBuckets) {
    if (isNothing(targetRGB) || (saturationBrightnessBasedBuckets.length === 0 && hueBasedBuckets.length === 0)) {
        return null;
    }
    const targetColor = targetRGB;
    if (isNothing(targetColor)) {
        return null;
    }
    const targetHsb = rgbToHsb(targetColor);
    for (const saturationBrightnessBasedBucket of saturationBrightnessBasedBuckets) {
        if (doesColorMeetCriteria(targetHsb, saturationBrightnessBasedBucket)) {
            return fromHex(saturationBrightnessBasedBucket.colorHex);
        }
    }
    for (const hueBasedBucket of hueBasedBuckets) {
        if (doesColorMeetCriteria(targetHsb, hueBasedBucket)) {
            return fromHex(hueBasedBucket.colorHex);
        }
    }
    return null;
}
/**
 * Determines whether a color meets the specified criteria.
 *
 * For saturation/brightness criteria, the color passes if either its saturation or brightness
 * is less than or equal to the maximum allowed values.
 *
 * For hue criteria, the color passes if its hue falls within the specified range (inclusive).
 *
 * @param color The RGB color to evaluate
 * @param criteria The criteria to check against (either hue-based or saturation/brightness-based)
 * @returns True if the color passes the criteria, false otherwise or if color is null/undefined
 */
export function doesColorMeetCriteria(color, criteria) {
    if (isNothing(color)) {
        return false;
    }
    const hsbColor = isHSBColor(color) ? color : rgbToHsb(color);
    // Check saturation / brightness buckets first, using <= for inclusive range checks
    if (isSaturationBrightnessCriteria(criteria)) {
        return hsbColor.saturation <= criteria.maxSaturation || hsbColor.brightness <= criteria.maxBrightness;
    }
    else {
        // Check if the hue falls within the min/max bounds (inclusive)
        // Special handling for the Pink range potentially including 360 if needed,
        // but mapping 360->0 covers the Coral case correctly.
        return hsbColor.hue >= criteria.minHue && hsbColor.hue <= criteria.maxHue;
    }
}
function isHSBColor(value) {
    if (typeof value !== "object" || value === null) {
        return false;
    }
    const obj = value; // Cast for easier property access
    // Check if the type property exists and has the correct value
    return "type" in obj && obj.type === "hsb";
}
/**
 * Converts RGB color to HSB color space.
 * HSL is particularly good at representing relationships between colors
 * as humans perceive them, especially for determining similar hues
 * across different brightness levels.
 */
function rgbToHsb(rgb) {
    const r = rgb.red;
    const g = rgb.green;
    const b = rgb.blue;
    const max = Math.max(r, g, b);
    const min = Math.min(r, g, b);
    const delta = max - min;
    // Calculate brightness (value in HSV)
    const brightness = max * 100;
    // Calculate saturation
    const saturation = max === 0 ? 0 : (delta / max) * 100;
    // Calculate hue
    let hue = 0;
    if (delta !== 0) {
        switch (max) {
            case r:
                hue = ((g - b) / delta + (g < b ? 6 : 0)) * 60;
                break;
            case g:
                hue = ((b - r) / delta + 2) * 60;
                break;
            case b:
                hue = ((r - g) / delta + 4) * 60;
                break;
            default:
                break;
        }
    }
    // Ensure hue is in [0, 360)
    hue = (hue + 360) % 360;
    return {
        type: "hsb",
        hue: hue,
        saturation: saturation,
        brightness: brightness,
    };
}
//# sourceMappingURL=color-util.js.map