class BagTest(unittest.TestCase):
    def setUp(self):
        self.bag = Bag()

    def tearDown(self):
        self.bag = None

    def test_can_check_bag_to_see_if_empty(self):
        self.assertTrue(self.bag.is_empty())

    def test_can_return_item_count_when_empty(self):
        self.assertEqual(self.bag.item_count(), 0)
    
    def test_after_adding_item_bag_is_no_longer_empty(self):
        item = Item("rock")
        self.bag.add(item)
        self.assertFalse(self.bag.is_empty())

    def test_can_add_item_to_bag(self):
        self.assertEqual(self.bag.item_count(), 0)
        item = Item("thing")
        self.bag.add(item)
        self.assertEqual(self.bag.item_count(), 1)
       
    def bag_n_dump(self, name):
        item = Item(name)
        self.bag.add(item)
        return self.bag.dump()

    def test_dumped_pile_has_count(self):
        pile = self.bag_n_dump('fuzz')

        self.assertEqual(len(pile), 1)
        fuzz = pile['fuzz']
        self.assertEqual(fuzz["count"], 1)

    def test_dumped_pile_has_name(self):
        pile = self.bag_n_dump('cheese')

        cheese = pile['cheese']
        self.assertEqual(cheese["name"], "cheese")

    def test_dumped_pile_has_item(self):
        pile = self.bag_n_dump('Amulet of Wendor')

        amulet = pile['Amulet of Wendor']
        item = amulet['item']
        self.assertIsInstance(item, Item)

    def test_can_look_in_bag(self):
        stick = Item("stick")
        self.bag.add(stick)
        seen = self.bag.look()
        self.assertIn("1 stick", seen)
    
    def test_can_fund_how_many_of_an_item_are_in_bag(self):
        planet = Item("planet")
        self.bag.add(planet)
        self.bag.add(planet)
        self.assertEqual(2, self.bag.how_many("planet"))

    def test_how_many_handles_no_items_of_type(self):
        self.assertEqual(0, self.bag.how_many("jabberwocky"))

    def test_adding_multiple_of_the_same_item_increases_item_count(self):
        butter = Item("butter")
        self.bag.add(butter)
        self.bag.add(butter)
        self.assertEqual(2, self.bag.item_count())
        self.bag.add(butter)
        self.assertEqual(3, self.bag.item_count())

    def test_can_add_many(self):
        turtle = Item("turtle")
        self.bag.add_many(turtle, 5)
        self.assertEqual(5, self.bag.how_many("turtle"))
        self.assertEqual(5, self.bag.item_count())

    def test_can_remove_an_item(self):
        self.bag.add(Item("cheezburger"))
        removed_count, item = self.bag.remove("cheezburger")
        self.assertEqual(1, removed_count)
        self.assertEqual("cheezburger", item.name)
        self.assertIsInstance(item, Item)
        self.assertEqual(0, self.bag.how_many("cheezburger"))
    
    def test_cannot_remove_an_item_that_is_not_there(self):
        removed_count, item = self.bag.remove("Kaiser Soze")
        self.assertEqual(0, removed_count)
        self.assertIsNone(item)
        self.assertEqual(0, self.bag.how_many("Kaiser Soze"))

    def test_removed_items_behave_like_items_that_never_existed(self):
        self.bag.add(Item("smoke"))
        removed_count, item = self.bag.remove("smoke")
        self.assertEqual(1, removed_count)

        removed_count, item = self.bag.remove("smoke")
        self.assertEqual(0, removed_count)
        self.assertIsNone(item)
        self.assertEqual(0, self.bag.how_many("smoke"))
        self.assertEqual(0, self.bag.item_count())
        
    def test_remove_many_items_from_bag_at_once(self):
        self.bag.add_many(Item("blind mouse"), 5)
        removed_count, item = self.bag.remove_many("blind mouse", 3)
        self.assertEqual(3, removed_count)
        self.assertEqual(2, self.bag.how_many("blind mouse"))
        self.assertEqual("blind mouse", item.name)

    def test_remove_many_items_will_remove_only_as_many_as_exist(self):
        self.bag.add_many(Item("rhymes"), 5)
        removed_count, item = self.bag.remove_many("rhymes", 7)
        self.assertEqual(5, removed_count)
        self.assertEqual(0, self.bag.how_many("rhymes"))
        self.assertEqual("rhymes", item.name)
        self.assertTrue(self.bag.isEmpty())

    def test_adding_multiple_of_the_same_item_increases_item_count(self):
        butter = Item("butter")
        self.bag.add(butter)
        self.bag.add(butter)
        self.assertEqual(2, self.bag.item_count())
        self.bag.add(butter)
        self.assertEqual(3, self.bag.item_count())

    def test_can_remove_an_item(self):
        self.bag.add(Item("cheezburger"))
        removed_count, item = self.bag.remove("cheezburger")
        self.assertEqual(1, removed_count)
        self.assertEqual("cheezburger", item.name)
        self.assertIsInstance(item, Item)
        self.assertEqual(0, self.bag.how_many("cheezburger"))
    
    def test_cannot_remove_an_item_that_is_not_there(self):
        removed_count, item = self.bag.remove("Kaiser Soze")
        self.assertEqual(0, removed_count)
        self.assertIsNone(item)
        self.assertEqual(0, self.bag.how_many("Kaiser Soze"))

    def test_removed_items_behave_like_items_that_never_existed(self):
        self.bag.add(Item("smoke"))
        removed_count, item = self.bag.remove("smoke")
        self.assertEqual(1, removed_count)

        removed_count, item = self.bag.remove("smoke")
        self.assertEqual(0, removed_count)
        self.assertIsNone(item)
        self.assertEqual(0, self.bag.how_many("smoke"))
        self.assertEqual(0, self.bag.item_count())
        
    def test_remove_many_items_from_bag_at_once(self):
        self.bag.add_many(Item("blind mouse"), 5)
        removed_count, item = self.bag.remove_many("blind mouse", 3)
        self.assertEqual(3, removed_count)
        self.assertEqual(2, self.bag.how_many("blind mouse"))
        self.assertEqual("blind mouse", item.name)

    def test_remove_many_items_will_remove_only_as_many_as_exist(self):
        self.bag.add_many(Item("rhymes"), 5)
        removed_count, item = self.bag.remove_many("rhymes", 7)
        self.assertEqual(5, removed_count)
        self.assertEqual(0, self.bag.how_many("rhymes"))
        self.assertEqual("rhymes", item.name)
