"""Avalon Memory-Mapped protocol decoder."""

from __future__ import annotations
from dataclasses import dataclass


@dataclass
class AvalonTxn:
    addr: int
    data: int
    write: bool
    start: int
    end: int
    byteenable: int = 0xF


class Avalon:
    """Avalon-MM protocol decoder."""

    @staticmethod
    def bind(**signals) -> "BoundAvalon":
        return BoundAvalon(signals)


class BoundAvalon:
    def __init__(self, signals: dict):
        self.signals = signals
        self._trace = None
        for sig in signals.values():
            if hasattr(sig, "_trace"):
                self._trace = sig._trace
                break

    def _val(self, name, index):
        sig = self.signals.get(name)
        if sig is None:
            return None
        old = self._trace._index
        self._trace._index = index
        self._trace._virtual_cache.clear()
        v = sig.value
        self._trace._index = old
        return v

    def transactions(self) -> list[AvalonTxn]:
        """Extract Avalon-MM transactions.

        Handshake: read/write asserted and waitrequest deasserted (or absent).
        """
        t = self._trace
        if t is None:
            return []

        read_sig = self.signals.get("read")
        write_sig = self.signals.get("write")
        waitrequest = self.signals.get("waitrequest")

        results = []

        # Write transactions
        if write_sig is not None:
            if waitrequest is not None:
                hs = t.find(write_sig & ~waitrequest)
            else:
                hs = t.find(write_sig)
            for idx in hs:
                results.append(AvalonTxn(
                    addr=self._val("address", idx) or 0,
                    data=self._val("writedata", idx) or 0,
                    write=True,
                    start=idx, end=idx,
                    byteenable=self._val("byteenable", idx) or 0xF,
                ))

        # Read transactions
        if read_sig is not None:
            if waitrequest is not None:
                hs = t.find(read_sig & ~waitrequest)
            else:
                hs = t.find(read_sig)
            for idx in hs:
                results.append(AvalonTxn(
                    addr=self._val("address", idx) or 0,
                    data=self._val("readdata", idx) or 0,
                    write=False,
                    start=idx, end=idx,
                    byteenable=self._val("byteenable", idx) or 0xF,
                ))

        results.sort(key=lambda t: t.start)
        return results
