import unittest
from bridzik import Stash, cards, Card_colors, Card_values,\
                    RuleException, BridzikException, Card, Round

class StashCase(unittest.TestCase):
    def test_first_card(self):
        heart_7 = Card(Card_colors['HEARTS'], Card_values['C7'])
        s = Stash(3)
        s.add_card(3, heart_7)
        self.assertEqual(s.get_first_card(), heart_7)
        
        heart_8 = Card(Card_colors['HEARTS'], Card_values['C8']) 
        s.add_card(1, heart_8)
        self.assertNotEqual(s.get_first_card(), heart_8)
        self.assertEqual(s.get_first_card(), heart_7)

    def test_add_cart(self):
        heart_7 = Card(Card_colors['HEARTS'], Card_values['C7'])
        leaves_7 = Card(Card_colors['LEAVES'], Card_values['C7'])
        leaves_ace = Card(Card_colors['LEAVES'], Card_values['ACE'])
        bells_10 = Card(Card_colors['BELLS'], Card_values['C10'])
        acorns_10 = Card(Card_colors['ACORNS'], Card_values['C10'])
        s = Stash(1)

        s.add_card(1, heart_7)
        self.assertEqual(s._cards[1], heart_7)
        s.add_card(2, leaves_7)
        self.assertEqual(s._cards[2], leaves_7)
        s.add_card(3, leaves_ace)
        self.assertEqual(s._cards[3], leaves_ace)
        s.add_card(0, bells_10)
        self.assertEqual(s._cards[0], bells_10)

        self.assertRaises(BridzikException, s.add_card, card=bells_10, player=4)
        self.assertRaises(BridzikException, s.add_card, card=acorns_10, player=0)

    def test_get_winner(self):
        heart_7 = Card(Card_colors['HEARTS'], Card_values['C7'])
        leaves_7 = Card(Card_colors['LEAVES'], Card_values['C7'])
        acorns_ace = Card(Card_colors['ACORNS'], Card_values['ACE'])
        bells_10 = Card(Card_colors['BELLS'], Card_values['C10'])

        # only heart takes stash
        c1 = [leaves_7, heart_7, acorns_ace, bells_10] 
        s1 = Stash(0)
        for i in range(4):
            s1.add_card(i, c1[i])
        self.assertEqual(s1.get_winner(), 1)

        # highest color takes the stash
        leaves_8 = Card(Card_colors['LEAVES'], Card_values['C8'])
        leaves_lower = Card(Card_colors['LEAVES'], Card_values['LOWER'])
        c2 = [leaves_8, leaves_7, acorns_ace, leaves_lower]
        s2 = Stash(0)
        for i in range(4):
            s2.add_card(i, c2[i])
        self.assertEqual(s2.get_winner(), 3)

        # no matching color and no heart in stash
        c3 = [bells_10, leaves_lower, acorns_ace, leaves_8]
        s3 = Stash(0)
        for i in range(4):
            s3.add_card(i, c3[i])
        self.assertEqual(s3.get_winner(), 0)

        # highest heart takes the stash
        heart_upper = Card(Card_colors['HEARTS'], Card_values['UPPER'])
        c4 = [leaves_8, leaves_lower, heart_upper, heart_7]
        s4 = Stash(0)
        for i in range(4):
            s4.add_card(i, c4[i])
        self.assertEqual(s4.get_winner(), 2)

        # test exceptions
        s5 = Stash(3)
        self.assertRaises(BridzikException, s5.get_winner)
        
    def test_get_cards(self):
        heart_7 = Card(Card_colors['HEARTS'], Card_values['C7'])
        leaves_7 = Card(Card_colors['LEAVES'], Card_values['C7'])
        acorns_ace = Card(Card_colors['ACORNS'], Card_values['ACE'])
        bells_10 = Card(Card_colors['BELLS'], Card_values['C10'])
        s = Stash(0)
        self.assertEqual(s.get_cards(), {})
        s.add_card(2, heart_7)
        self.assertEqual(s.get_cards(), {2: heart_7})
        s.add_card(3, leaves_7)
        self.assertEqual(s.get_cards(), {2: heart_7, 3: leaves_7})
        s.add_card(0, acorns_ace)
        self.assertEqual(s.get_cards(), {2: heart_7, 3: leaves_7, 0: acorns_ace})
        s.add_card(1, bells_10)
        self.assertEqual(s.get_cards(), {2: heart_7, 3: leaves_7, 0: acorns_ace, 1: bells_10})

    def test_is_complete(self):
        heart_7 = Card(Card_colors['HEARTS'], Card_values['C7'])
        leaves_7 = Card(Card_colors['LEAVES'], Card_values['C7'])
        acorns_ace = Card(Card_colors['ACORNS'], Card_values['ACE'])
        bells_10 = Card(Card_colors['BELLS'], Card_values['C10'])
        s = Stash(0)
        c = [heart_7, leaves_7, acorns_ace, bells_10]
        for i in range(4):
            self.assertFalse(s.is_completed())
            s.add_card(i, c[0])
        self.assertTrue(s.is_completed())

    def test_get_active_player(self):
        heart_7 = Card(Card_colors['HEARTS'], Card_values['C7'])
        leaves_7 = Card(Card_colors['LEAVES'], Card_values['C7'])
        leaves_ace = Card(Card_colors['LEAVES'], Card_values['ACE'])
        bells_10 = Card(Card_colors['BELLS'], Card_values['C10'])
        s = Stash(1)

        self.assertEqual(s.get_active_player(), 1)
        s.add_card(1, heart_7)
        self.assertEqual(s.get_active_player(), 2)
        s.add_card(2, leaves_7)
        self.assertEqual(s.get_active_player(), 3)
        s.add_card(3, leaves_ace)
        self.assertEqual(s.get_active_player(), 0)
        s.add_card(0, bells_10)
        self.assertRaises(BridzikException, s.get_active_player)


