from typing import override
from flut._flut.engine import FlutEnumObject, FlutImmutableObject, FlutRealtimeObject
from flut._flut.runtime import _flut_unpack_required_field


class Color(FlutImmutableObject):

    def __init__(self, value: int = 0x00000000):
        super().__init__("Color")
        self.value = value & 0xFFFFFFFF

    @staticmethod
    def _flut_unpack(data: dict) -> "Color":
        return Color(value=_flut_unpack_required_field(data, "value"))

    @staticmethod
    def fromARGB(a: int, r: int, g: int, b: int) -> "Color":
        return Color(
            ((a & 0xFF) << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF)
        )

    @staticmethod
    def fromRGBO(r: int, g: int, b: int, opacity: float) -> "Color":
        return Color(
            ((int(opacity * 255) & 0xFF) << 24)
            | ((r & 0xFF) << 16)
            | ((g & 0xFF) << 8)
            | (b & 0xFF)
        )

    @property
    def alpha(self) -> int:
        return (self.value >> 24) & 0xFF

    @property
    def red(self) -> int:
        return (self.value >> 16) & 0xFF

    @property
    def green(self) -> int:
        return (self.value >> 8) & 0xFF

    @property
    def blue(self) -> int:
        return self.value & 0xFF

    @property
    def opacity(self) -> float:
        return self.alpha / 255.0

    def withValues(self, *, alpha=None, red=None, green=None, blue=None) -> "Color":
        return Color.fromARGB(
            int(alpha * 255) if alpha is not None else self.alpha,
            int(red * 255) if red is not None else self.red,
            int(green * 255) if green is not None else self.green,
            int(blue * 255) if blue is not None else self.blue,
        )

    @override
    def _flut_pack(self) -> dict:
        result = self._flut_base_props()
        result["value"] = self.value
        return result

    def __repr__(self):
        return f"Color(0x{self.value:08X})"

    def __eq__(self, other):
        if isinstance(other, Color):
            return self.value == other.value
        return False

    def __hash__(self):
        return hash(self.value)


class PaintingStyle(FlutEnumObject):
    fill: "PaintingStyle"
    stroke: "PaintingStyle"


class _FontWeight(FlutImmutableObject):
    def __init__(self, index: int, value: int):
        super().__init__("FontWeight")
        self.index = index
        self.value = value

    @override
    def _flut_pack(self) -> dict:
        result = self._flut_base_props()
        result["index"] = self.index
        return result

    @staticmethod
    def _flut_unpack(data: dict) -> "_FontWeight":
        index = data.get("index", 3)  # default w400/normal
        return FontWeight._by_index[index]


class FontWeight:
    w100 = _FontWeight(0, 100)
    w200 = _FontWeight(1, 200)
    w300 = _FontWeight(2, 300)
    w400 = _FontWeight(3, 400)
    w500 = _FontWeight(4, 500)
    w600 = _FontWeight(5, 600)
    w700 = _FontWeight(6, 700)
    w800 = _FontWeight(7, 800)
    w900 = _FontWeight(8, 900)
    normal = w400
    bold = w700
    _by_index = [w100, w200, w300, w400, w500, w600, w700, w800, w900]


class Size(FlutImmutableObject):
    def __init__(self, width: float = 0, height: float = 0):
        super().__init__("Size")
        self.width = width
        self.height = height

    @staticmethod
    def _flut_unpack(data: dict) -> "Size":
        return Size(
            width=_flut_unpack_required_field(data, "width"),
            height=_flut_unpack_required_field(data, "height"),
        )

    @override
    def _flut_pack(self) -> dict:
        result = self._flut_base_props()
        result["width"] = self.width
        result["height"] = self.height
        return result


class Offset(FlutImmutableObject):
    def __init__(self, dx: float = 0, dy: float = 0):
        super().__init__("Offset")
        self.dx = dx
        self.dy = dy

    @staticmethod
    def _flut_unpack(data: dict) -> "Offset":
        return Offset(
            dx=_flut_unpack_required_field(data, "dx"),
            dy=_flut_unpack_required_field(data, "dy"),
        )

    @override
    def _flut_pack(self) -> dict:
        result = self._flut_base_props()
        result["dx"] = self.dx
        result["dy"] = self.dy
        return result


