import decimal
from _typeshed import Incomplete, SupportsWrite
from collections import OrderedDict
from collections.abc import Callable, Generator, Iterable, Sequence
from contextlib import contextmanager
from re import Pattern
from types import EllipsisType
from typing import Any, ClassVar, Literal, NamedTuple, Protocol, TypeAlias, TypeVar, overload, type_check_only
from typing_extensions import Self

from .enums import PathPaintRule
from .syntax import Name, Raw

__pdoc__: dict[str, bool]

_T = TypeVar("_T")
_CallableT = TypeVar("_CallableT", bound=Callable[..., Any])

@type_check_only
class _SupportsSerialize(Protocol):
    def serialize(self) -> str: ...

@type_check_only
class _SupportsEndPoint(Protocol):
    @property
    def end_point(self) -> Point: ...

def force_nodocument(item: _CallableT) -> _CallableT: ...
def force_document(item: _CallableT) -> _CallableT: ...

Number: TypeAlias = int | float | decimal.Decimal
NumberClass: tuple[type, ...]
WHITESPACE: frozenset[str]
EOL_CHARS: frozenset[str]
DELIMITERS: frozenset[str]
STR_ESC: Pattern[str]
STR_ESC_MAP: dict[str, str]
_Primitive: TypeAlias = (
    _SupportsSerialize
    | Number
    | str
    | bytes
    | bool
    | Raw
    | list[_Primitive]
    | tuple[_Primitive, ...]
    | dict[Name, _Primitive]
    | None
)

class GraphicsStateDictRegistry(OrderedDict[Raw, Name]):
    def register_style(self, style: GraphicsStyle) -> Name | None: ...

def number_to_str(number: Number) -> str: ...
def render_pdf_primitive(primitive: _Primitive) -> Raw: ...

@type_check_only
class _DeviceRGBBase(NamedTuple):
    r: Number
    g: Number
    b: Number
    a: Number | None

class DeviceRGB(_DeviceRGBBase):
    OPERATOR: ClassVar[str]
    def __new__(cls, r: Number, g: Number, b: Number, a: Number | None = None) -> Self: ...
    @property
    def colors(self) -> tuple[Number, Number, Number]: ...
    @property
    def colors255(self) -> tuple[Number, Number, Number]: ...
    def serialize(self) -> str: ...

@type_check_only
class _DeviceGrayBase(NamedTuple):
    g: Number
    a: Number | None

class DeviceGray(_DeviceGrayBase):
    OPERATOR: ClassVar[str]
    def __new__(cls, g: Number, a: Number | None = None) -> Self: ...
    @property
    def colors(self) -> tuple[Number, Number, Number]: ...
    @property
    def colors255(self) -> tuple[Number, Number, Number]: ...
    def serialize(self) -> str: ...

@type_check_only
class _DeviceCMYKBase(NamedTuple):
    c: Number
    m: Number
    y: Number
    k: Number
    a: Number | None

class DeviceCMYK(_DeviceCMYKBase):
    OPERATOR: ClassVar[str]
    def __new__(cls, c: Number, m: Number, y: Number, k: Number, a: Number | None = None) -> Self: ...
    @property
    def colors(self) -> tuple[Number, Number, Number, Number]: ...
    def serialize(self) -> str: ...

def rgb8(r: Number, g: Number, b: Number, a: Number | None = None) -> DeviceRGB: ...
def gray8(g: Number, a: Number | None = None) -> DeviceGray: ...

@overload
def convert_to_device_color(r: DeviceCMYK) -> DeviceCMYK: ...
@overload
def convert_to_device_color(r: DeviceGray) -> DeviceGray: ...
@overload
def convert_to_device_color(r: DeviceRGB) -> DeviceRGB: ...
@overload
def convert_to_device_color(r: str) -> DeviceRGB: ...
@overload
def convert_to_device_color(r: int, g: Literal[-1] = -1, b: Literal[-1] = -1) -> DeviceGray: ...
@overload
def convert_to_device_color(r: Sequence[int] | int, g: int, b: int) -> DeviceGray | DeviceRGB: ...

