from collections import abc
from pythautomata.base_types.symbol import Symbol
from typing import Union
from pythautomata.exceptions.unexpected_type_exception import UnexpectedTypeException


class Sequence():
    """Ordered collection of Symbols.
    """

    def __init__(self, value: Union['Sequence', list[Symbol], tuple[Symbol]] = ()):
        if value == ():
            self._value: tuple[Symbol] = ()
        elif isinstance(value, Sequence):
            self._value = value.value
        elif isinstance(value, list):
            self._value = tuple(value)
        elif isinstance(value, tuple):
            self._value = value
        else:
            raise ValueError

    @property
    def value(self) -> tuple[Symbol]:
        return self._value

    def get_prefixes(self) -> list['Sequence']:
        result = list()
        for i in range(1, len(self.value) + 1):
            result.append(Sequence(self.value[:i]))
        return result

    def get_suffixes(self) -> list['Sequence']:
        result = list()
        for i in range(0, len(self.value)):
            result.append(Sequence(self.value[i:]))
        return result

    def append(self, symbol_to_append: Symbol):
        value = list(self.value)
        value.append(symbol_to_append)
        return Sequence(value)

    def __getitem__(self, key):
        return self.value[key]

    def __len__(self):
        return len(self.value)

    def __add__(self, other: Union['Sequence', Symbol]) -> 'Sequence':
        if isinstance(other, Sequence):
            return Sequence(self.value + other.value)
        if isinstance(other, Symbol):
            return Sequence(self.value + (other,))
        raise UnexpectedTypeException()

    def __radd__(self, other: Union['Sequence', Symbol]) -> 'Sequence':
        if isinstance(other, Sequence):
            return Sequence(other.value + self.value)
        if isinstance(other, Symbol):
            return Sequence((other,) + self.value)
        raise UnexpectedTypeException()

    def __repr__(self):
        return "ϵ" if self.value == () else "".join(map(lambda x: str(x), self.value))

    def __lt__(self, other: 'Sequence') -> bool:
        if len(self.value) == len(other.value):
            return self.__repr__() < other.__repr__()
        else:
            return len(self.value) < len(other.value)

    def __iter__(self):
        return self.value.__iter__()

    def __eq__(self, other) -> bool:
        if isinstance(other, Sequence):
            return self.value == other.value
        return False

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