from __future__ import annotations
from typing import Protocol, runtime_checkable, Sequence, MutableSequence


@runtime_checkable
class IMatrixData(Protocol):
    """
    ZOSAPI.Common.IMatrixData
    2D double-precision matrix accessor for fast block I/O or element-wise access.
    """

    # ----------------------------
    # Methods
    # ----------------------------
    def ReadData(self, Size: int, Data: MutableSequence[float]) -> None:
        """
        Retrieve the entire dataset into a preallocated 1D buffer (row-major).
        `Size` must equal `TotalLength == Rows * Cols`.
        """

    def WriteData(self, Size: int, Data: Sequence[float]) -> None:
        """
        Replace the entire dataset from a 1D buffer (row-major).
        No effect if `IsReadOnly` is True. `Size` must equal `TotalLength`.
        """

    def GetValueAt(self, Row: int, Col: int) -> float:
        """Get a single matrix element."""

    def SetValueAt(self, Row: int, Col: int, Value: float) -> None:
        """Set a single matrix element (no effect if `IsReadOnly` is True)."""

    # ----------------------------
    # Properties
    # ----------------------------
    @property
    def IsReadOnly(self) -> bool:
        """True if the underlying data cannot be modified."""

    @property
    def Rows(self) -> int:
        """Number of rows in the 2D array."""

    @property
    def Cols(self) -> int:
        """Number of columns in the 2D array."""

    @property
    def TotalLength(self) -> int:
        """Total number of elements (Rows * Cols)."""

    @property
    def Data(self) -> "Sequence[Sequence[float]]":
        """
        2D view of the matrix. Mutating the returned object does NOT update
        the source; assign a full replacement or use WriteData for bulk updates.
        """
    @Data.setter
    def Data(self, value: "Sequence[Sequence[float]]") -> None:
        """Replace the entire matrix (no effect if `IsReadOnly` is True)."""


__all__ = ["IMatrixData"]
