"""Defines the unit tests for the :mod:`colour.plotting.common` module."""

import matplotlib.pyplot as plt
import numpy as np
import os
import shutil
import tempfile
import unittest
from functools import partial
from matplotlib.pyplot import Axes, Figure

import colour
from colour.colorimetry import SDS_ILLUMINANTS
from colour.io import read_image
from colour.models import RGB_COLOURSPACES, XYZ_to_sRGB, gamma_function
from colour.plotting import ColourSwatch
from colour.plotting import (
    colour_style,
    override_style,
    XYZ_to_plotting_colourspace,
    colour_cycle,
    artist,
    camera,
    render,
    label_rectangles,
    uniform_axes3d,
    filter_passthrough,
    filter_RGB_colourspaces,
    filter_cmfs,
    filter_illuminants,
    filter_colour_checkers,
    update_settings_collection,
    plot_single_colour_swatch,
    plot_multi_colour_swatches,
    plot_single_function,
    plot_multi_functions,
    plot_image,
)
from colour.utilities import attest

__author__ = "Colour Developers"
__copyright__ = "Copyright 2013 Colour Developers"
__license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause"
__maintainer__ = "Colour Developers"
__email__ = "colour-developers@colour-science.org"
__status__ = "Production"

__all__ = [
    "TestColourStyle",
    "TestOverrideStyle",
    "TestXyzToPlottingColourspace",
    "TestColourCycle",
    "TestArtist",
    "TestCamera",
    "TestRender",
    "TestLabelRectangles",
    "TestUniformAxes3d",
    "TestFilterPassthrough",
    "TestFilterRgbColourspaces",
    "TestFilterCmfs",
    "TestFilterIlluminants",
    "TestFilterColourCheckers",
    "TestUpdateSettingsCollection",
    "TestPlotSingleColourSwatch",
    "TestPlotMultiColourSwatches",
    "TestPlotSingleFunction",
    "TestPlotMultiFunctions",
    "TestPlotImage",
]


class TestColourStyle(unittest.TestCase):
    """
    Define :func:`colour.plotting.common.colour_style` definition unit tests
    methods.
    """

    def test_colour_style(self):
        """Test :func:`colour.plotting.common.colour_style` definition."""

        self.assertIsInstance(colour_style(use_style=False), dict)


class TestOverrideStyle(unittest.TestCase):
    """
    Define :func:`colour.plotting.common.override_style` definition unit tests
    methods.
    """

    def test_override_style(self):
        """Test :func:`colour.plotting.common.override_style` definition."""

        text_color = plt.rcParams["text.color"]
        try:

            @override_style(**{"text.color": "red"})
            def test_text_color_override():
                """Test :func:`colour.plotting.common.override_style` definition."""

                attest(plt.rcParams["text.color"] == "red")

            test_text_color_override()
        finally:
            plt.rcParams["text.color"] = text_color


class TestXyzToPlottingColourspace(unittest.TestCase):
    """
    Define :func:`colour.plotting.common.XYZ_to_plotting_colourspace`
    definition unit tests methods.
    """

    def test_XYZ_to_plotting_colourspace(self):
        """
        Test :func:`colour.plotting.common.XYZ_to_plotting_colourspace`
        definition.
        """

        XYZ = np.random.random(3)
        np.testing.assert_almost_equal(
            XYZ_to_sRGB(XYZ), XYZ_to_plotting_colourspace(XYZ), decimal=7
        )


class TestColourCycle(unittest.TestCase):
    """
    Define :func:`colour.plotting.common.colour_cycle` definition unit tests
    methods.
    """

    def test_colour_cycle(self):
        """Test :func:`colour.plotting.common.colour_cycle` definition."""

        cycler = colour_cycle()

        np.testing.assert_almost_equal(
            next(cycler),
            np.array([0.95686275, 0.26274510, 0.21176471, 1.00000000]),
            decimal=7,
        )

        np.testing.assert_almost_equal(
            next(cycler),
            np.array([0.61582468, 0.15423299, 0.68456747, 1.00000000]),
            decimal=7,
        )

        np.testing.assert_almost_equal(
            next(cycler),
            np.array([0.25564014, 0.31377163, 0.70934256, 1.00000000]),
            decimal=7,
        )

        cycler = colour_cycle(colour_cycle_map="viridis")

        np.testing.assert_almost_equal(
            next(cycler),
            np.array([0.26700400, 0.00487400, 0.32941500, 1.00000000]),
            decimal=7,
        )


class TestArtist(unittest.TestCase):
    """
    Define :func:`colour.plotting.common.artist` definition unit tests
    methods.
    """

    def test_artist(self):
        """Test :func:`colour.plotting.common.artist` definition."""

        figure_1, axes_1 = artist()

        self.assertIsInstance(figure_1, Figure)
        self.assertIsInstance(axes_1, Axes)

        _figure_2, axes_2 = artist(axes=axes_1, uniform=True)
        self.assertIs(axes_1, axes_2)

        figure_3, _axes_3 = artist(uniform=True)
        self.assertEqual(figure_3.get_figwidth(), figure_3.get_figheight())


class TestCamera(unittest.TestCase):
    """
    Define :func:`colour.plotting.common.camera` definition unit tests
    methods.
    """

    def test_camera(self):
        """Test :func:`colour.plotting.common.camera` definition."""

        figure, _axes = artist()
        axes = figure.add_subplot(111, projection="3d")

        _figure, axes = camera(axes=axes, elevation=45, azimuth=90)

        self.assertEqual(axes.elev, 45)
        self.assertEqual(axes.azim, 90)


class TestRender(unittest.TestCase):
    """
    Define :func:`colour.plotting.common.render` definition unit tests
    methods.
    """

    def setUp(self):
        """Initialise the common tests attributes."""

        self._temporary_directory = tempfile.mkdtemp()

    def tearDown(self):
        """After tests actions."""

        shutil.rmtree(self._temporary_directory)

    def test_render(self):
        """Test :func:`colour.plotting.common.render` definition."""

        figure, axes = artist()

        render(
            figure=figure,
            axes=axes,
            standalone=False,
            aspect="equal",
            axes_visible=True,
            bounding_box=[0, 1, 0, 1],
            tight_layout=False,
            legend=True,
            legend_columns=2,
            transparent_background=False,
            title="Render Unit Test",
            wrap_title=True,
            x_label="x Label",
            y_label="y Label",
            x_ticker=False,
            y_ticker=False,
        )

        render(standalone=True)

        render(
            filename=os.path.join(self._temporary_directory, "render.png"),
            axes_visible=False,
        )


class TestLabelRectangles(unittest.TestCase):
    """
    Define :func:`colour.plotting.common.label_rectangles` definition unit
    tests methods.
    """

    def test_label_rectangles(self):
        """Test :func:`colour.plotting.common.label_rectangles` definition."""

        figure, axes = artist()

        samples = np.linspace(0, 1, 10)

        _figure, axes = label_rectangles(
            samples, axes.bar(samples, 1), figure=figure, axes=axes
        )

        self.assertEqual(len(axes.texts), len(samples))


class TestUniformAxes3d(unittest.TestCase):
    """
    Define :func:`colour.plotting.common.uniform_axes3d` definition unit tests