class InventoryBagTest(unittest.TestCase):
    def setUp(self):
        self.bag = Bag()

    def tearDown(self):
        self.bag = None

    def test_can_use_bag_to_hold_items_that_are_found(self):
        # Inara is curious to know if she is carrying any items. She checks her bag to see what is in her inventory
        self.assertIsNotNone(self.bag)

        # Her inventory bag is empty and contains no items
        self.assertTrue(self.bag.is_empty())
        self.assertEqual(self.bag.item_count(), 0)

        # Inara sees a pile of rocks nearby and lacking any other items decides to put them into her bag. She puts one rock into her bag.  Looking into her bag, it is no longer empty, she has one rock in it.
        rock = Item("rock")
        self.bag.add(rock)
        self.assertFalse(self.bag.is_empty())
        self.assertEqual(self.bag.item_count(), 1)

        # note perhaps this should be checking for look() rather than dump()
        seen = self.bag.look()
        self.assertIn("1 rock", seen)

        # She puts two more rocks into her bag. Looking into her bag, she sees that it now contains 3 rocks
        self.bag.add(rock)
        another_rock = Item("rock")
        self.bag.add(another_rock)
        self.assertEqual(self.bag.item_count(), 3)

        seen = self.bag.look()
        self.assertIn("3 rock", seen)

        # Inara, happy to have something in her bag, starts on her adventure.  Before long she stumbles on something in a dark shadow.  Picking it up she sees that it is a shiny dagger.  After putting it into her bag she checks her bag to ensure that it is safe inside.  She now has 3 rocks and a dagger in her bag.
        dagger = Item("dagger")
        self.bag.add(dagger)

        seen = self.bag.look()
        self.assertIn("1 dagger", seen)

    def test_items_are_removed_from_bag(self):
        # Mary Poppins is summoned by the children to bring joy to their lives.  She is holding a bag.  It contains a hatrack, a carpet, two lollipops and 5 brooms.  It also contains medicine, a teaspoon and a few spoonfulls of sugar.
        self.bag.add(Item("hatrack"))
        self.bag.add(Item("carpet"))
        self.bag.add(Item("Thing 1"))
        self.bag.add(Item("Thing 2"))
        self.bag.add(Item("Cat"))
        self.bag.add(Item("Hat"))
        self.bag.add(Item("lollipop"))
        self.bag.add(Item("lollipop"))
        self.bag.add(Item("lollipop"))
        self.bag.add_many(Item("lollipop"), 5)
        self.bag.add(Item("medicine"))
        self.bag.add(Item("teaspoon"))
        self.bag.add_many(Item("spoonful of sugar"), 3)

        # Her bag contains at least 14 items.
        total_items = self.bag.item_count()
        self.assertTrue(total_items >= 14)

        # Mary enters the unhappy home with a messy room and tells the kids to tidy up.  The children don't want to tidy up so she starts searching through her bag.  She removes a hatrack but puts it back
        hatrack_count = self.bag.how_many("hatrack")
        self.bag.remove("hatrack")
        self.assertEqual(hatrack_count - 1, self.bag.how_many("hatrack"))
        self.bag.add(Item("hatrack"))
        self.assertEqual(hatrack_count, self.bag.how_many("hatrack"))

        # Mary then removes the medicine.
        med_count, medicine = self.bag.remove("medicine")
        self.assertEqual(med_count, 1)
        self.assertIsInstance(medicine, Item)
        self.assertEqual(medicine.name, "medicine")

        # She tries to remove two teaspoons but only has one. 
        # She removes the teaspoon
        teaspoon_count, teaspoon = self.bag.remove_many("teaspoon", 2)
        self.assertEqual(teaspoon_count, 1)
        self.assertEqual(self.bag.how_many("teaspoon"), 0)
        self.assertEqual(teaspoon.name, "teaspoon")

        # She removes one spoonful of sugar and begins to sing
        sugar_count, sugar = self.bag.remove("spoonful of sugar")
        self.assertEqual(1, sugar_count)

        # She removes another spoonful of sugar and continues to sing.
        sugar_count, sugar = self.bag.remove("spoonful of sugar")
        self.assertEqual(1, sugar_count)

        # She removes the third spoonful of sugar smiles to herself and takes her medicine. 
        sugar_count, sugar = self.bag.remove("spoonful of sugar")
        self.assertEqual(1, sugar_count)

        # Mary now has 5 less items in her bag
        new_total_items = self.bag.item_count()
        self.assertEqual(5, total_items - new_total_items)

        # She returns the medicine and spoon to her bag and with at least 12 items in her bag continues with her work
        self.bag.add(teaspoon)
        self.bag.add(medicine)
        self.assertTrue(self.bag.item_count() >= 12)

    def test_items_are_dumped_into_a_pile(self):

        # Items can be dumped into a pile and sorted through
        # This is a remnant of my inital version implementation of the InventoryBagAddAndLookTestCase which has since been converted to the look function.  Not sure if I should keep dump()
        # This may be useful in the future for saving the state of the bag
        rock = Item("rock")
        self.bag.add(rock)

        pile = self.bag.dump()
        item_list = pile.keys()
        self.assertEqual(len(item_list), 1)
        self.assertIn("rock", item_list)
        item = pile["rock"]
        self.assertEqual(item["name"], "rock")
        self.assertEqual(item["count"], 1)