package com.pkrharrison.gameplay;

import static org.junit.Assert.*;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import org.junit.Test;

import com.pkrharrison.gameplay.Card;
import com.pkrharrison.gameplay.Card.Rank;
import com.pkrharrison.gameplay.Card.Suit;

public class CardTest {

	@Test
	public void testCardIds() {
		Set<Integer> allIds = new HashSet<Integer>(); 
		for (Card card: Card.STD_DECK) {
			// verify each card in the standard deck has a unique id, 
			assertTrue(allIds.add(card.getCardId())); 		
		}
		
		// verify that there are 52 unique ids 
		assertTrue(allIds.size() == 52);
		
		// assert each id corresponds to its index in the standard deck
		for (int i = 0; i < 52; i++)  {
			assertTrue(Card.STD_DECK[i].getCardId() == i); 
		}
		
		
	}
	
	@Test
	public void testConstructors() {
		Card card = new Card(Rank.ACE, Suit.CLUBS);
		Card card2 = new Card('A', 'c');
		assertTrue(card.rank().equals(Rank.ACE));
		assertTrue(card.suit().equals(Suit.CLUBS));
		assertTrue(card2.rank().equals(Rank.ACE));
		assertTrue(card2.suit().equals(Suit.CLUBS));
		assertTrue(card.equals(card2));
	}
	
	@Test
	public void testStdDeckOrdering() {
		// test that the standard deck is arranged in descending order
		Card lastCard = null; 
		for (Card card : Card.STD_DECK) {
			if (lastCard != null) {
				assertTrue(lastCard.rank().compareTo(card.rank()) >= 0);
				if (lastCard.rank().compareTo(card.rank()) == 0) {
					assertTrue(lastCard.suit().compareTo(card.suit()) > 0); 
				}
			}
			lastCard = card; 			
		}
	}
	
	@Test
	public void testRanks() {
		Rank[] ranks = Rank.values();
		for (int i = 0; i < ranks.length; i++) {		
			assertTrue(Rank.getRank(ranks[i].getChar()).equals(ranks[i]));
			assertTrue(ranks[i].inc().equals(ranks[Math.min(i+1, ranks.length-1)])); 
			assertTrue(ranks[i].dec().equals(ranks[Math.max(i-1, 0)]));
		}
	}
	
	@Test
	public void testSuits() {
		Suit[] suits = Suit.values(); 
		for (int i = 0; i < suits.length; i++) {		
			assertTrue(Suit.getSuit(suits[i].getChar()).equals(suits[i]));
		}
	}
	
	@Test
	public void testLongToString() {
		assertTrue(Card.longToString(0).equals("")); 
		
		for (int i = 0; i < 52; i++) {
			long val = (0x01L << i); 
			assertEquals(2, Card.longToString(val).length());			
		}
		
		for (int i = 0; i < 52; i++) {
			for (int j = i + 1; j < 52; j++) {
				long val = (0x01L << i); 
				val |= (0x01L << j); 
				assertEquals(4,  Card.longToString(val).length());
			}
		}
		
		long val = (0x01L << 52) - 1; 
		assertEquals(52*2, Card.longToString(val).length());
		
	}

	@Test
	public void testCardsToLong() {
		
		ArrayList<Card> arr = new ArrayList<>(); 
		for (int i = 0; i <= 52; i++) {
			
			Card[] cards = arr.toArray(new Card[0]); 
			long val = Card.cardsToLong(cards);
			
			assertEquals(i, Long.bitCount(val));
			
			Card[] newarr = Card.stringToCards(Card.longToString(val));
			assertTrue(new HashSet<Card>(Arrays.asList(newarr)).equals(new HashSet<Card>(arr))); 
			if (i < 52) {
				arr.add(Card.STD_DECK[i]); 
			}
		}
	}
	
	@Test
	public void testCardsToString() {
		ArrayList<Card> arr = new ArrayList<>(); 
		for (int i = 0; i <= 52; i++) {
			
			Card[] cards = arr.toArray(new Card[0]); 
			String val = Card.cardsToString(cards);
			
			assertEquals(2*cards.length, val.length());
			
			Card[] newarr = Card.stringToCards(val);
			assertTrue(new HashSet<Card>(Arrays.asList(newarr)).equals(new HashSet<Card>(arr))); 
			if (i < 52) {
				arr.add(Card.STD_DECK[i]); 
			}
		}
		
	}
	
	@Test
	public void testStringToCards() {
		assertEquals(0, Card.stringToCards("").length);
		
		Rank[] ranks = Rank.values(); 
		Suit[] suits = Suit.values(); 
		
		for (Rank rank : ranks) {
			for (Suit suit : suits) {
				String str = ""+rank.getChar() + ""+suit.getChar();
				Card[] arr = Card.stringToCards(str);
				assertEquals(1, arr.length); 
				assertTrue(arr[0].equals(new Card(rank, suit)));  
			}
		}

		
	}
	
	@Test 
	public void testStringToLong() {
		assertEquals(0L, Card.stringToLong(""));

		
		Rank[] ranks = Rank.values(); 
		Suit[] suits = Suit.values(); 
		
		for (Rank rank : ranks) {
			for (Suit suit : suits) {
				String str = ""+rank.getChar() + ""+suit.getChar();				
				
				long val = Card.stringToLong(str);
				String card = Card.longToString(val);
				
				assertEquals(str, card); 				  
			}
		}


		
	}
	
}
