# CookieJar ADT: Python Dictionary Implementation (Standard Mutable)
#
# 345678901234567890123456789012345678901234567890123456789012345678901234567890
#
# 2018-10-10: Initial Python version loosely based on Scala version
# 2018-10-11: Corrected/improved statement of invariant
# 2022-04-20: Improved comments. Reformatted with black.

from typing import Dict, Generic, TypeVar
from dataclasses import dataclass, field  # 3.7+

from cookiejar import CookieJarABC, CookieType

# Class CookieJarDict implements the CookieJar ADT using Python 3
# dictionary data type to implement the jar (mathematical bag) of
# cookies.  The keys of the dictionary are the cookie types and the
# value is the count of cookies of that type in the jar.  If the count
# is zero, then that key does not appear in the map.
#
#   IMPLEMENTATION INVARIANT:
#     (ForAll c, i : c IN CookieType &&
#                    i == OCCURRENCES(c,Bag(self)) ::
#         (i > 0  => self.jar[c] == i) &&
#         (i == 0 => c not in self.jar) )

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


@dataclass
class CookieJarDict(CookieJarABC, Generic[CookieType]):
    jar: Dict[CookieType, int] = field(default_factory=dict)

    def put_in(self, cookie: CookieType) -> None:
        """Insert "cookie" into this cookie jar"""
        if cookie in self.jar:
            self.jar[cookie] += 1
        else:
            self.jar[cookie] = 1

    def eat(self, cookie: CookieType) -> None:
        """Remove "cookie" from this cookie jar"""
        if cookie in self.jar:  # Assumes value is int
            if self.jar[cookie] > 1:
                self.jar[cookie] -= 1
            else:
                del self.jar[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
