"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
// Technically this should be shades and tints, but for simplicity we'll call everything a shade.
/* This utility module is used with theming. Given a color to shade, whether the theme is inverted (i.e. is a dark color),
 * and the desired shade enum, this will return an appropriate shade of that color.
 */
var colors_1 = require("./colors");
var Colors = require("./colors");
var Utilities_1 = require("../../Utilities");
// Soften: to get closer to the background color's luminance (softening with a white background would be lightening, with black it'd be darkening)
// Strongen: opposite of soften
// Luminance multiplier constants for generating shades of a given color
var WhiteShadeTableBG = [.973, .957, .918, .855, .816, .784, .651, .463]; // white bg
var BlackTintTableBG = [.463, .55, .651, .784, .816, .855, .918, .957]; // black bg
var WhiteShadeTable = [.463, .651, .784, .816, .855, .918, .957, .973]; // white fg
var BlackTintTable = [.463, .55, .651, .784, .816, .855, .918, .957]; // black fg
var LumTintTable = [.12, .23, .34, .45, .56, .67, .78, .89]; // light (strongen all)
var LumShadeTable = [.89, .78, .67, .56, .45, .34, .23, .12]; // dark (soften all)
var ColorTintTable = [.07, .18, .436, .751, .956]; // default soften
var ColorShadeTable = [.90, .64, .550]; // default strongen
// If the given shade's luminance is below/above these values, we'll swap to using the White/Black tables above
var LowLuminanceThreshold = 0.2;
var HighLuminanceThreshold = 0.8;
/** Shades of a given color, from softest to strongest. */
var Shade;
(function (Shade) {
    Shade[Shade["Unshaded"] = 0] = "Unshaded";
    Shade[Shade["Shade1"] = 1] = "Shade1";
    Shade[Shade["Shade2"] = 2] = "Shade2";
    Shade[Shade["Shade3"] = 3] = "Shade3";
    Shade[Shade["Shade4"] = 4] = "Shade4";
    Shade[Shade["Shade5"] = 5] = "Shade5";
    Shade[Shade["Shade6"] = 6] = "Shade6";
    Shade[Shade["Shade7"] = 7] = "Shade7";
    Shade[Shade["Shade8"] = 8] = "Shade8";
    // remember to update isValidShade()!
})(Shade = exports.Shade || (exports.Shade = {}));
/**
 * Returns true if the argument is a valid Shade value
 * @param {Shade} shade The Shade value to validate.
 */
function isValidShade(shade) {
    'use strict';
    return (typeof shade === 'number') && (shade >= Shade.Unshaded) && (shade <= Shade.Shade8);
}
exports.isValidShade = isValidShade;
function _isBlack(color) {
    return color.r === 0 && color.g === 0 && color.b === 0;
}
function _isWhite(color) {
    return color.r === colors_1.MAX_COLOR_RGBA && color.g === colors_1.MAX_COLOR_RGBA && color.b === colors_1.MAX_COLOR_RGBA;
}
function _darken(hsl, factor) {
    return {
        h: hsl.h,
        s: hsl.s,
        l: hsl.l * factor
    };
}
function _lighten(hsl, factor) {
    return {
        h: hsl.h,
        s: hsl.s,
        l: hsl.l * factor + (100 * (1 - factor))
    };
}
function isDark(color) {
    return Colors.hsv2hsl(color.h, color.s, color.v).l < 50;
}
exports.isDark = isDark;
/**
 * Given a color and a shade specification, generates the requested shade of the color.
 * Logic:
 * if white
 *  darken via tables defined above
 * if black
 *  lighten
 * if light
 *  strongen
 * if dark
 *  soften
 * else default
 *  soften or strongen depending on shade#
 * @param {IColor} color The base color whose shade is to be computed
 * @param {Shade} shade The shade of the base color to compute
 * @param {Boolean} isInverted Default false. Whether the given theme is inverted (reverse strongen/soften logic)
 */
