"""Byte-wise cursor utilities for decoding binary ring payloads."""

from __future__ import annotations

import struct


class ByteCursor:
    """Read primitive values from bytes while tracking offset."""

    def __init__(self, payload: bytes) -> None:
        """Create cursor at start of payload."""
        self.payload = payload
        self.offset = 0

    def read_u8(self) -> int:
        """Read unsigned 8-bit integer."""
        value = struct.unpack_from("<B", self.payload, self.offset)[0]
        self.offset += 1
        return value

    def read_u16(self) -> int:
        """Read unsigned 16-bit integer."""
        value = struct.unpack_from("<H", self.payload, self.offset)[0]
        self.offset += 2
        return value

    def read_u32(self) -> int:
        """Read unsigned 32-bit integer."""
        value = struct.unpack_from("<I", self.payload, self.offset)[0]
        self.offset += 4
        return value

    def read_u64(self) -> int:
        """Read unsigned 64-bit integer."""
        value = struct.unpack_from("<Q", self.payload, self.offset)[0]
        self.offset += 8
        return value

    def read_f32(self) -> float:
        """Read 32-bit float."""
        value = struct.unpack_from("<f", self.payload, self.offset)[0]
        self.offset += 4
        return float(value)

    def read_bytes(self, size: int) -> bytes:
        """Read raw byte slice of fixed size."""
        data = self.payload[self.offset : self.offset + size]
        self.offset += size
        return data
