package pokercore

func (c Cards) scoreStraightFlush() HandScore {
	if !c.containsStraightFlush() {
		panic("hand must be a straight flush to score it")
	}
	return ScoreStraightFlush + 1000*c.HighestRank().Score()
}

func (c Cards) scoreFlush() HandScore {
	if !c.containsFlush() {
		panic("hand must be a flush to score it")
	}
	var score HandScore
	for _, card := range c {
		score += card.Rank.Score()
	}
	return ScoreFlush + score
}

func (c Cards) scoreHighCard() HandScore {
	if !c.isUnmadeHand() {
		panic("hand must be a high card to score it")
	}
	var score HandScore
	for _, card := range c {
		score += card.Rank.Score()
	}
	return ScoreHighCard + score
}

func (c Cards) pairRank() Rank {
	if !c.containsPair() {
		panic("hand must have a pair to have a pair rank")
	}
	sorted := c.SortByRankAscending()
	if sorted[0].Rank == sorted[1].Rank {
		return sorted[0].Rank
	}
	if sorted[1].Rank == sorted[2].Rank {
		return sorted[1].Rank
	}
	if sorted[2].Rank == sorted[3].Rank {
		return sorted[2].Rank
	}
	if sorted[3].Rank == sorted[4].Rank {
		return sorted[3].Rank
	}
	panic("nope")
}

func (c Cards) pairFirstKicker() Card {
	if !c.containsPair() {
		panic("hand must have a pair to have a first kicker")
	}
	sorted := c.SortByRankAscending()
	if sorted[0].Rank == sorted[1].Rank {
		return sorted[4]
	}
	if sorted[1].Rank == sorted[2].Rank {
		return sorted[4]
	}
	if sorted[2].Rank == sorted[3].Rank {
		return sorted[4]
	}
	if sorted[3].Rank == sorted[4].Rank {
		return sorted[2]
	}
	panic("nope")
}

func (c Cards) pairSecondKicker() Card {
	if !c.containsPair() {
		panic("hand must have a pair to have a second kicker")
	}
	sorted := c.SortByRankAscending()
	if sorted[0].Rank == sorted[1].Rank {
		// first kicker is [4]
		return sorted[3]
	}
	if sorted[1].Rank == sorted[2].Rank {
		// first kicker is [4]
		return sorted[3]
	}
	if sorted[2].Rank == sorted[3].Rank {
		// first kicker is [4]
		return sorted[1]
	}
	if sorted[3].Rank == sorted[4].Rank {
		// first kicker is [2]
		return sorted[1]
	}
	panic("nope")
}

func (c Cards) pairThirdKicker() Card {
	if !c.containsPair() {
		panic("hand must have a pair to have a third kicker")
	}
	sorted := c.SortByRankAscending()
	if sorted[0].Rank == sorted[1].Rank {
		// first kicker is [4]
		// second kicker is [3]
		return sorted[2]
	}
	if sorted[1].Rank == sorted[2].Rank {
		// first kicker is [4]
		// second kicker is [3]
		return sorted[0]
	}
	if sorted[2].Rank == sorted[3].Rank {
		// first kicker is [4]
		// second kicker is [1]
		return sorted[0]
	}
	if sorted[3].Rank == sorted[4].Rank {
		// first kicker is [2]
		// second kicker is [1]
		return sorted[0]
	}
	panic("nope")
}

func (c Cards) twoPairBiggestPair() Rank {
	if !c.containsTwoPair() {
		panic("hand must have two pair to have a biggest pair")
	}
	sorted := c.SortByRankAscending()
	if sorted[0].Rank == sorted[1].Rank && sorted[2].Rank == sorted[3].Rank {
		return sorted[2].Rank
	}

	if sorted[0].Rank == sorted[1].Rank && sorted[3].Rank == sorted[4].Rank {
		return sorted[0].Rank
	}
	if sorted[1].Rank == sorted[2].Rank && sorted[3].Rank == sorted[4].Rank {
		return sorted[1].Rank
	}
	panic("nope")
}

func (c Cards) twoPairSmallestPair() Rank {
	if !c.containsTwoPair() {
		panic("hand must have two pair to have a smallest pair")
	}
	sorted := c.SortByRankAscending()
	if sorted[0].Rank == sorted[1].Rank && sorted[2].Rank == sorted[3].Rank {
		return sorted[0].Rank
	}
	if sorted[0].Rank == sorted[1].Rank && sorted[3].Rank == sorted[4].Rank {
		return sorted[0].Rank
	}
	if sorted[1].Rank == sorted[2].Rank && sorted[3].Rank == sorted[4].Rank {
		return sorted[1].Rank
	}
	panic("nope")
}

func (c Cards) twoPairKicker() Card {
	if !c.containsTwoPair() {
		panic("hand must have two pair to have a twoPairKicker")
	}
	sorted := c.SortByRankAscending()
	if sorted[0].Rank == sorted[1].Rank && sorted[2].Rank == sorted[3].Rank {
		return sorted[4]
	}
	if sorted[0].Rank == sorted[1].Rank && sorted[3].Rank == sorted[4].Rank {
		return sorted[2]
	}
	if sorted[1].Rank == sorted[2].Rank && sorted[3].Rank == sorted[4].Rank {
		return sorted[0]
	}
	panic("nope")
}