function getShade(color, shade, isInverted) {
    'use strict';
    if (isInverted === void 0) { isInverted = false; }
    if (!color) {
        return null;
    }
    if (shade === Shade.Unshaded || !isValidShade(shade)) {
        return color;
    }
    var hsl = Colors.hsv2hsl(color.h, color.s, color.v);
    var tableIndex = shade - 1;
    var _soften = _lighten;
    var _strongen = _darken;
    if (isInverted) {
        // tableIndex = LumTintTable.length - 1 - tableIndex;
        _soften = _darken;
        _strongen = _lighten;
    }
    if (_isWhite(color)) {
        hsl = _darken(hsl, WhiteShadeTable[tableIndex]);
    }
    else if (_isBlack(color)) {
        hsl = _lighten(hsl, BlackTintTable[tableIndex]);
    }
    else if (hsl.l / 100 > HighLuminanceThreshold) {
        hsl = _strongen(hsl, LumShadeTable[tableIndex]);
    }
    else if (hsl.l / 100 < LowLuminanceThreshold) {
        hsl = _soften(hsl, LumTintTable[tableIndex]);
    }
    else {
        if (tableIndex < ColorTintTable.length) {
            hsl = _soften(hsl, ColorTintTable[tableIndex]);
        }
        else {
            hsl = _strongen(hsl, ColorShadeTable[tableIndex - ColorTintTable.length]);
        }
    }
    return Colors.getColorFromRGBA(Utilities_1.assign(Colors.hsl2rgb(hsl.h, hsl.s, hsl.l), { a: color.a }));
}
exports.getShade = getShade;
// Background shades/tints are generated differently. The provided color will be guaranteed
//   to be the darkest or lightest one. If it is <50% luminance, it will always be the darkest,
//   otherwise it will always be the lightest.
function getBackgroundShade(color, shade, isInverted) {
    'use strict';
    if (isInverted === void 0) { isInverted = false; }
    if (!color) {
        return null;
    }
    if (shade === Shade.Unshaded || !isValidShade(shade)) {
        return color;
    }
    var hsl = Colors.hsv2hsl(color.h, color.s, color.v);
    var tableIndex = shade - 1;
    if (!isInverted) {
        hsl = _darken(hsl, WhiteShadeTableBG[tableIndex]);
    }
    else {
        hsl = _lighten(hsl, BlackTintTableBG[BlackTintTable.length - 1 - tableIndex]);
    }
    return Colors.getColorFromRGBA(Utilities_1.assign(Colors.hsl2rgb(hsl.h, hsl.s, hsl.l), { a: color.a }));
}
exports.getBackgroundShade = getBackgroundShade;
/* Calculates the contrast ratio between two colors. Used for verifying
 * color pairs meet minimum accessibility requirements.
 * See: https://www.w3.org/TR/WCAG20/ section 1.4.3
 */
function getContrastRatio(color1, color2) {
    // Formula defined by: http://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html#contrast-ratiodef
    // relative luminance: http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
    /* calculate the intermediate value needed to calculating relative luminance */
    function _getThing(x) {
        if (x <= .03928) {
            return x / 12.92;
        }
        else {
            return Math.pow((x + .055) / 1.055, 2.4);
        }
    }
    var r1 = _getThing(color1.r / colors_1.MAX_COLOR_RGBA);
    var g1 = _getThing(color1.g / colors_1.MAX_COLOR_RGBA);
    var b1 = _getThing(color1.b / colors_1.MAX_COLOR_RGBA);
    var L1 = (.2126 * r1) + (.7152 * g1) + (.0722 * b1); // relative luminance of first color
    L1 += .05;
    var r2 = _getThing(color2.r / colors_1.MAX_COLOR_RGBA);
    var g2 = _getThing(color2.g / colors_1.MAX_COLOR_RGBA);
    var b2 = _getThing(color2.b / colors_1.MAX_COLOR_RGBA);
    var L2 = (.2126 * r2) + (.7152 * g2) + (.0722 * b2); // relative luminance of second color
    L2 += .05;
    // return the lighter color divided by darker
    return L1 / L2 > 1 ?
        L1 / L2 : L2 / L1;
}
exports.getContrastRatio = getContrastRatio;
//# sourceMappingURL=shades.js.map