'''
CMSI 2130 - Classwork 1
Author: SOLUTION

Complete each exercise as described in the Classwork spec, ensuring that its subsequent
unit test is satisfied by running the associated review_tests.py as indicated in the spec.
'''

from dataclasses import *
from typing import *
import copy

# Warmup Problems
# ---------------------------------------------------------------------------

def is_sublist(list1: list[int], list2: list[int]) -> bool:
    '''
    Returns a bool designating whether or not all ints in list1 also appear
    in list2
    
    Parameters:
        list1 (list[int]):
            A list of ints for whom membership is being checked against list2
        list2 (list[int]):
            A list of ints against which list1's members are being checked
    
    Returns:
          bool:
              True if all of list1's ints are also in list2 without needing the
              reverse to be true
        
    Examples:
        is_sublist([1, 3, 2, 1, 3], [1, 2, 3]) => True
        is_sublist([1, 3, 2, 1, 3], [1, 2]) => False
    '''
    set2 = set(list2)
    for item1 in list1:
        if not item1 in set2: return False
    return True

    # Also a good solution, if you like playing programming golf:
    # set2 = set(list2)
    # return all(item1 in set2 for item1 in list1)
    
    # [!] A less good solution would have been to convert BOTH lists
    # to sets and then use issubset -- this would be less computationally
    # and space efficient (though not asymptotically) because you don't
    # need to convert list1 to a set

# Forneymon Problems
# ---------------------------------------------------------------------------
class Forneymon:
    '''
    Skeleton class outline for hit new blockbuster game: Forneymon.

    Attributes:
        _name (str):
            The Forneymon's name like "Burneymon" or "Dampymon"
        _health (int):
            The Forneymon's remaining hit points
        _friends (set["Forneymon"]):
            A set of Forneymon references that point to other Forneymon with
            whom this one is friends
    '''

    def __init__(self, name: str, health: int, friends: set["Forneymon"]):
        '''
        Default constructor for initializing a new Forneymon with the given
        name, health (number of hit points), and set of friends

        Parameters:
            name (str):
                The Forneymon's name like "Burneymon" or "Dampymon"
            health (int):
                The Forneymon's remaining hit points
            friends (set["Forneymon"]):
                A set of Forneymon references that point to other Forneymon with
                whom this one is friends
        '''
        self._name: str = name
        self._health: int = health
        self._friends: set["Forneymon"] = copy.deepcopy(friends)
    
    def __eq__(self, other: Any) -> bool:
        if other is None: return False
        if not isinstance(other, Forneymon): return False
        return self._name == other._name and self._health == other._health
    
    def __hash__(self) -> int:
        return hash((self._name, self._health))
    
    def __lt__(self, other: "Forneymon") -> bool:
        return self._health < other._health
    
    def __str__(self) -> str:
        return self._name * 2
    
    def add_friend(self, other: "Forneymon") -> None:
        '''
        Adds a reference to the given Forneymon to the set of this Forneymon's friends.
        
        Parameters:
            other (Forneymon):
                The friend to add to this Forneymon's set of friends.
        '''
        self._friends.add(other)
    
    def lose_friend(self, other: "Forneymon") -> None:
        '''
        Removes a reference to the given Forneymon from the set of this Forneymon's friends.
        So sad :(
        
        Parameters:
            other (Forneymon):
                The friend to remove from this Forneymon's set of friends.
        '''
        self._friends.remove(other)
        
    def get_friends(self) -> set["Forneymon"]:
        '''
        Returns a deep copy of the set of this Forneymon's friends.
        
        Returns:
            set[Forneymon]:
                A copy of this Forneymon's set of friends.
        '''
        return copy.deepcopy(self._friends)
