% Bundled helper for WCAG contrast ratios.
%
% Example:
%   contrast_ratio("#111827", "#ffffff", Ratio).
%   contrast_passes("#111827", "#ffffff", aa).

contrast_ratio(Foreground, Background, Ratio) :-
    fudge_contrast_luminance(Foreground, L1),
    fudge_contrast_luminance(Background, L2),
    Light is max(L1, L2),
    Dark is min(L1, L2),
    Ratio is (Light + 0.05) / (Dark + 0.05).

contrast_passes(Foreground, Background, aa) :-
    contrast_ratio(Foreground, Background, Ratio),
    Ratio >= 4.5.
contrast_passes(Foreground, Background, aa_large) :-
    contrast_ratio(Foreground, Background, Ratio),
    Ratio >= 3.
contrast_passes(Foreground, Background, aaa) :-
    contrast_ratio(Foreground, Background, Ratio),
    Ratio >= 7.
contrast_passes(Foreground, Background, aaa_large) :-
    contrast_ratio(Foreground, Background, Ratio),
    Ratio >= 4.5.

fudge_contrast_luminance(Color, Luminance) :-
    fudge_contrast_rgb(Color, R, G, B),
    fudge_contrast_linear_channel(R, RLinear),
    fudge_contrast_linear_channel(G, GLinear),
    fudge_contrast_linear_channel(B, BLinear),
    Luminance is 0.2126 * RLinear + 0.7152 * GLinear + 0.0722 * BLinear.

fudge_contrast_rgb(Color, R, G, B) :-
    fudge_contrast_hex_body(Color, Body),
    string_length(Body, 6),
    !,
    fudge_contrast_hex_byte(Body, 0, R),
    fudge_contrast_hex_byte(Body, 2, G),
    fudge_contrast_hex_byte(Body, 4, B).
fudge_contrast_rgb(Color, R, G, B) :-
    fudge_contrast_hex_body(Color, Body),
    string_length(Body, 3),
    fudge_contrast_hex_digit(Body, 0, RN),
    fudge_contrast_hex_digit(Body, 1, GN),
    fudge_contrast_hex_digit(Body, 2, BN),
    R is RN * 17,
    G is GN * 17,
    B is BN * 17.

fudge_contrast_hex_body(Color, Body) :-
    fudge_contrast_text(Color, Raw),
    normalize_space(string(Trimmed), Raw),
    string_lower(Trimmed, Lower),
    (   sub_string(Lower, 0, 1, _, "#")
    ->  sub_string(Lower, 1, _, 0, Body)
    ;   Body = Lower
    ).

fudge_contrast_text(Value, Text) :-
    string(Value),
    !,
    Text = Value.
fudge_contrast_text(Value, Text) :-
    atom(Value),
    !,
    atom_string(Value, Text).
fudge_contrast_text(Value, Text) :-
    term_string(Value, Text).

fudge_contrast_hex_byte(Body, Start, Byte) :-
    Next is Start + 1,
    fudge_contrast_hex_digit(Body, Start, High),
    fudge_contrast_hex_digit(Body, Next, Low),
    Byte is High * 16 + Low.

fudge_contrast_hex_digit(Body, Index, Value) :-
    sub_string(Body, Index, 1, _, Digit),
    string_codes(Digit, [Code]),
    (   Code >= 0'0,
        Code =< 0'9
    ->  Value is Code - 0'0
    ;   Code >= 0'a,
        Code =< 0'f
    ->  Value is Code - 0'a + 10
    ).

fudge_contrast_linear_channel(Byte, Linear) :-
    Srgb is Byte / 255,
    (   Srgb =< 0.03928
    ->  Linear is Srgb / 12.92
    ;   Linear is ((Srgb + 0.055) / 1.055) ** 2.4
    ).