def cmyk8(c, m, y, k, a=None) -> DeviceCMYK: ...
def color_from_hex_string(hexstr: str) -> DeviceRGB: ...
def color_from_rgb_string(rgbstr: str) -> DeviceRGB: ...

class Point(NamedTuple):
    x: Number
    y: Number
    def render(self) -> str: ...
    def dot(self, other: Point) -> Number: ...
    def angle(self, other: Point) -> float: ...
    def mag(self) -> Number: ...
    def __add__(self, other: Point) -> Point: ...  # type: ignore[override]
    def __sub__(self, other: Point) -> Point: ...
    def __neg__(self) -> Point: ...
    def __mul__(self, other: Number) -> Point: ...  # type: ignore[override]
    def __rmul__(self, other: Number) -> Point: ...  # type: ignore[override]
    def __truediv__(self, other: Number) -> Point: ...
    def __floordiv__(self, other: Number) -> Point: ...
    def __matmul__(self, other: Transform) -> Point: ...

class Transform(NamedTuple):
    a: Number
    b: Number
    c: Number
    d: Number
    e: Number
    f: Number
    @classmethod
    def identity(cls) -> Self: ...
    @classmethod
    def translation(cls, x: Number, y: Number) -> Self: ...
    @classmethod
    def scaling(cls, x: Number, y: Number | None = None) -> Self: ...
    @classmethod
    def rotation(cls, theta: Number) -> Self: ...
    @classmethod
    def rotation_d(cls, theta_d: Number) -> Self: ...
    @classmethod
    def shearing(cls, x: Number, y: Number | None = None) -> Self: ...
    def translate(self, x: Number, y: Number) -> Self: ...
    def scale(self, x: Number, y: Number | None = None) -> Self: ...
    def rotate(self, theta: Number) -> Self: ...
    def rotate_d(self, theta_d: Number) -> Self: ...
    def shear(self, x: Number, y: Number | None = None) -> Self: ...
    def about(self, x: Number, y: Number) -> Transform: ...
    def __mul__(self, other: Number) -> Transform: ...  # type: ignore[override]
    def __rmul__(self, other: Number) -> Transform: ...  # type: ignore[override]
    def __matmul__(self, other: Transform) -> Self: ...
    def render(self, last_item: _T) -> tuple[str, _T]: ...

class GraphicsStyle:
    INHERIT: ClassVar[EllipsisType]
    MERGE_PROPERTIES: ClassVar[tuple[str, ...]]
    TRANSPARENCY_KEYS: ClassVar[tuple[Name, ...]]
    PDF_STYLE_KEYS: ClassVar[tuple[Name, ...]]
    @classmethod
    def merge(cls, parent, child) -> Self: ...
    def __init__(self) -> None: ...
    def __deepcopy__(self, memo) -> Self: ...

    @property
    def allow_transparency(self): ...
    @allow_transparency.setter
    def allow_transparency(self, new): ...

    @property
    def paint_rule(self) -> PathPaintRule | EllipsisType: ...
    @paint_rule.setter
    def paint_rule(self, new: PathPaintRule | str | EllipsisType | None) -> None: ...

    @property
    def auto_close(self) -> bool | EllipsisType: ...
    @auto_close.setter
    def auto_close(self, new: bool | EllipsisType) -> None: ...

    @property
    def intersection_rule(self): ...
    @intersection_rule.setter
    def intersection_rule(self, new) -> None: ...

    @property
    def fill_color(self): ...
    @fill_color.setter
    def fill_color(self, color) -> None: ...

    @property
    def fill_opacity(self): ...
    @fill_opacity.setter
    def fill_opacity(self, new) -> None: ...

    @property
    def stroke_color(self): ...
    @stroke_color.setter
    def stroke_color(self, color: str | DeviceRGB | DeviceGray | DeviceCMYK | EllipsisType | None) -> None: ...

    @property
    def stroke_opacity(self): ...
    @stroke_opacity.setter
    def stroke_opacity(self, new) -> None: ...

    @property
    def blend_mode(self): ...
    @blend_mode.setter
    def blend_mode(self, value) -> None: ...

    @property
    def stroke_width(self): ...
    @stroke_width.setter
    def stroke_width(self, width: Number | EllipsisType | None) -> None: ...

    @property
    def stroke_cap_style(self): ...
    @stroke_cap_style.setter
    def stroke_cap_style(self, value) -> None: ...

    @property
    def stroke_join_style(self): ...
    @stroke_join_style.setter
    def stroke_join_style(self, value) -> None: ...

    @property
    def stroke_miter_limit(self): ...
    @stroke_miter_limit.setter
    def stroke_miter_limit(self, value: Number | EllipsisType) -> None: ...

    @property
    def stroke_dash_pattern(self): ...
    @stroke_dash_pattern.setter
    def stroke_dash_pattern(self, value: Number | Iterable[Number] | EllipsisType | None) -> None: ...

    @property
    def stroke_dash_phase(self): ...
    @stroke_dash_phase.setter
    def stroke_dash_phase(self, value: Number | EllipsisType): ...

    def serialize(self) -> Raw | None: ...
    def resolve_paint_rule(self) -> PathPaintRule: ...

