import numpy as np
import pandas as pd

import holoviews as hv
from holoviews.testing import assert_data_equal

from ...utils import LoggingComparison
from .test_plot import TestMPLPlot, mpl_renderer


class TestAreaPlot(LoggingComparison, TestMPLPlot):
    def test_area_padding_square(self):
        area = hv.Area([(1, 1), (2, 2), (3, 3)]).opts(padding=0.1)
        plot = mpl_renderer.get_plot(area)
        x_range, y_range = plot.handles["axis"].get_xlim(), plot.handles["axis"].get_ylim()
        assert x_range[0] == 0.8
        assert x_range[1] == 3.2
        assert y_range[0] == 0
        assert y_range[1] == 3.2

    def test_area_padding_square_per_axis(self):
        area = hv.Area([(1, 1), (2, 2), (3, 3)]).opts(padding=((0, 0.1), (0.1, 0.2)))
        plot = mpl_renderer.get_plot(area)
        x_range, y_range = plot.handles["axis"].get_xlim(), plot.handles["axis"].get_ylim()
        assert x_range[0] == 1
        assert x_range[1] == 3.2
        assert y_range[0] == 0
        assert y_range[1] == 3.4

    def test_area_with_lower_vdim(self):
        area = hv.Area([(1, 0.5, 1), (2, 1.5, 2), (3, 2.5, 3)], vdims=["y", "y2"]).opts(
            padding=0.1
        )
        plot = mpl_renderer.get_plot(area)
        x_range, y_range = plot.handles["axis"].get_xlim(), plot.handles["axis"].get_ylim()
        assert x_range[0] == 0.8
        assert x_range[1] == 3.2
        assert y_range[0] == 0.25
        assert y_range[1] == 3.25

    def test_area_padding_negative(self):
        area = hv.Area([(1, -1), (2, -2), (3, -3)]).opts(padding=0.1)
        plot = mpl_renderer.get_plot(area)
        x_range, y_range = plot.handles["axis"].get_xlim(), plot.handles["axis"].get_ylim()
        assert x_range[0] == 0.8
        assert x_range[1] == 3.2
        assert y_range[0] == -3.2
        assert y_range[1] == 0

    def test_area_padding_mixed(self):
        area = hv.Area([(1, 1), (2, -2), (3, 3)]).opts(padding=0.1)
        plot = mpl_renderer.get_plot(area)
        x_range, y_range = plot.handles["axis"].get_xlim(), plot.handles["axis"].get_ylim()
        assert x_range[0] == 0.8
        assert x_range[1] == 3.2
        assert y_range[0] == -2.5
        assert y_range[1] == 3.5

    def test_area_padding_hard_range(self):
        area = hv.Area([(1, 1), (2, 2), (3, 3)]).redim.range(y=(0, 4)).opts(padding=0.1)
        plot = mpl_renderer.get_plot(area)
        x_range, y_range = plot.handles["axis"].get_xlim(), plot.handles["axis"].get_ylim()
        assert x_range[0] == 0.8
        assert x_range[1] == 3.2
        assert y_range[0] == 0
        assert y_range[1] == 4

    def test_area_padding_soft_range(self):
        area = hv.Area([(1, 1), (2, 2), (3, 3)]).redim.soft_range(y=(0, 3.5)).opts(padding=0.1)
        plot = mpl_renderer.get_plot(area)
        x_range, y_range = plot.handles["axis"].get_xlim(), plot.handles["axis"].get_ylim()
        assert x_range[0] == 0.8
        assert x_range[1] == 3.2
        assert y_range[0] == 0
        assert y_range[1] == 3.5

    def test_area_padding_nonsquare(self):
        area = hv.Area([(1, 1), (2, 2), (3, 3)]).opts(padding=0.1, aspect=2)
        plot = mpl_renderer.get_plot(area)
        x_range, y_range = plot.handles["axis"].get_xlim(), plot.handles["axis"].get_ylim()
        assert x_range[0] == 0.9
        assert x_range[1] == 3.1
        assert y_range[0] == 0
        assert y_range[1] == 3.2

    def test_area_padding_logx(self):
        area = hv.Area([(1, 1), (2, 2), (3, 3)]).opts(padding=0.1, logx=True)
        plot = mpl_renderer.get_plot(area)
        x_range, y_range = plot.handles["axis"].get_xlim(), plot.handles["axis"].get_ylim()
        assert x_range[0] == 0.89595845984076228
        assert x_range[1] == 3.3483695221017129
        assert y_range[0] == 0
        assert y_range[1] == 3.2

    def test_area_padding_logy(self):
        area = hv.Area([(1, 1), (2, 2), (3, 3)]).opts(padding=0.1, logy=True)
        plot = mpl_renderer.get_plot(area)
        x_range, y_range = plot.handles["axis"].get_xlim(), plot.handles["axis"].get_ylim()
        assert x_range[0] == 0.8
        assert x_range[1] == 3.2
        assert y_range[0] == 0.01
        assert y_range[1] == 3.3483695221017129
        self.log_handler.assert_contains(
            "WARNING", "Logarithmic axis range encountered value less than"
        )

    def test_area_stack_vdims(self):
        df = pd.DataFrame({"x": [1, 2, 3], "y_1": [1, 2, 3], "y_2": [6, 4, 2], "y_3": [8, 1, 2]})
        overlay = hv.Overlay(
            [hv.Area(df, kdims="x", vdims=col, label=col) for col in ["y_1", "y_2", "y_3"]]
        )
        plot = hv.Area.stack(overlay)
        baselines = [np.array([0, 0, 0]), np.array([1.0, 2.0, 3.0]), np.array([7.0, 6.0, 5.0])]
        for n, baseline in zip(plot.data, baselines, strict=True):
            assert_data_equal(plot.data[n].data.Baseline.to_numpy(), baseline)
