import branca.utilities as ut
import os
from pathlib import Path
import json
import pytest


rootpath = Path(os.path.dirname(os.path.abspath(__file__))) / '..' / 'branca'
color_brewer_minimum_n = 3
color_brewer_maximum_n = 253  # Why this limitation @ branca/utilities.py:108 ?


# Loads schemes and their meta-data
with open(rootpath / '_schemes.json') as f:
    schemes = json.loads(f.read())
with open(rootpath / 'scheme_info.json') as f:
    scheme_info = json.loads(f.read())
with open(rootpath / 'scheme_base_codes.json') as f:
    core_schemes = json.loads(f.read())['codes']


def test_color_brewer_base():
    scheme = ut.color_brewer('YlGnBu', 9)
    assert scheme == [
        '#ffffd9', '#edf8b1', '#c7e9b4',
        '#7fcdbb', '#41b6c4', '#1d91c0',
        '#225ea8', '#253494', '#081d58'
    ]


def test_color_brewer_reverse():
    scheme = ut.color_brewer('YlGnBu')
    scheme_r = ut.color_brewer('YlGnBu_r')
    assert scheme[::-1] == scheme_r


def test_color_brewer_extendability():
    """
    The non-qualitative schemes should be extendable.

    :see https://github.com/python-visualization/branca/issues/104
    :see https://github.com/python-visualization/branca/issues/114

    Moreover, the following error was not reported via issues:
    * TypeError in the linear_gradient function when trying to extend any scheme. Indeed, in color_brewer, the key searched in the scheme database was not found, thus, it was passing `None` instead of a real scheme vector to linear_gradient.
    """
    for sname in core_schemes:
        for n in range(color_brewer_minimum_n, color_brewer_maximum_n+1):
            try:
                scheme = ut.color_brewer(sname, n=n)
            except Exception as e:
                if scheme_info[sname] == 'Qualitative' and isinstance(e, ValueError):
                    continue
                raise

            assert len(scheme) == n

            # When we try to extend a scheme, the reverse is not always the exact reverse vector of the original one.
            # Thus, we do not test this property!
            _ = ut.color_brewer(sname + '_r', n=n)


def test_color_avoid_unexpected_error():
    """
    We had unexpected errors by providing some scheme name with unexpected value of `n`.
    This function tests them.

    Identified errors which was not reported via issues:
    * The scheme 'viridis' was not in the base_codes JSON;
    * Multiple scheme hadn't any metadata in scheme_info JSON;
    * When a `n` value provided to `color_scheme` was a float, it tried to select an unknown scheme without raising the right Exception type.
    """

    # Verify that every scheme has is present in base codes
    scheme_names = set()
    for sname in schemes.keys():
        scheme_names.add(
            sname.split('_')[0]
        )
    assert scheme_names == set(core_schemes)

    # Verify that every scheme has a metadata
    assert scheme_names == set(scheme_info.keys())

    # Verify that every scheme can be generated in color_brewer using exotic value of `n`. Note that big but valid
    # values are generated by test_color_brewer_extendability.
    for sname in scheme_names:
        for n in [-10] + list(range(-1, color_brewer_minimum_n)) + list(range(color_brewer_maximum_n+1, color_brewer_maximum_n+10)):
            with pytest.raises(ValueError):
                ut.color_brewer(sname, n)
        for n in [str(color_brewer_minimum_n), float(color_brewer_minimum_n), 'abc']:
            with pytest.raises(TypeError):
                ut.color_brewer(sname, n)