func (c Cards) threeOfAKindTripsRank() Rank {
	if !c.containsThreeOfAKind() {
		panic("hand must have three of a kind to have a trips rank")
	}
	sorted := c.SortByRankAscending()
	if sorted[0].Rank == sorted[1].Rank && sorted[1].Rank == sorted[2].Rank {
		return sorted[0].Rank
	}
	if sorted[1].Rank == sorted[2].Rank && sorted[2].Rank == sorted[3].Rank {
		return sorted[1].Rank
	}
	if sorted[2].Rank == sorted[3].Rank && sorted[3].Rank == sorted[4].Rank {
		return sorted[2].Rank
	}
	panic("nope")
}

func (c Cards) threeOfAKindKickers() Cards {
	if !c.containsThreeOfAKind() {
		panic("hand must have three of a kind to have kickers")
	}
	sorted := c.SortByRankAscending()
	if sorted[0].Rank == sorted[1].Rank && sorted[1].Rank == sorted[2].Rank {
		return Cards{sorted[3], sorted[4]}
	}
	if sorted[1].Rank == sorted[2].Rank && sorted[2].Rank == sorted[3].Rank {
		return Cards{sorted[0], sorted[4]}
	}
	if sorted[2].Rank == sorted[3].Rank && sorted[3].Rank == sorted[4].Rank {
		return Cards{sorted[0], sorted[1]}
	}
	panic("nope")
}

func (c Cards) threeOfAKindFirstKicker() Card {
	x := c.threeOfAKindKickers()
	return x[0]
}

func (c Cards) threeOfAKindSecondKicker() Card {
	x := c.threeOfAKindKickers()
	return x[1]
}

func (c Cards) fourOfAKindRank() Rank {
	if !c.containsFourOfAKind() {
		panic("hand must have four of a kind to have a four of a kind rank")
	}
	sorted := c.SortByRankAscending()
	if sorted[0].Rank == sorted[1].Rank && sorted[1].Rank == sorted[2].Rank && sorted[2].Rank == sorted[3].Rank {
		return sorted[0].Rank
	}
	if sorted[1].Rank == sorted[2].Rank && sorted[2].Rank == sorted[3].Rank && sorted[3].Rank == sorted[4].Rank {
		return sorted[1].Rank
	}
	panic("nope")
}

func (c Cards) fourOfAKindKicker() Card {
	if !c.containsFourOfAKind() {
		panic("hand must have four of a kind to have a four of a kind kicker")
	}
	sorted := c.SortByRankAscending()
	if sorted[0].Rank == sorted[1].Rank && sorted[1].Rank == sorted[2].Rank && sorted[2].Rank == sorted[3].Rank {
		return sorted[4]
	}
	if sorted[1].Rank == sorted[2].Rank && sorted[2].Rank == sorted[3].Rank && sorted[3].Rank == sorted[4].Rank {
		return sorted[0]
	}
	panic("nope")
}

func (c Cards) fullHouseTripsRank() Rank {
	if !c.containsFullHouse() {
		panic("hand must have a full house to have a trips rank")
	}
	sorted := c.SortByRankAscending()
	if sorted[0].Rank == sorted[1].Rank && sorted[1].Rank == sorted[2].Rank && sorted[3].Rank == sorted[4].Rank {
		return sorted[0].Rank
	}
	if sorted[0].Rank == sorted[1].Rank && sorted[2].Rank == sorted[3].Rank && sorted[3].Rank == sorted[4].Rank {
		return sorted[4].Rank
	}
	panic("nope")
}

func (c Cards) fullHousePairRank() Rank {
	if !c.containsFullHouse() {
		panic("hand must have a full house to have a pair rank")
	}
	sorted := c.SortByRankAscending()
	if sorted[0].Rank == sorted[1].Rank && sorted[1].Rank == sorted[2].Rank && sorted[3].Rank == sorted[4].Rank {
		return sorted[4].Rank
	}
	if sorted[0].Rank == sorted[1].Rank && sorted[2].Rank == sorted[3].Rank && sorted[3].Rank == sorted[4].Rank {
		return sorted[0].Rank
	}
	panic("nope")
}

func (hand Cards) containsDuplicates() bool {
	seen := make(map[Card]bool)
	for _, card := range hand {
		if _, ok := seen[card]; ok {
			return true
		}
		seen[card] = true
	}
	return false
}

func (hand Cards) IsFiveCardPokerHand() bool {
	return len(hand) == 5 && !hand.containsDuplicates()
}

func (hand Cards) containsFlush() bool {
	if !hand.IsFiveCardPokerHand() {
		panic("hand must have 5 cards to be scored")
	}
	suit := hand[0].Suit
	for i := 1; i < len(hand); i++ {
		if hand[i].Suit != suit {
			return false
		}
	}
	return true
}