class Move(NamedTuple):
    pt: Point
    @property
    def end_point(self) -> Point: ...
    def render(
        self, gsd_registry: GraphicsStateDictRegistry, style: GraphicsStyle, last_item: _SupportsEndPoint, initial_point: Point
    ) -> tuple[str, Self, Point]: ...
    def render_debug(
        self,
        gsd_registry: GraphicsStateDictRegistry,
        style: GraphicsStyle,
        last_item: _SupportsEndPoint,
        initial_point: Point,
        debug_stream: SupportsWrite[str],
        pfx: str,
    ) -> tuple[str, Self, Point]: ...

class RelativeMove(NamedTuple):
    pt: Point
    def render(
        self, gsd_registry: GraphicsStateDictRegistry, style: GraphicsStyle, last_item: _SupportsEndPoint, initial_point: Point
    ) -> tuple[str, Move, Point]: ...
    def render_debug(
        self,
        gsd_registry: GraphicsStateDictRegistry,
        style: GraphicsStyle,
        last_item: _SupportsEndPoint,
        initial_point: Point,
        debug_stream: SupportsWrite[str],
        pfx: str,
    ) -> tuple[str, Move, Point]: ...

class Line(NamedTuple):
    pt: Point
    @property
    def end_point(self) -> Point: ...
    def render(
        self, gsd_registry: GraphicsStateDictRegistry, style: GraphicsStyle, last_item: _SupportsEndPoint, initial_point: Point
    ) -> tuple[str, Self, Point]: ...
    def render_debug(
        self,
        gsd_registry: GraphicsStateDictRegistry,
        style: GraphicsStyle,
        last_item: _SupportsEndPoint,
        initial_point: Point,
        debug_stream: SupportsWrite[str],
        pfx: str,
    ) -> tuple[str, Self, Point]: ...

class RelativeLine(NamedTuple):
    pt: Point
    def render(self, gsd_registry, style, last_item, initial_point): ...
    def render_debug(self, gsd_registry, style, last_item, initial_point, debug_stream, pfx): ...

class HorizontalLine(NamedTuple):
    x: Number
    def render(self, gsd_registry, style, last_item, initial_point): ...
    def render_debug(self, gsd_registry, style, last_item, initial_point, debug_stream, pfx): ...

class RelativeHorizontalLine(NamedTuple):
    x: Number
    def render(self, gsd_registry, style, last_item, initial_point): ...
    def render_debug(self, gsd_registry, style, last_item, initial_point, debug_stream, pfx): ...

class VerticalLine(NamedTuple):
    y: Number
    def render(self, gsd_registry, style, last_item, initial_point): ...
    def render_debug(self, gsd_registry, style, last_item, initial_point, debug_stream, pfx): ...

