//
//  hand.java
//  cribbage
//

public class Hand {
	static final int maxCards = 6;
	
	private Card[] hand;
	private int numCards;
	
	// constructor
	Hand () {
		hand = new Card[maxCards];
		numCards = 0;
	}
	
	// copy constructor
	public Hand (Hand aHand) {
		hand = new Card[maxCards];
		numCards = 0;
		
		for (int i = 0; i < aHand.numCards(); i++) {
			this.putCard(aHand.getCard(i));
		}
	}
	
	// accessors
	public Card getCard(int pos) {
		//Pre:  pos in 0..maxCards-1
		//Post: Card is still in Hand
		return hand[pos];
	}
	
	public int numCards() {
		//Pre:  The hand is created
		//Post: The number of Cards in hand is returned
		return numCards;
	}
	
	// mutators
	public void putCard(Card aCard) {
		//Pre:  The Hand is created
		//Post: aCard is inserted into the hand
		hand[numCards++] = aCard;
	}
	
	public void deleteCard(int pos) {
		//Pre:  The Hand is created, there is a card in location pos
		//Post: The card is removed from the hand
		
		// move all the cards down in the hand
		for (int i=pos; i<numCards-1; i++)
			hand[i] = hand [i+1];
		hand[numCards-1] = null;
		numCards--;
		
	}
	
	public int tally () {
		// Pre:  the hand is created and has four or five cards
		// Post: The number of points earned by the hand is tallied and returned
		int flushCards = 0;
		int total = 0;
		int ranks[] = new int[15];
		
		Card currentCard;
		

		for (int i = 0; i < numCards; i++) {
			Card baseCard = this.getCard(i);
			
			// Check for nobs (having a jack of the same suit as the cut-card)
			if (( numCards == 5) && (i != 4) && ( baseCard.getRank() == 11 ) && ( baseCard.getSuit() == this.getCard(4).getSuit() ) ) {
				total += 1; // One for his nob
				 //System.out.println("Player scored "+total+" after 1 for his nob.");
			}
			
			// Find all pairs
			for (int j = (i+1); j < numCards; j++) {
				Card compCard = this.getCard(j);

				if (baseCard.getRank() == compCard.getRank()) {
					total += 2; // Give two points
					 //System.out.println("Player scored "+total+" after the found pair.");
				}
			}
		}
		
		// Find all totals of 15:
		// Count in binary from 00000 to 11111 (or 2^numCards),
		// all the 1's in the number correspond to the card posistion in the hand.
		// Add together all the cards represented, see if they equal 15
		String testBinary;
		int cardsTotal;
		int testBinaryLength;
		for (int i = 0; i < ((int) Math.pow(2.0,(float)numCards)); i++) { // Note 2^5 = 32 decimal = 0010 0000 binary
			// Convert the current test number to a binary string
			testBinary = Integer.toBinaryString(i);
			testBinaryLength = testBinary.length();
			// We also need to buffer the binary string with 0's
			for (int zeroAdder = 0; zeroAdder < (numCards - testBinaryLength); zeroAdder++)
				testBinary = '0'+ testBinary;
			// Check for comparisions
			cardsTotal = 0;
			for (int cardPos = 0; cardPos < numCards; cardPos++) {
				if (testBinary.charAt(cardPos) == '1') {
					if (hand[cardPos].getRank() > 10)
						cardsTotal += 10;
					else
						cardsTotal += hand[cardPos].getRank();
				}
			}
			if (cardsTotal == 15) {
				total += 2;			
				//System.out.println("Found pair in "+testBinary); // Debugging code
			    //System.out.println("Player scored "+total+" after the flush of 4.");
			}
		}
		
		// Check for a flush:
		int theSuit = this.getCard(0).getSuit();
		for (int i = 0; i < numCards-1; i++) {
			currentCard = this.getCard(i);
			// For a match in suit;
			if (theSuit == currentCard.getSuit())
				flushCards++; // Increase the count of flush cards
		}
		if (flushCards == 4) { // Note that we assume the hand only has four cards, plus the cut card.
			total += 4; // Give four points for the flush
			 //System.out.println("Player scored "+total+" after the flush of 4.");
		}
		
		// Check for the cut card suit
		if ( (flushCards == 4) && (theSuit == this.getCard(4).getSuit()) ) {
			total++; // Give one more point for matching the cut card suit.
			 //System.out.println("Player scored "+total+" after the flush of 5.");
		}
		

		
		// Lastly, check for runs:
		// We create and array of ranks, counting the number of each card in the hand,
		// If 3 or more cards are next to each other, give points
		// If there are cards that form multiple runs (ie 2-3-3-4 forms two runs of three),
		// Decrement the count of the card and run through the array again.
		
		// Initialize a ranks array to all 0's
		for (int i = 0; i < ranks.length; i++)
			ranks[i] = 0;
		
		// For each card, increment its corresponding array location
		for (int i = 0; i < numCards; i++) {
			currentCard = this.getCard(i);
			ranks[currentCard.getRank()]++;
		}
		
		// Run through the array for cards that are in sequence
		int seqCards = 0;
		boolean prevCardSeq = false;
		boolean dualRun = true;
		
		while (dualRun) { // Will be set to true the first time, and if a Royale or Grand-Royale is possible
			dualRun = false;
			for (int i = 0; i < ranks.length; i++) {
				if (ranks[i] > 0) {
					// System.out.println("Found "+ranks[i]+" card rank "+i);
					if (!prevCardSeq) // If the current card did not have a card before it, reset to 0
						seqCards = 0;

					// Check if this card might be part of more than one run
					// This condition exists if it is next to another card in rank,
					// and more than one of the current rank exists.
					// We also check to see if another card has already flagged this attribute,
					// because want to be capable of handling "Grand-Royales"
					
					if ( (prevCardSeq | (ranks[i+1] > 0) ) && (ranks[i] > 1) && !dualRun) {
						ranks[i]--;
						dualRun = true;
					}
					
					
					seqCards++;
					// System.out.println("There are currently "+seqCards+" cards in the sequence (Rank "+i+")");
					prevCardSeq = true;
					
					if (seqCards == 3)
						total = total + 3; // Give three initial points for getting a run of 3
					else if (seqCards == 4)
						total++; // Give an additional point for a run of 4
					else if (seqCards == 5)
						total++; // Give one more additional point for a run of 5
				}
				
				else
					prevCardSeq = false;
			}
		}
		
		 //System.out.println("Player scored "+total+" after the runs.");
		
		return total;
	}
	
