/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */
goog.module('goog.color.alphaTest');
goog.setTestOnly();

const alpha = goog.require('goog.color.alpha');
const testSuite = goog.require('goog.testing.testSuite');

testSuite({
  /**
   * @suppress {visibility} accessing private properties
   */
  testIsValidAlphaHexColor() {
    const goodAlphaHexColors = [
      '#ffffffff',
      '#ff781259',
      '#01234567',
      '#Ff003DaB',
      '#3CAF',
      '#abcdefab',
      '#3CAB',
    ];
    const badAlphaHexColors =
        ['#xxxxxxxx', '88990077', 'not_color', '#123456789', 'fffffgfg'];
    for (let i = 0; i < goodAlphaHexColors.length; i++) {
      assertTrue(
          goodAlphaHexColors[i],
          alpha.isValidAlphaHexColor_(goodAlphaHexColors[i]));
    }
    for (let i = 0; i < badAlphaHexColors.length; i++) {
      assertFalse(
          badAlphaHexColors[i],
          alpha.isValidAlphaHexColor_(badAlphaHexColors[i]));
    }
  },

  /**
   * @suppress {visibility} accessing private properties
   */
  testIsValidRgbaColor() {
    const goodRgbaColors = [
      'rgba(1, 20, 234, 1)',
      'rgba(255,127, 0,1)',
      'rgba(0,0,255,0.5)',
      '(255, 26, 75, 0.2)',
      'RGBA(0, 55, 0, 0.6)',
      'rGbA(0, 200, 0, 0.123456789)',
      'rgba(255, 0, 0, 1.0)',
      '  rgba(1,\t2,\n3,\r0.2) ',
      'rgba(255, 0, 0, .2)',
    ];
    const badRgbaColors = [
      '(255, 0, 0)',
      '(2555,0,0, 0)',
      '(1,2,3,4,5)',
      'rgba(1,20,)',
      'RGBA(20,20,20,)',
      'RGBA',
      'rgba(255, 0, 0, 1.1)',
      'rgba(255, 0, 0, 1.00001)',
      'rgba(255, 0, 0, 1.)',
      'rgba(01, 0, 0, 1)',
    ];
    for (let i = 0; i < goodRgbaColors.length; i++) {
      assertEquals(
          goodRgbaColors[i], 4,
          alpha.isValidRgbaColor_(goodRgbaColors[i]).length);
    }
    for (let i = 0; i < badRgbaColors.length; i++) {
      assertEquals(
          badRgbaColors[i], 0,
          alpha.isValidRgbaColor_(badRgbaColors[i]).length);
    }
  },

  /**
   * @suppress {visibility} accessing private properties
   */
  testIsValidHslaColor() {
    const goodHslaColors = [
      'hsla(120, 0%, 0%, 1)',
      'hsla(360,20%,0%,1)',
      'hsla(0,0%,50%,0.5)',
      'HSLA(0, 55%, 0%, 0.6)',
      'hsla(0, 85%, 0%, 0.123456789)',
      'hsla(120, 0%, 0%, 1.0)',
      '  hsla(120,\t0%,\n0%,\r0.2) ',
    ];
    const badHslaColors = [
      '(255, 0, 0, 0)',
      'hsla(2555,0,0, 0)',
      'hsla(1,2,3,4,5)',
      'hsla(1,20,)',
      'HSLA(20,20,20,)',
      'hsla(255, 0, 0, 1.1)',
      'hsla(255, 0, 0, 1.00001)',
      'HSLA',
      'hsla(255, 0, 0, 1.)',
    ];
    for (let i = 0; i < goodHslaColors.length; i++) {
      assertEquals(
          goodHslaColors[i], 4,
          alpha.isValidHslaColor_(goodHslaColors[i]).length);
    }
    for (let i = 0; i < badHslaColors.length; i++) {
      assertEquals(
          badHslaColors[i], 0,
          alpha.isValidHslaColor_(badHslaColors[i]).length);
    }
  },

  testParse() {
    const colors = [
      'rgba(15, 250, 77, 0.5)',
      '(127, 127, 127, 0.8)',
      '#ffeeddaa',
      '12345678',
      'hsla(160, 50%, 90%, 0.2)',
    ];
    const parsed = colors.map(alpha.parse);
    assertEquals('rgba', parsed[0].type);
    assertEquals(alpha.rgbaToHex(15, 250, 77, 0.5), parsed[0].hex);
    assertEquals('rgba', parsed[1].type);
    assertEquals(alpha.rgbaToHex(127, 127, 127, 0.8), parsed[1].hex);
    assertEquals('hex', parsed[2].type);
    assertEquals('#ffeeddaa', parsed[2].hex);
    assertEquals('hex', parsed[3].type);
    assertEquals('#12345678', parsed[3].hex);
    assertEquals('hsla', parsed[4].type);
    assertEquals('#d9f2ea33', parsed[4].hex);

    const e = assertThrows(
        'not_color is not a valid color string',
        goog.partial(alpha.parse, 'not_color'));
    assertContains(
        'Error processing not_color', 'is not a valid color string', e.message);
  },

  testHexToRgba() {
    const testColors = [
      ['#B0FF2D66', [176, 255, 45, 0.4]],
      ['#b26e5fcc', [178, 110, 95, 0.8]],
      ['#66f3', [102, 102, 255, 0.2]],
    ];

    for (let i = 0; i < testColors.length; i++) {
      const r = alpha.hexToRgba(testColors[i][0]);
      const t = testColors[i][1];

      assertEquals('Red channel should match.', t[0], r[0]);
      assertEquals('Green channel should match.', t[1], r[1]);
      assertEquals('Blue channel should match.', t[2], r[2]);
      assertEquals('Alpha channel should match.', t[3], r[3]);
    }

    const badColors = ['', '#g00', 'some words'];
    for (let i = 0; i < badColors.length; i++) {
      const e = assertThrows(goog.partial(alpha.hexToRgba, badColors[i]));
      assertEquals(
          '\'' + badColors[i] + '\' is not a valid alpha hex color', e.message);
    }
  },

  testHexToRgbaStyle() {
    assertEquals('rgba(255,0,0,1)', alpha.hexToRgbaStyle('#ff0000ff'));
    assertEquals('rgba(206,206,206,0.8)', alpha.hexToRgbaStyle('#cecececc'));
    assertEquals('rgba(51,204,170,0.2)', alpha.hexToRgbaStyle('#3CA3'));
    assertEquals('rgba(1,2,3,0.016)', alpha.hexToRgbaStyle('#01020304'));
    assertEquals('rgba(255,255,0,0.333)', alpha.hexToRgbaStyle('#FFFF0055'));

    const badHexColors = ['#12345', null, undefined, '#.1234567890'];
    for (let i = 0; i < badHexColors.length; ++i) {
      const e = assertThrows(
          badHexColors[i] + ' is an invalid hex color',
          goog.partial(alpha.hexToRgbaStyle, badHexColors[i]));
      assertEquals(
          '\'' + badHexColors[i] + '\' is not a valid alpha hex color',
          e.message);
    }
  },

  testRgbaToHex() {
    assertEquals('#af13ffff', alpha.rgbaToHex(175, 19, 255, 1));
    assertEquals('#357cf099', alpha.rgbaToHex(53, 124, 240, 0.6));
    const badRgba = [
      [-1, -1, -1, -1],
      [0, 0, 0, 2],
      ['a', 'b', 'c', 'd'],
      [undefined, 5, 5, 5],
    ];
    for (let i = 0; i < badRgba.length; ++i) {
      const e = assertThrows(
          badRgba[i] + ' is not a valid rgba color',
          goog.partial(alpha.rgbaArrayToHex, badRgba[i]));
      assertContains('is not a valid RGBA color', e.message);
    }
  },

  testRgbaToRgbaStyle() {
    const testColors = [
      [[175, 19, 255, 1], 'rgba(175,19,255,1)'],
      [[53, 124, 240, .6], 'rgba(53,124,240,0.6)'],
      [[10, 20, 30, .1234567], 'rgba(10,20,30,0.123)'],
      [[20, 30, 40, 1 / 3], 'rgba(20,30,40,0.333)'],
    ];

    for (let i = 0; i < testColors.length; ++i) {
      const r = alpha.rgbaToRgbaStyle(
          testColors[i][0][0], testColors[i][0][1], testColors[i][0][2],
          testColors[i][0][3]);
      assertEquals(testColors[i][1], r);
    }

    const badColors = [[0, 0, 0, 2]];
    for (let i = 0; i < badColors.length; ++i) {
      const e = assertThrows(goog.partial(
          alpha.rgbaToRgbaStyle, badColors[i][0], badColors[i][1],
          badColors[i][2], badColors[i][3]));

      assertContains('is not a valid RGBA color', e.message);
    }

    // Loop through all bad color values and ensure they fail in each channel.
    const badValues = [-1, 300, 'a', undefined, null, NaN];
    const color = [0, 0, 0, 0];
    for (let i = 0; i < badValues.length; ++i) {
      for (let channel = 0; channel < color.length; ++channel) {
        color[channel] = badValues[i];
        const e = assertThrows(
            `${color} is not a valid rgba color`,
            goog.partial(alpha.rgbaToRgbaStyle, color));
        assertContains('is not a valid RGBA color', e.message);

        color[channel] = 0;
      }
    }
  },

  testRgbaArrayToRgbaStyle() {
    const testColors = [
      [[175, 19, 255, 1], 'rgba(175,19,255,1)'],
      [[53, 124, 240, .6], 'rgba(53,124,240,0.6)'],
    ];

    for (let i = 0; i < testColors.length; ++i) {
      const r = alpha.rgbaArrayToRgbaStyle(testColors[i][0]);
      assertEquals(testColors[i][1], r);
    }

    const badColors = [[0, 0, 0, 2]];
    for (let i = 0; i < badColors.length; ++i) {
      const e =
          assertThrows(goog.partial(alpha.rgbaArrayToRgbaStyle, badColors[i]));

      assertContains('is not a valid RGBA color', e.message);
    }

    // Loop through all bad color values and ensure they fail in each channel.
    const badValues = [-1, 300, 'a', undefined, null, NaN];
    const color = [0, 0, 0, 0];
    for (let i = 0; i < badValues.length; ++i) {
      for (let channel = 0; channel < color.length; ++channel) {
        color[channel] = badValues[i];
        const e = assertThrows(
            `${color} is not a valid rgba color`,
            goog.partial(alpha.rgbaToRgbaStyle, color));
        assertContains('is not a valid RGBA color', e.message);

        color[channel] = 0;
      }
    }
  },

  testRgbaArrayToHsla() {
    const opaqueBlueRgb = [0, 0, 255, 1];
    const opaqueBlueHsl = alpha.rgbaArrayToHsla(opaqueBlueRgb);
    assertArrayEquals(
        'Conversion from RGBA to HSLA should be as expected', [240, 1, 0.5, 1],
        opaqueBlueHsl);

    const nearlyOpaqueYellowRgb = [255, 190, 0, 0.7];
    const nearlyOpaqueYellowHsl = alpha.rgbaArrayToHsla(nearlyOpaqueYellowRgb);
    assertArrayEquals(
        'Conversion from RGBA to HSLA should be as expected', [45, 1, 0.5, 0.7],
        nearlyOpaqueYellowHsl);

    const transparentPurpleRgb = [180, 0, 255, 0];
    const transparentPurpleHsl = alpha.rgbaArrayToHsla(transparentPurpleRgb);
    assertArrayEquals(
        'Conversion from RGBA to HSLA should be as expected', [282, 1, 0.5, 0],
        transparentPurpleHsl);
  },

  /**
   * @suppress {visibility} accessing private properties
   */
  testNormalizeAlphaHex() {
    const compactColor = '#abcd';
    const normalizedCompactColor = alpha.normalizeAlphaHex_(compactColor);
    assertEquals(
        'The color should have been normalized to the right length',
        '#aabbccdd', normalizedCompactColor);

    const uppercaseColor = '#ABCDEF01';
    const normalizedUppercaseColor = alpha.normalizeAlphaHex_(uppercaseColor);
    assertEquals(
        'The color should have been normalized to lowercase', '#abcdef01',
        normalizedUppercaseColor);
  },

  testHsvaArrayToHex() {
    const opaqueSkyBlueHsv = [190, 1, 255, 1];
    const opaqueSkyBlueHex = alpha.hsvaArrayToHex(opaqueSkyBlueHsv);
    assertEquals(
        'The HSVA array should have been properly converted to hex',
        '#00d5ffff', opaqueSkyBlueHex);

    const halfTransparentPinkHsv = [300, 1, 255, 0.5];
    const halfTransparentPinkHex = alpha.hsvaArrayToHex(halfTransparentPinkHsv);
    assertEquals(
        'The HSVA array should have been properly converted to hex',
        '#ff00ff7f', halfTransparentPinkHex);

    const transparentDarkTurquoiseHsv = [175, 1, 127, 0.5];
    const transparentDarkTurquoiseHex =
        alpha.hsvaArrayToHex(transparentDarkTurquoiseHsv);
    assertEquals(
        'The HSVA array should have been properly converted to hex',
        '#007f747f', transparentDarkTurquoiseHex);
  },

  testExtractHexColor() {
    const opaqueRed = '#ff0000ff';
    const red = alpha.extractHexColor(opaqueRed);
    assertEquals(
        'The hex part of the color should have been extracted correctly',
        '#ff0000', red);

    const halfOpaqueDarkGreenCompact = '#0507';
    const darkGreen = alpha.extractHexColor(halfOpaqueDarkGreenCompact);
    assertEquals(
        'The hex part of the color should have been extracted correctly',
        '#005500', darkGreen);
  },

  testExtractAlpha() {
    const colors = ['#ff0000ff', '#0507', '#ff000005'];
    const expectedOpacities = ['ff', '77', '05'];

    for (let i = 0; i < colors.length; i++) {
      const opacity = alpha.extractAlpha(colors[i]);
      assertEquals(
          'The alpha transparency should have been extracted correctly',
          expectedOpacities[i], opacity);
    }
  },

  testHslaArrayToRgbaStyle() {
    assertEquals(
        'rgba(102,255,102,0.5)',
        alpha.hslaArrayToRgbaStyle([120, 100, 70, 0.5]));
    assertEquals(
        'rgba(28,23,23,0.9)', alpha.hslaArrayToRgbaStyle([0, 10, 10, 0.9]));
  },

  /**
   * @suppress {visibility} accessing private properties
   */
  testRgbaStyleParsableResult() {
    const testColors = [
      [175, 19, 255, 1],
      [53, 124, 240, .6],
      [20, 30, 40, 0.3333333],
      [255, 255, 255, 0.7071067811865476],
    ];

    for (let i = 0, testColor; testColor = testColors[i]; i++) {
      const rgbaStyle = alpha.rgbaStyle_(testColor);
      const parsedColor = alpha.hexToRgba(alpha.parse(rgbaStyle).hex);
      assertEquals(testColor[0], parsedColor[0]);
      assertEquals(testColor[1], parsedColor[1]);
      assertEquals(testColor[2], parsedColor[2]);
      // Parsing keeps a 1/255 accuracy on the alpha channel.
      assertRoughlyEquals(testColor[3], parsedColor[3], 0.005);
    }
  },
});