class RelativeVerticalLine(NamedTuple):
    y: Number
    def render(self, gsd_registry, style, last_item, initial_point): ...
    def render_debug(self, gsd_registry, style, last_item, initial_point, debug_stream, pfx): ...

class BezierCurve(NamedTuple):
    c1: Point
    c2: Point
    end: Point
    @property
    def end_point(self) -> Point: ...
    def render(self, gsd_registry, style, last_item, initial_point): ...
    def render_debug(self, gsd_registry, style, last_item, initial_point, debug_stream, pfx): ...

class RelativeBezierCurve(NamedTuple):
    c1: Point
    c2: Point
    end: Point
    def render(self, gsd_registry, style, last_item, initial_point): ...
    def render_debug(self, gsd_registry, style, last_item, initial_point, debug_stream, pfx): ...

class QuadraticBezierCurve(NamedTuple):
    ctrl: Point
    end: Point
    @property
    def end_point(self) -> Point: ...
    def to_cubic_curve(self, start_point): ...
    def render(self, gsd_registry, style, last_item, initial_point): ...
    def render_debug(self, gsd_registry, style, last_item, initial_point, debug_stream, pfx): ...

class RelativeQuadraticBezierCurve(NamedTuple):
    ctrl: Point
    end: Point
    def render(self, gsd_registry, style, last_item, initial_point): ...
    def render_debug(self, gsd_registry, style, last_item, initial_point, debug_stream, pfx): ...

class Arc(NamedTuple):
    radii: Point
    rotation: Number
    large: bool
    sweep: bool
    end: Point
    @staticmethod
    def subdivde_sweep(sweep_angle: Number) -> Generator[tuple[Point, Point, Point]]: ...
    def render(self, gsd_registry, style, last_item, initial_point): ...
    def render_debug(self, gsd_registry, style, last_item, initial_point, debug_stream, pfx): ...

class RelativeArc(NamedTuple):
    radii: Point
    rotation: Number
    large: bool
    sweep: bool
    end: Point
    def render(self, gsd_registry, style, last_item, initial_point): ...
    def render_debug(self, gsd_registry, style, last_item, initial_point, debug_stream, pfx): ...

class Rectangle(NamedTuple):
    org: Point
    size: Point
    def render(self, gsd_registry, style, last_item, initial_point): ...
    def render_debug(self, gsd_registry, style, last_item, initial_point, debug_stream, pfx): ...

class RoundedRectangle(NamedTuple):
    org: Point
    size: Point
    corner_radii: Point
    def render(self, gsd_registry, style, last_item, initial_point): ...
    def render_debug(self, gsd_registry, style, last_item, initial_point, debug_stream, pfx): ...

class Ellipse(NamedTuple):
    radii: Point
    center: Point
    def render(self, gsd_registry, style, last_item, initial_point): ...
    def render_debug(self, gsd_registry, style, last_item, initial_point, debug_stream, pfx): ...

class ImplicitClose(NamedTuple):
    def render(self, gsd_registry, style, last_item, initial_point): ...
    def render_debug(self, gsd_registry, style, last_item, initial_point, debug_stream, pfx): ...

class Close(NamedTuple):
    def render(self, gsd_registry, style, last_item, initial_point): ...
    def render_debug(self, gsd_registry, style, last_item, initial_point, debug_stream, pfx): ...

class DrawingContext:
    def __init__(self) -> None: ...
    def add_item(self, item, _copy: bool = True) -> None: ...
    def render(self, gsd_registry, first_point, scale, height, starting_style): ...
    def render_debug(self, gsd_registry, first_point, scale, height, starting_style, debug_stream): ...