class CardCase(unittest.TestCase):
    def test_hashable_consistent_with_eq(self):
        # vlastne __eq__ nesmie zrusit hashovatelnost (dict/set pouzitie)
        heart_7 = Card(Card_colors['HEARTS'], Card_values['C7'])
        self.assertEqual(hash(heart_7), hash(Card(Card_colors['HEARTS'], Card_values['C7'])))
        self.assertEqual(len(set(cards)), 32)
        self.assertEqual({Card_colors['HEARTS']: 1}[Card_colors['HEARTS']], 1)
        self.assertEqual({Card_values['ACE']: 1}[Card_values['ACE']], 1)


class RoundCase(unittest.TestCase):
    def test_round_constructor(self):
        self.assertRaises(BridzikException, Round, round_number=8, first_player=0)
        self.assertRaises(BridzikException, Round, round_number=0, first_player=4)
        
        for first_player in range(4):
            for round_n in range(8):
                r = Round(round_n, first_player)
                self.assertEqual(r.round_number, round_n)
                self.assertEqual(r.first_player, first_player)
                self.assertEqual(r.guesses, {})
                self.assertEqual(r.stashes, [])
                self.assertEqual(len(r.round_cards), 4*(8-round_n))
                for player_n in range(4):
                    self.assertEqual(
                        r.round_cards[player_n * (8-round_n) : (player_n+1) * (8-round_n)],
                        r.player_cards[player_n]
                    )
    
    def test_add_player_guess(self):
        r = Round(0, 1)

        self.assertRaises(BridzikException, r.add_player_guess, player=2, guess=0)

        r.add_player_guess(1, 1)
        self.assertEqual(r.guesses[1], 1)
        self.assertRaises(BridzikException, r.add_player_guess, player=1, guess=0)

        r.add_player_guess(2, 0)
        self.assertEqual(r.guesses[2], 0)
        r.add_player_guess(3, 2)
        self.assertEqual(r.guesses[3], 2)

        self.assertEqual(r.stashes, [])

        self.assertRaises(BridzikException, r.add_player_guess, player=0, guess=5)

        r.add_player_guess(0, 4)
        self.assertEqual(r.guesses[0], 4)
        self.assertEqual(r.stashes[0].first_player, 0)
    
    def test_get_highest_guessing_player(self):
        r1 = Round(0, 0)
        r1.add_player_guess(0, 2)
        r1.add_player_guess(1, 1)
        r1.add_player_guess(2, 3)
        self.assertRaises(BridzikException, r1.get_highest_guessing_player)
        r1.add_player_guess(3, 4)
        self.assertEqual(r1.get_highest_guessing_player(), 3)

        r2 = Round(0, 2)
        r2.add_player_guess(2, 5)
        r2.add_player_guess(3, 0)
        r2.add_player_guess(0, 1)
        r2.add_player_guess(1, 1)
        self.assertEqual(r2.get_highest_guessing_player(), 2)

    def test_get_active_player(self):
        r = Round(6, 1)
        self.assertEqual(r.get_active_player(), 1)
        r.add_player_guess(1, 1)
        self.assertEqual(r.get_active_player(), 2)
        r.add_player_guess(2, 0)
        self.assertEqual(r.get_active_player(), 3)
        r.add_player_guess(3, 1)
        self.assertEqual(r.get_active_player(), 0)
        r.add_player_guess(0, 2)
        self.assertEqual(r.get_active_player(), 0)

        r.play_card(0, r.player_cards[0][0])
        self.assertEqual(r.get_active_player(), 1)

    def test_play_card(self):
        shuffler = lambda list: None
        c0 = [
                Card(Card_colors['BELLS'], Card_values['UPPER']),
                Card(Card_colors['HEARTS'], Card_values['UPPER'])
        ]
        c1 = [
                Card(Card_colors['BELLS'], Card_values['C7']),
                Card(Card_colors['HEARTS'], Card_values['C10'])
        ]
        c2 = [
                Card(Card_colors['BELLS'], Card_values['ACE']),
                Card(Card_colors['BELLS'], Card_values['C8'])
        ]
        c3 = [
                Card(Card_colors['LEAVES'], Card_values['C7']),
                Card(Card_colors['BELLS'], Card_values['LOWER'])
        ]
        c = ['dummy']*24 + c0 + c1 + c2 + c3
        r = Round(6, 1, c, shuffler)
        r.add_player_guess(1, 0)
        r.add_player_guess(2, 0)
        r.add_player_guess(3, 1)
        self.assertRaises(BridzikException, r.play_card, player=0, card=c0[0]) # neukoncene tipovanie
        r.add_player_guess(0, 2)
        self.assertRaises(BridzikException, r.play_card, player=1, card=c1[0]) # mimo poradia

        r.play_card(0, c0[0])
        self.assertRaises(BridzikException, r.play_card, player=1, card=c1[1]) # cerven namiesto farby
        r.play_card(1, c1[0])
        r.play_card(2, c2[0])
        self.assertRaises(BridzikException, r.play_card, player=3, card=c3[0]) # ina farba
        r.play_card(3, c3[1])

        r.play_card(2, c2[1])
        r.play_card(3, c3[0])
        self.assertRaises(BridzikException, r.play_card, player=0, card=c0[0]) # uz zahrana karta
        r.play_card(0, c0[1])
        r.play_card(1, c1[1])

        self.assertTrue(len(r.stashes), 2)

    def test_is_completed(self):
        r = Round(7, 1)
        self.assertFalse(r.is_completed())
        r.add_player_guess(1, 0)
        r.add_player_guess(2, 0)
        r.add_player_guess(3, 1)
        r.add_player_guess(0, 1)

        r.play_card(3, r.player_cards[3][0])
        r.play_card(0, r.player_cards[0][0])
        r.play_card(1, r.player_cards[1][0])
        self.assertFalse(r.is_completed())
        r.play_card(2, r.player_cards[2][0])
        self.assertTrue(r.is_completed())

    def test_is_guessing_completed(self):
        r = Round(6, 1)
        self.assertFalse(r.is_guessing_completed())
        r.add_player_guess(1, 1)
        r.add_player_guess(2, 0)
        r.add_player_guess(3, 2)
        r.add_player_guess(0, 1)
        self.assertTrue(r.is_guessing_completed())

    def test_get_last_stash(self):
        r = Round(7, 0)
        self.assertIsNone(r.get_last_stash())
        r.add_player_guess(0, 1)
        r.add_player_guess(1, 1)
        r.add_player_guess(2, 0)
        r.add_player_guess(3, 0)
        self.assertEqual(r.get_last_stash(), r.stashes[0])
        r.play_card(0, r.player_cards[0][0])
        r.play_card(1, r.player_cards[1][0])
        r.play_card(2, r.player_cards[2][0])
        r.play_card(3, r.player_cards[3][0])
        self.assertEqual(r.get_last_stash(), r.stashes[0])
        r.stashes.append(Stash(2))
        self.assertEqual(r.get_last_stash(), r.stashes[1])

    def test_get_stashes_winner_summary(self):
        shuffler = lambda list: None
        c0 = [
                Card(Card_colors['BELLS'], Card_values['UPPER']),
                Card(Card_colors['HEARTS'], Card_values['UPPER'])
        ]
        c1 = [
                Card(Card_colors['BELLS'], Card_values['C7']),
                Card(Card_colors['HEARTS'], Card_values['C10'])
        ]
        c2 = [
                Card(Card_colors['BELLS'], Card_values['ACE']),
                Card(Card_colors['BELLS'], Card_values['C8'])
        ]
        c3 = [
                Card(Card_colors['LEAVES'], Card_values['C7']),
                Card(Card_colors['BELLS'], Card_values['LOWER'])
        ]
        c = ['dummy']*24 + c0 + c1 + c2 + c3
        r = Round(6, 1, c, shuffler)
        self.assertEqual(r.get_stashes_winner_summary(), [0]*4)
        r.add_player_guess(1, 0)
        r.add_player_guess(2, 0)
        r.add_player_guess(3, 1)
        r.add_player_guess(0, 2)

        r.play_card(0, c0[0])
        r.play_card(1, c1[0])
        r.play_card(2, c2[0])
        self.assertEqual(r.get_stashes_winner_summary(), [0]*4)
        r.play_card(3, c3[1])
        self.assertEqual(r.get_stashes_winner_summary(), [0, 0, 1, 0])

        r.play_card(2, c2[1])
        r.play_card(3, c3[0])
        r.play_card(0, c0[1])
        r.play_card(1, c1[1])
        self.assertEqual(r.get_stashes_winner_summary(), [1, 0, 1, 0])

    def test_get_points_summary(self):
        shuffler = lambda list: None
        c0 = [
                Card(Card_colors['BELLS'], Card_values['UPPER']),
                Card(Card_colors['HEARTS'], Card_values['UPPER'])
        ]
        c1 = [
                Card(Card_colors['BELLS'], Card_values['C7']),
                Card(Card_colors['HEARTS'], Card_values['C10'])
        ]
        c2 = [
                Card(Card_colors['BELLS'], Card_values['ACE']),
                Card(Card_colors['BELLS'], Card_values['C8'])
        ]
        c3 = [
                Card(Card_colors['LEAVES'], Card_values['C7']),
                Card(Card_colors['BELLS'], Card_values['LOWER'])
        ]
        c = ['dummy']*24 + c0 + c1 + c2 + c3
        r = Round(6, 1, c, shuffler)
        r.add_player_guess(1, 0)
        r.add_player_guess(2, 1)
        r.add_player_guess(3, 0)
        r.add_player_guess(0, 2)

        r.play_card(0, c0[0])
        r.play_card(1, c1[0])
        r.play_card(2, c2[0])
        r.play_card(3, c3[1])

        r.play_card(2, c2[1])
        r.play_card(3, c3[0])
        r.play_card(0, c0[1])
        r.play_card(1, c1[1])
        self.assertEqual(r.get_points_summary(), [0, 10, 11, 10])



if __name__ == '__main__':
    unittest.main(verbosity=2)
