import pytest
from gi.repository import Gtk

from gaphas.tool.zoom import Zoom, on_begin, on_scale_changed, zoom_tool


@pytest.fixture
def zoom_data(view):
    zoom_data = Zoom()
    zoom_data.x0 = 0
    zoom_data.y0 = 0
    zoom_data.sx = 1
    zoom_data.sy = 1
    zoom_data.begin(view.matrix, 0, 0)
    return zoom_data


def test_can_create_zoom_tool(view):
    tool = zoom_tool()
    view.add_controller(tool)

    assert isinstance(tool, Gtk.Gesture)


def test_begin_state(zoom_data, view):
    class MockGesture:
        def get_widget(self):
            return view

        def get_point(self, sequence):
            return True, 1, 2

    gesture = MockGesture()
    sequence = None

    on_begin(gesture, sequence, zoom_data)

    assert zoom_data.x0 == 1
    assert zoom_data.y0 == 2
    assert zoom_data.sx == 1
    assert zoom_data.sy == 1


@pytest.mark.asyncio
async def test_scaling(zoom_data, view):
    tool = zoom_tool()
    view.add_controller(tool)

    on_scale_changed(tool, 1.2, zoom_data)

    assert view.matrix[0] == pytest.approx(1.2)
    assert view.matrix[3] == pytest.approx(1.2)


@pytest.mark.asyncio
async def test_multiple_scaling_events(zoom_data, view):
    tool = zoom_tool()
    view.add_controller(tool)

    on_scale_changed(tool, 1.1, zoom_data)
    on_scale_changed(tool, 1.2, zoom_data)

    assert view.matrix[0] == pytest.approx(1.2)
    assert view.matrix[3] == pytest.approx(1.2)


@pytest.mark.asyncio
async def test_scaling_with_unequal_scaling_factor(zoom_data, view):
    tool = zoom_tool()
    view.add_controller(tool)

    zoom_data.sx = 2

    on_scale_changed(tool, 1.2, zoom_data)

    assert view.matrix[0] == pytest.approx(2.4)
    assert view.matrix[3] == pytest.approx(1.2)


@pytest.mark.asyncio
async def test_zoom_should_center_around_mouse_cursor(zoom_data, view):
    tool = zoom_tool()
    view.add_controller(tool)
    zoom_data.x0 = 100
    zoom_data.y0 = 50

    on_scale_changed(tool, 1.2, zoom_data)

    assert view.matrix[4] == pytest.approx(-20.0)
    assert view.matrix[5] == pytest.approx(-10.0)


@pytest.mark.asyncio
async def test_zoom_out_should_be_limited_to_20_percent(zoom_data, view):
    tool = zoom_tool()
    view.add_controller(tool)

    on_scale_changed(tool, 0.0, zoom_data)

    assert view.matrix[0] == pytest.approx(0.2)
    assert view.matrix[3] == pytest.approx(0.2)


@pytest.mark.asyncio
async def test_zoom_in_should_be_limited_to_20_times(zoom_data, view):
    tool = zoom_tool()
    view.add_controller(tool)

    on_scale_changed(tool, 100.0, zoom_data)

    assert view.matrix[0] == pytest.approx(20)
    assert view.matrix[3] == pytest.approx(20)