class PaintedPath:
    def __init__(self, x: Number = 0, y: Number = 0) -> None: ...
    def __deepcopy__(self, memo) -> Self: ...
    @property
    def style(self) -> GraphicsStyle: ...

    @property
    def transform(self): ...
    @transform.setter
    def transform(self, tf) -> None: ...

    @property
    def auto_close(self): ...
    @auto_close.setter
    def auto_close(self, should) -> None: ...

    @property
    def paint_rule(self): ...
    @paint_rule.setter
    def paint_rule(self, style) -> None: ...

    @property
    def clipping_path(self): ...
    @clipping_path.setter
    def clipping_path(self, new_clipath) -> None: ...

    @contextmanager
    def transform_group(self, transform) -> Generator[Self]: ...
    def add_path_element(self, item, _copy: bool = True) -> None: ...
    def remove_last_path_element(self) -> None: ...
    def rectangle(self, x: Number, y: Number, w: Number, h: Number, rx: Number = 0, ry: Number = 0) -> Self: ...
    def circle(self, cx: Number, cy: Number, r: Number) -> Self: ...
    def ellipse(self, cx: Number, cy: Number, rx: Number, ry: Number) -> Self: ...
    def move_to(self, x: Number, y: Number) -> Self: ...
    def move_relative(self, x: Number, y: Number) -> Self: ...
    def line_to(self, x: Number, y: Number) -> Self: ...
    def line_relative(self, dx: Number, dy: Number) -> Self: ...
    def horizontal_line_to(self, x: Number) -> Self: ...
    def horizontal_line_relative(self, dx: Number) -> Self: ...
    def vertical_line_to(self, y: Number) -> Self: ...
    def vertical_line_relative(self, dy: Number) -> Self: ...
    def curve_to(self, x1: Number, y1: Number, x2: Number, y2: Number, x3: Number, y3: Number) -> Self: ...
    def curve_relative(self, dx1: Number, dy1: Number, dx2: Number, dy2: Number, dx3: Number, dy3: Number) -> Self: ...
    def quadratic_curve_to(self, x1: Number, y1: Number, x2: Number, y2: Number) -> Self: ...
    def quadratic_curve_relative(self, dx1: Number, dy1: Number, dx2: Number, dy2: Number) -> Self: ...
    def arc_to(
        self, rx: Number, ry: Number, rotation: Number, large_arc: bool, positive_sweep: bool, x: Number, y: Number
    ) -> Self: ...
    def arc_relative(
        self, rx: Number, ry: Number, rotation: Number, large_arc: bool, positive_sweep: bool, dx: Number, dy: Number
    ) -> Self: ...
    def close(self) -> None: ...
    def render(self, gsd_registry, style, last_item, initial_point, debug_stream=None, pfx=None): ...
    def render_debug(self, gsd_registry, style, last_item, initial_point, debug_stream, pfx): ...

class ClippingPath(PaintedPath):
    paint_rule: PathPaintRule
    def __init__(self, x: Number = 0, y: Number = 0) -> None: ...
    def render(self, gsd_registry, style, last_item, initial_point, debug_stream=None, pfx=None): ...
    def render_debug(self, gsd_registry, style, last_item, initial_point, debug_stream, pfx): ...

class GraphicsContext:
    style: GraphicsStyle
    path_items: list[Incomplete]
    def __init__(self) -> None: ...
    def __deepcopy__(self, memo) -> Self: ...

    @property
    def transform(self) -> Transform | None: ...
    @transform.setter
    def transform(self, tf) -> None: ...

    @property
    def clipping_path(self) -> ClippingPath | None: ...
    @clipping_path.setter
    def clipping_path(self, new_clipath) -> None: ...

    def add_item(self, item, _copy: bool = True) -> None: ...
    def remove_last_item(self) -> None: ...
    def merge(self, other_context) -> None: ...
    def build_render_list(
        self, gsd_registry, style, last_item, initial_point, debug_stream=None, pfx=None, _push_stack: bool = True
    ): ...
    def render(
        self, gsd_registry, style: DrawingContext, last_item, initial_point, debug_stream=None, pfx=None, _push_stack: bool = True
    ): ...
    def render_debug(
        self, gsd_registry, style: DrawingContext, last_item, initial_point, debug_stream, pfx, _push_stack: bool = True
    ): ...