	public int getPeggingCard (CurrentPlay cardsInPlay) {
		// Pre:  The hand exists and may or may not have any cards
		// Post: A card is returned, the best to use on cardsInplay. It is removed from the hand.
		// The player must decide which card is best to score pegging points
		
		int numPlayCards = cardsInPlay.numCards();
		int playTotal = cardsInPlay.getTotal();

		
		if (numCards > 0) {
			
			// If no cards are currently in play, put in a random card
			if (numPlayCards == 0) {
				return 0;
			}
			else {
				
				// Call the currentPlay.pointsScored() function for each combonation of numCards-1 cards
				// We'll play the card that returns the most points.
				CurrentPlay testPlay;
				int returnPos = 0;
				int highestScore = -1;
				int testScore;
				int cardRank;
				
				for (int i=0; i < numCards; i++) {
				
					cardRank = hand[i].getRank();
					if (cardRank > 10)
						cardRank = 10;
				    
					testPlay = new CurrentPlay(cardsInPlay);
					
					if (testPlay.getTotal() + cardRank <= 31) {
						testScore = testPlay.playCard(hand[i]);
						
						if (testScore > highestScore) {
							highestScore = testScore;
							returnPos = i;
						}
					}
				}
				
				// Return the card that scored the most points
				if (highestScore > -1) {
					return returnPos;
				}
				
			}
		}
		// Otherwise we have no cards that are playable 
		return -1;
	}

	// Testing Display function
	public void display() {
		for (int i=0; i < numCards; i++) {
			System.out.println(hand[i].display());
		}
	}
	
	// Testing function
	public static void main(String[] str) {
		/*
		Hand myHand = new Hand();
		Deck myDeck = new Deck();
		Card currentCard;
		
		// populate the hand
		for (int i = 0; i<maxCards; i++) {
			currentCard = myDeck.getCard();
			myHand.putCard(currentCard);
		}
		
		System.out.println("Hand initialized to:");
		// display the hand
		for (int i = 0; i<myHand.numCards(); i++) {
			currentCard = myHand.getCard(i);
			System.out.println("Card Suit: "+currentCard.getSuit()+" Rank: "+currentCard.getRank());
		}
		
		//System.out.println("\nRemoving Card in Position 2");
		currentCard = myHand.getCard(2);
		myHand.deleteCard(2);
		
		System.out.println("The discard is the card:");
			System.out.println("Card Suit: "+currentCard.getSuit()+" Rank: "+currentCard.getRank());
		
		System.out.println("The remaining cards are:");
		for (int i = 0; i<myHand.numCards(); i++) {
			currentCard = myHand.getCard(i);
			System.out.println("Card Suit: "+currentCard.getSuit()+" Rank: "+currentCard.getRank());
		}
		*/
		Hand myHand = new Hand();
		int score;
		
		// Define what cards we want:
		// Card(suit,rank) Club-0 Heart-1 Spade-2 Diamond-3
		Card card1 = new Card(3,2);
		Card card2 = new Card(0,11);
		Card card3 = new Card(2,3);
		Card card4 = new Card(0,3);
		Card card5 = new Card(0,8); // Would be the crib cut card
		
		// Populate the hand
		myHand.putCard(card1);
		myHand.putCard(card2);
		myHand.putCard(card3);
		myHand.putCard(card4);
		myHand.putCard(card5);
		
		System.out.println("The Player has the hand:");
		myHand.display();
		
		// Score the hand
		score = myHand.tally();
		
		System.out.println("The Player scored "+score+" points.");
		
	}
}
