# CookieJar ADT: Python Unordered List Implementation (Extended Mutable)
#
# 345678901234567890123456789012345678901234567890123456789012345678901234567890
#
# 2018-10-10: Unordered List version adapted from Dict version
# 2018-10-12: Corrected/improved statement of invariant
#             Added methods count() and cardinalty()
# 2022-04-20: Improved comments. Reformatted with black.

from typing import List, Generic
from dataclasses import dataclass, field  # 3.7+

from cookiejar2 import CookieJarABC, CookieType

# Class CookieJarList implements the CookieJar ADT using a Python List data
# type to implement the jar (mathematical bag) of cookies.  The list elements
# are the cookie types with one occurrence for each occurrence in the cookie
# jar. The list is not required to be arranged in a particular order
# (i.e. "unordered").
#
#   IMPLEMENTATION INVARIANT:
#     (ForAll c : c IN CookieType ::
#         OCCURRENCES(c,Bag(self))  == jar.count(c))
#
# Once verified: self.count(c) == OCCURRENCES(c,Bag(self))

# The @dataclass decorator generates default __init__ and __repr__ methods for
# the CookieJarList class. Setting the intial value of jar to
# field(default_factory=list) causes the initial value to be an empty list.

@dataclass
class CookieJarList(CookieJarABC, Generic[CookieType]):
    jar: List[CookieType] = field(default_factory=list)

    def put_in(self, cookie: CookieType) -> None:
        """Insert "cookie" into this cookie jar"""
        self.jar.append(cookie)

    def eat(self, cookie: CookieType) -> None:
        """Remove "cookie" from this cookie jar"""
        if cookie in self.jar:
            self.jar.remove(cookie)
        else:
            print(f"Error: No cookie of type {cookie} in jar to eat")

    def is_empty(self) -> bool:
        """Is this cookie jar empty?"""
        return len(self.jar) == 0

    def has(self, cookie: CookieType) -> bool:
        """Is "cookie" in this cookie jar?"""
        return cookie in self.jar

    def count(self, cookie: CookieType) -> int:
        """How many instances of "cookie" in cookie jar?"""
        return len([c for c in self.jar if c == cookie])

    def cardinality(self) -> int:
        """How many cookies in cookie jar?"""
        return len(self.jar)