func (hand Cards) containsStraight() bool {
	if !hand.IsFiveCardPokerHand() {
		panic("hand must have 5 cards to be scored")
	}
	sorted := hand.SortByRankAscending()

	if sorted[4].Rank == ACE && sorted[3].Rank == FIVE {
		// special case for A-5 straight
		if sorted[0].Rank == DEUCE && sorted[1].Rank == THREE && sorted[2].Rank == FOUR {
			return true
		} else {
			return false
		}
	}
	return sorted[0].Rank.Int()+1 == sorted[1].Rank.Int() &&
		sorted[1].Rank.Int()+1 == sorted[2].Rank.Int() &&
		sorted[2].Rank.Int()+1 == sorted[3].Rank.Int() &&
		sorted[3].Rank.Int()+1 == sorted[4].Rank.Int()
}

func (hand Cards) containsStraightFlush() bool {
	if !hand.IsFiveCardPokerHand() {
		panic("hand must have 5 cards to be scored")
	}
	if hand.containsStraight() && hand.containsFlush() {
		return true
	}
	return false
}

func (hand Cards) containsRoyalFlush() bool {
	if !hand.IsFiveCardPokerHand() {
		panic("hand must have 5 cards to be scored")
	}
	// This seems like it works, but a five-high straight flush is not a royal flush
	// and the highest ranked card in five-high straigh flush is an ace.
	//if hand.containsStraightFlush() && hand.HighestRank() == ACE {
	//		return true
	//}
	sorted := hand.SortByRankAscending()
	if hand.containsStraightFlush() && hand.HighestRank() == ACE && sorted[0].Rank == TEN {
		return true
	}
	return false
}

func (hand Cards) containsFourOfAKind() bool {
	if !hand.IsFiveCardPokerHand() {
		panic("hand must have 5 cards to be scored")
	}
	sorted := hand.SortByRankAscending()
	if sorted[0].Rank == sorted[1].Rank && sorted[1].Rank == sorted[2].Rank && sorted[2].Rank == sorted[3].Rank {
		// the quads precede the kicker
		return true
	}
	if sorted[1].Rank == sorted[2].Rank && sorted[2].Rank == sorted[3].Rank && sorted[3].Rank == sorted[4].Rank {
		// the kicker is the first card
		return true
	}
	return false
}

func (hand Cards) containsFullHouse() bool {
	if !hand.IsFiveCardPokerHand() {
		panic("hand must have 5 cards to be scored")
	}
	sorted := hand.SortByRankAscending()

	if sorted[0].Rank == sorted[1].Rank && sorted[1].Rank == sorted[2].Rank && sorted[3].Rank == sorted[4].Rank {
		// the trips precede the pair
		return true
	}
	if sorted[0].Rank == sorted[1].Rank && sorted[2].Rank == sorted[3].Rank && sorted[3].Rank == sorted[4].Rank {
		// the pair is first
		return true
	}
	return false
}

func (hand Cards) containsPair() bool {
	if !hand.IsFiveCardPokerHand() {
		panic("hand must have 5 cards to be scored")
	}
	sorted := hand.SortByRankAscending()
	if sorted[0].Rank == sorted[1].Rank {
		return true
	}
	if sorted[1].Rank == sorted[2].Rank {
		return true
	}
	if sorted[2].Rank == sorted[3].Rank {
		return true
	}
	if sorted[3].Rank == sorted[4].Rank {
		return true
	}
	return false
}

func (hand Cards) containsThreeOfAKind() bool {
	if !hand.IsFiveCardPokerHand() {
		panic("hand must have 5 cards to be scored")
	}
	sorted := hand.SortByRankAscending()
	if sorted[0].Rank == sorted[1].Rank && sorted[1].Rank == sorted[2].Rank {
		return true
	}
	if sorted[1].Rank == sorted[2].Rank && sorted[2].Rank == sorted[3].Rank {
		return true
	}
	if sorted[2].Rank == sorted[3].Rank && sorted[3].Rank == sorted[4].Rank {
		return true
	}
	return false
}

func (hand Cards) containsTwoPair() bool {
	if !hand.IsFiveCardPokerHand() {
		panic("hand must have 5 cards to be scored")
	}
	sorted := hand.SortByRankAscending()
	if sorted[0].Rank == sorted[1].Rank && sorted[2].Rank == sorted[3].Rank {
		return true
	}
	if sorted[0].Rank == sorted[1].Rank && sorted[3].Rank == sorted[4].Rank {
		return true
	}
	if sorted[1].Rank == sorted[2].Rank && sorted[3].Rank == sorted[4].Rank {
		return true
	}
	return false
}

func (hand Cards) isUnmadeHand() bool {
	if !hand.IsFiveCardPokerHand() {
		panic("hand must have 5 cards to be scored")
	}
	return !hand.containsPair() && !hand.containsTwoPair() && !hand.containsThreeOfAKind() && !hand.containsStraight() && !hand.containsFlush() && !hand.containsFullHouse() && !hand.containsFourOfAKind() && !hand.containsStraightFlush() && !hand.containsRoyalFlush()
}
