"""Pattern matcher."""

from typing import Callable, Optional
from hdltools.patterns import Pattern


class PatternMatcherError(Exception):
    """Pattern matcher error."""


class PatternMatcher:
    """Sequential pattern matcher."""

    def __init__(
        self, *expected_sequence: Pattern, match_cb: Optional[Callable] = None
    ):
        """Initialize."""
        for pattern in expected_sequence:
            if not isinstance(pattern, Pattern):
                raise TypeError(
                    "elements from expected sequence must be Pattern objects"
                )

        # optional callback
        if match_cb is not None and not callable(match_cb):
            raise TypeError("match_cb must be a callable")
        self._match_cb = match_cb

        # expected sequence
        self._sequence = expected_sequence

        # progress tracking
        self._progress = 0

    def matched(self):
        """Sequential pattern match complete."""
        if self._match_cb is not None:
            self._match_cb(self)

    def restart(self):
        """Restart matching sequence."""
        self._progress = 0

    @property
    def progress(self):
        """Get progress."""
        return self._progress

    def __len__(self):
        """Get sequence length."""
        return len(self._sequence)

    def __getitem__(self, index: int) -> Pattern:
        """Get a pattern from the sequence."""
        return self._sequence[index]

    def new_state(self, value: str) -> bool:
        """Act on new state."""
        if self._progress == len(self._sequence):
            # finished already
            raise PatternMatcherError("sequential matching already complete")

        # try to match
        if self._sequence[self._progress].match(value):
            if self._progress == len(self._sequence) - 1:
                # finished
                self.matched()
            self._progress += 1
            return True

        return False

@@ -260,16 +260,24 @@
    self,
+%0A       
  *expect
@@ -297,16 +297,24 @@
 Pattern,
+%0A       
  match_c
@@ -336,16 +336,55 @@
 lable%5D =
+ None,%0A        initial: Optional%5Bstr%5D =
  None%0A  
@@ -831,124 +831,765 @@
   # 
-expected sequence%0A        self._sequence = expected_sequence%0A%0A        # progress tracking%0A        self._progress = 0
+initial value%0A        if initial is not None and not isinstance(initial, str):%0A            raise TypeError(%22initial value must be string%22)%0A        self._initial = initial%0A%0A        # expected sequence%0A        self._sequence = expected_sequence%0A%0A        # progress tracking%0A        self._progress = 0%0A%0A    @property%0A    def initial(self):%0A        %22%22%22Get initial value.%22%22%22%0A        return self._initial%0A%0A    @initial.setter%0A    def initial(self, value: str):%0A        %22%22%22Set initial value.%22%22%22%0A        if not isinstance(value, str):%0A            raise TypeError(%22value must be string%22)%0A        if self._progress %3E 0:%0A            raise PatternMatcherError(%0A                %22matching in progress, cannot set initial value%22%0A            )%0A        self._initial = value
 %0A%0A  

handle initial values