// // DealerHandView.swift // Blackjack // // Displays the dealer's hand with cards and value. // import SwiftUI import CasinoKit struct DealerHandView: View { let hand: BlackjackHand let showHoleCard: Bool let showCardCount: Bool let cardWidth: CGFloat let cardSpacing: CGFloat @ScaledMetric(relativeTo: .headline) private var labelFontSize: CGFloat = Design.BaseFontSize.medium var body: some View { VStack(spacing: Design.Spacing.small) { // Label and value HStack(spacing: Design.Spacing.small) { Text(String(localized: "DEALER")) .font(.system(size: labelFontSize, weight: .bold, design: .rounded)) .foregroundStyle(.white) // Show value: always show if hole card visible, or show single card value in European mode if !hand.cards.isEmpty { if showHoleCard { ValueBadge(value: hand.value, color: Color.Hand.dealer) } else if hand.cards.count == 1 { // European mode: show single visible card value ValueBadge(value: hand.cards[0].blackjackValue, color: Color.Hand.dealer) } } } // Cards HStack(spacing: hand.cards.isEmpty ? Design.Spacing.small : cardSpacing) { if hand.cards.isEmpty { CardPlaceholderView(width: cardWidth) CardPlaceholderView(width: cardWidth) } else { ForEach(hand.cards.indices, id: \.self) { index in let isFaceUp = index == 0 || showHoleCard CardView( card: hand.cards[index], isFaceUp: isFaceUp, cardWidth: cardWidth ) .overlay(alignment: .bottomLeading) { if showCardCount && isFaceUp { HiLoCountBadge(card: hand.cards[index]) } } .zIndex(Double(index)) } // Show placeholder for second card in European mode (no hole card) if hand.cards.count == 1 && !showHoleCard { CardPlaceholderView(width: cardWidth) .opacity(Design.Opacity.medium) } } } // Result badge if let result = hand.cards.count >= 2 && showHoleCard ? handResultText : nil { Text(result) .font(.system(size: labelFontSize, weight: .black)) .foregroundStyle(handResultColor) .padding(.horizontal, Design.Spacing.medium) .padding(.vertical, Design.Spacing.xSmall) .background( Capsule() .fill(handResultColor.opacity(Design.Opacity.hint)) ) } } .accessibilityElement(children: .ignore) .accessibilityLabel(dealerAccessibilityLabel) } // MARK: - Computed Properties private var handResultText: String? { if hand.isBlackjack { return String(localized: "BLACKJACK") } if hand.isBusted { return String(localized: "BUST") } return nil } private var handResultColor: Color { if hand.isBlackjack { return .yellow } if hand.isBusted { return .green } // Good for player return .white } private var dealerAccessibilityLabel: String { if hand.cards.isEmpty { return String(localized: "Dealer: No cards") } let visibleCards = showHoleCard ? hand.cards : [hand.cards[0]] let cardsDescription = visibleCards.map { $0.accessibilityDescription }.joined(separator: ", ") return String(localized: "Dealer: \(cardsDescription). Value: \(showHoleCard ? String(hand.value) : "hidden")") } } #Preview("Empty Hand") { ZStack { Color.Table.felt.ignoresSafeArea() DealerHandView( hand: BlackjackHand(), showHoleCard: false, showCardCount: false, cardWidth: 60, cardSpacing: -20 ) } } #Preview("Two Cards - Hole Hidden") { ZStack { Color.Table.felt.ignoresSafeArea() DealerHandView( hand: BlackjackHand(cards: [ Card(suit: .spades, rank: .ace), Card(suit: .hearts, rank: .king) ]), showHoleCard: false, showCardCount: false, cardWidth: 60, cardSpacing: -20 ) } } #Preview("Blackjack - Revealed") { ZStack { Color.Table.felt.ignoresSafeArea() DealerHandView( hand: BlackjackHand(cards: [ Card(suit: .spades, rank: .ace), Card(suit: .hearts, rank: .king) ]), showHoleCard: true, showCardCount: true, cardWidth: 60, cardSpacing: -20 ) } }