#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
import time
from random import randint

from colored import back, fore, style
from colored.hexadecimal import Hex
from colored.library import Library


def compare_with_expected(in_hex: str, expected: str) -> bool:
    """ Contribution by Fredrik Klasson. """
    hexad = Hex()
    nearest = hexad.find(in_hex)
    # Look up the matched hex value (e.g. xterm colors 10 and 46 are
    # the same hex value)
    match = Library.HEX_COLORS[nearest] == Library.HEX_COLORS[expected]

    e_str = '%s%s##%s' % (fore(expected), back(expected), style('reset'))
    n_str = '%s%s##%s' % (fore(nearest), back(nearest), style('reset'))
    print("%s: %s => %s = %s" % ('pass' if match else 'FAIL', in_hex, n_str, e_str))

    return match


def main() -> None:
    """Test the nearest matching hex color lookup.
    Contribution by Fredrik Klasson. """
    print('            Nearest   Expected')

    test_set = {
        '1': ('#7f0000', '#800000', '#810000'),
        '2': ('#007f00', '#008000', '#008100'),
        '4': ('#00007f', '#000080', '#000081'),
        '10': ('#00fe00', '#00ff00', '#01ff00'),
        '11': ('#fe0000', '#ff0000', '#ff0100'),
        '12': ('#0000fe', '#0000ff', '#0100ff'),
        '13': ('#feff00', '#ffff00', '#fffe00'),
        '14': ('#00feff', '#00ffff', '#00fffe'),
        '15': ('#ff00fe', '#ff00ff', '#ff01ff'),
        '16': ('#fffeff', '#ffffff', '#effeff'),
        '17': ('#010101', '#000000', '#010000'),
        '18': ('#1e346c', '#00005f', '#1e346c'),
    }

    all_ok = True
    for expected, color_set in test_set.items():
        for hex_code in color_set:
            if not compare_with_expected(hex_code, expected):
                all_ok = False

    print('-' * 78)

    try:
        T_sta = time.perf_counter()
        r_row = ''
        g_row = ''
        b_row = ''
        i_row = ''
        hexad = Hex()  # Instantiate Hex once here

        for c in range(0, 0xFF + 1, 16):
            # Red gradient
            hex_code = '#%02x0000' % (c,)
            nearest_ansi_r = hexad.find(hex_code)  # Get the ANSI code
            r_row += '%s%s#' % (fore(nearest_ansi_r), back(nearest_ansi_r))

            # Green gradient
            hex_code = '#00%02x00' % (c,)
            nearest_ansi_g = hexad.find(hex_code)  # Get the ANSI code
            g_row += '%s%s#' % (fore(nearest_ansi_g), back(nearest_ansi_g))

            # Blue gradient
            hex_code = '#0000%02x' % (c,)
            nearest_ansi_b = hexad.find(hex_code)  # Get the ANSI code
            b_row += '%s%s#' % (fore(nearest_ansi_b), back(nearest_ansi_b))

            # Grayscale gradient
            hex_code = '#' + ('%02x' % (c,)) * 3
            nearest_ansi_i = hexad.find(hex_code)  # Get the ANSI code
            i_row += '%s%s#' % (fore(nearest_ansi_i), back(nearest_ansi_i))

        print('%s%s %s%s %s%s %s%s' % (
            r_row, style('reset'), g_row, style('reset'), b_row, style('reset'), i_row, style('reset')))
        dT = time.perf_counter() - T_sta
        print('Lookup time: %0.4f s => %0.4f s/lookup' % (dT, dT / (2 * 4 * 0xFF)))
        print('-' * 78)
    except Exception as e:  # pylint: disable=[W0718]
        print('Whopsie, something %s-ish went wrong: %s' % (e.__class__.__name__, e))
        import traceback  # pylint: disable=[C0415]
        traceback.print_exc()
        all_ok = False

    # This is just for fun, almost... let's call it a
    # "non-deterministic check that it doesn't throw any exceptions"
    try:
        T_sta = time.perf_counter()
        hexad = Hex() # Re-instantiate Hex for this section
        for y in range(0, 20):
            for x in range(0, 30):
                rnd = randint(0, 0xffffff)
                hex_code = '#%06x' % (rnd,)
                hexinv = '#%06x' % (0xffffff - rnd,)

                # Get nearest ANSI codes for the random hex values
                nearest_hexinv_ansi = hexad.find(hexinv)
                nearest_hex_code_ansi = hexad.find(hex_code)

                print('%s%s::' % (fore(nearest_hexinv_ansi), back(nearest_hex_code_ansi)), end='')
            print('')
        print(style('reset'))
        dT = time.perf_counter() - T_sta
        print('Random test time: %0.4f s => %0.4f s/lookup' % (dT, dT / (30 * 20 * 2)))
        print('-' * 78)
    except Exception as e:  # pylint: disable=[W0718]
        print('Whopsie, something %s-ish went wrong: %s' % (e.__class__.__name__, e))
        import traceback
        traceback.print_exc()
        all_ok = False

    if not all_ok:
        sys.exit(1)


if __name__ == "__main__":
    main()