class Rect(FlutImmutableObject):
    def __init__(self, left: float, top: float, right: float, bottom: float):
        super().__init__("Rect")
        self.left = left
        self.top = top
        self.right = right
        self.bottom = bottom

    @staticmethod
    def _flut_unpack(data: dict) -> "Rect":
        return Rect(
            left=_flut_unpack_required_field(data, "left"),
            top=_flut_unpack_required_field(data, "top"),
            right=_flut_unpack_required_field(data, "right"),
            bottom=_flut_unpack_required_field(data, "bottom"),
        )

    @staticmethod
    def fromLTWH(left: float, top: float, width: float, height: float) -> "Rect":
        return Rect(left, top, left + width, top + height)

    @staticmethod
    def fromLTRB(left: float, top: float, right: float, bottom: float) -> "Rect":
        return Rect(left, top, right, bottom)

    @staticmethod
    def fromCircle(*, center, radius: float) -> "Rect":
        return Rect(
            center.dx - radius,
            center.dy - radius,
            center.dx + radius,
            center.dy + radius,
        )

    @staticmethod
    def fromCenter(*, center, width: float, height: float) -> "Rect":
        return Rect(
            center.dx - width / 2,
            center.dy - height / 2,
            center.dx + width / 2,
            center.dy + height / 2,
        )

    @property
    def width(self) -> float:
        return self.right - self.left

    @property
    def height(self) -> float:
        return self.bottom - self.top

    @property
    def size(self):
        return Size(self.width, self.height)

    @override
    def _flut_pack(self) -> dict:
        result = self._flut_base_props()
        result["left"] = self.left
        result["top"] = self.top
        result["right"] = self.right
        result["bottom"] = self.bottom
        return result


class Paint(FlutImmutableObject):
    def __init__(self):
        super().__init__("Paint")
        self.color = None
        self.strokeWidth = None
        self.style = None

    @override
    def _flut_pack(self) -> dict:
        result = self._flut_base_props()
        if self.color is not None:
            result["color"] = self.color._flut_pack()
        if self.strokeWidth is not None:
            result["strokeWidth"] = self.strokeWidth
        if self.style is not None:
            result["style"] = self.style._flut_pack()
        return result


class Canvas(FlutRealtimeObject):
    def __init__(self, oid: int):
        super().__init__("Canvas", oid=oid)

    @staticmethod
    def _flut_unpack(data: dict) -> "Canvas":
        return Canvas(oid=_flut_unpack_required_field(data, "_flut_oid"))

    def drawLine(self, p1: Offset, p2: Offset, paint: Paint):
        self._call(
            "drawLine",
            p1=p1._flut_pack(),
            p2=p2._flut_pack(),
            paint=paint._flut_pack(),
        )

    def drawRect(self, rect: Rect, paint: Paint):
        self._call("drawRect", rect=rect._flut_pack(), paint=paint._flut_pack())

    def drawCircle(self, c: Offset, radius: float, paint: Paint):
        self._call(
            "drawCircle",
            c=c._flut_pack(),
            radius=radius,
            paint=paint._flut_pack(),
        )

    def drawOval(self, rect: Rect, paint: Paint):
        self._call("drawOval", rect=rect._flut_pack(), paint=paint._flut_pack())

    def drawArc(
        self,
        rect: Rect,
        startAngle: float,
        sweepAngle: float,
        useCenter: bool,
        paint: Paint,
    ):
        self._call(
            "drawArc",
            rect=rect._flut_pack(),
            startAngle=startAngle,
            sweepAngle=sweepAngle,
            useCenter=useCenter,
            paint=paint._flut_pack(),
        )

    def clipRect(self, rect: Rect):
        self._call("clipRect", rect=rect._flut_pack())

    def save(self):
        self._call("save")

    def restore(self):
        self._call("restore")

    def translate(self, dx: float, dy: float):
        self._call("translate", dx=dx, dy=dy)

    def scale(self, sx: float, sy: float = None):
        self._call("scale", sx=sx, sy=sy)

    def rotate(self, radians: float):
        self._call("rotate", radians=radians)


class PlaceholderAlignment(FlutEnumObject):
    baseline: "PlaceholderAlignment"
    aboveBaseline: "PlaceholderAlignment"
    belowBaseline: "PlaceholderAlignment"
    top: "PlaceholderAlignment"
    bottom: "PlaceholderAlignment"
    middle: "PlaceholderAlignment"


class TextBaseline(FlutEnumObject):
    alphabetic: "TextBaseline"
    ideographic: "TextBaseline"


class TextAlign(FlutEnumObject):
    left: "TextAlign"
    right: "TextAlign"
    center: "TextAlign"
    justify: "TextAlign"
    start: "TextAlign"
    end: "TextAlign"

