from functools import cached_property
from typing import List, cast

from axipy.cpp_render import ShadowContext
from axipy.cs import CoordSystem
from axipy.render.clip import ClipGeometry
from axipy.utl import Rect
from PySide2.QtGui import QPainter

__all__: List[str] = [
    "Context",
]


class Context:
    """
    Контекст рисования.

    Содержит информацию о том, куда производится рисование (QPainter),
    а так же о необходимых преобразованиях, которые необходимо применить
    к объекту непосредственно перед его отрисовкой.

    Args:
      painter: Объект QPainter для рисования.

    Пример создания контекста на базе растра. Далее его можно использовать для отрисовки карты :class:`Map`, отчета  :class:`Report`
    или легенды  :class:`Legend`:

    .. literalinclude:: /../../tests/doc_examples/render/test_example_context.py
        :pyobject: test_run_example_context
        :lines: 2-
        :dedent: 4
    """

    def __init__(self, painter: QPainter) -> None:
        if painter is not None:
            self._shadow = ShadowContext(painter)
        else:
            raise ValueError("Painter is not defined")

    @property
    def rect(self) -> Rect:
        """Прямоугольник в координатах карты, который будет отрисован."""
        return Rect.from_qt(self._shadow.get_rect())

    @rect.setter
    def rect(self, r: Rect) -> None:
        self._shadow.set_rect(r.to_qt())

    @property
    def coordsystem(self) -> CoordSystem:
        """
        Координатная система.

        Если она не задана, берется наиболее подходящая исходя из текущего контента.
        """
        return cast(CoordSystem, CoordSystem._wrap(self._shadow.get_cs()))

    @coordsystem.setter
    def coordsystem(self, cs: CoordSystem) -> None:
        self._shadow.set_cs(cs._shadow)

    @property
    def dpi(self) -> float:
        """
        Количество точек на дюйм, с которым происходит рисование.

        Влияет на отрисовку в «реальных» единицах измерения (мм, см, пункты).
        """
        return self._shadow.get_dpi()

    @dpi.setter
    def dpi(self, v: float) -> None:
        self._shadow.set_dpi(v)

    @property
    def pseudo_zoom(self) -> bool:
        return self._shadow.get_pseudo_zoom()

    @pseudo_zoom.setter
    def pseudo_zoom(self, v: bool) -> None:
        self._shadow.set_pseudo_zoom(v)

    @cached_property
    def _clip(self) -> ClipGeometry:
        return ClipGeometry._wrap(self._shadow)

    @property
    def clip(self) -> ClipGeometry:
        """
        Геометрия обрезки карты. Устанавливается геометрия, в рамках которой будет
        отрисована карта. За пределами отрисовка производиться не будет. Обрабатываются
        только площадные объекты. Также допустимо устанавливать коллекции.

        .. literalinclude:: /../../tests/doc_examples/render/test_example_context.py
            :pyobject: test_run_example_context_clip
            :lines: 4-
            :dedent: 4
        """
        return self._clip
