// // GameTableView.swift // Blackjack // // Main game container view. // import SwiftUI import CasinoKit struct GameTableView: View { @State private var settings = GameSettings() @State private var gameState: GameState? @State private var selectedChip: ChipDenomination = .twentyFive // MARK: - Sheet State @State private var showSettings = false @State private var showRules = false @State private var showStats = false // MARK: - Environment @Environment(\.horizontalSizeClass) private var horizontalSizeClass @Environment(\.verticalSizeClass) private var verticalSizeClass /// Whether we're on iPad private var isIPad: Bool { horizontalSizeClass == .regular } /// Maximum content width based on device private var maxContentWidth: CGFloat { if isIPad { return verticalSizeClass == .compact ? Design.Size.maxContentWidthLandscape : Design.Size.maxContentWidthPortrait } return .infinity } // MARK: - Body var body: some View { Group { if let state = gameState { mainGameView(state: state) } else { ProgressView() .task { gameState = GameState(settings: settings) } } } .sheet(isPresented: $showSettings) { SettingsView(settings: settings, gameState: gameState) } .sheet(isPresented: $showRules) { RulesHelpView() } .sheet(isPresented: $showStats) { if let state = gameState { StatisticsSheetView(state: state) } } } // Use global debug flag from Design constants private var showDebugBorders: Bool { Design.showDebugBorders } // MARK: - Main Game View @ViewBuilder private func mainGameView(state: GameState) -> some View { ZStack { // Background TableBackgroundView() VStack(spacing: 0) { // Top bar TopBarView( balance: state.balance, secondaryInfo: settings.showCardsRemaining ? "\(state.engine.cardsRemaining)" : nil, secondaryIcon: settings.showCardsRemaining ? "rectangle.portrait.on.rectangle.portrait.fill" : nil, onReset: { state.resetGame() }, onSettings: { showSettings = true }, onHelp: { showRules = true }, onStats: { showStats = true } ) .frame(maxWidth: maxContentWidth) .debugBorder(showDebugBorders, color: .cyan, label: "TopBar") // Card count display (when enabled) if settings.showCardCount { CardCountView( runningCount: state.engine.runningCount, trueCount: state.engine.trueCount ) .frame(maxWidth: maxContentWidth) .debugBorder(showDebugBorders, color: .mint, label: "CardCount") } // Reshuffle notification if state.showReshuffleNotification { ReshuffleNotificationView(showCardCount: settings.showCardCount) .frame(maxWidth: maxContentWidth) .transition(.move(edge: .top).combined(with: .opacity)) } // Table layout - fills available space BlackjackTableView( state: state, onPlaceBet: { placeBet(state: state) } ) .frame(maxWidth: maxContentWidth) // Chip selector - only shown during betting phase if state.currentPhase == .betting { Spacer() .debugBorder(showDebugBorders, color: .yellow, label: "ChipSpacer") ChipSelectorView( selectedChip: $selectedChip, balance: state.balance, currentBet: state.currentBet, maxBet: state.settings.maxBet ) .frame(maxWidth: maxContentWidth) .transition(.opacity.combined(with: .move(edge: .bottom))) .debugBorder(showDebugBorders, color: .pink, label: "ChipSelector") } // Action buttons - minimal spacing during player turn ActionButtonsView(state: state) .frame(maxWidth: maxContentWidth) .padding(.bottom, Design.Spacing.small) .debugBorder(showDebugBorders, color: .blue, label: "ActionBtns") } .frame(maxWidth: .infinity) // Insurance popup overlay (covers entire screen) if state.currentPhase == .insurance { InsurancePopupView( betAmount: state.currentBet / 2, balance: state.balance, onTake: { Task { await state.takeInsurance() } }, onDecline: { state.declineInsurance() } ) .transition(.opacity.combined(with: .scale(scale: 0.9))) } // Result banner overlay if state.showResultBanner, let result = state.lastRoundResult { ResultBannerView( result: result, currentBalance: state.balance, minBet: state.settings.minBet, onNewRound: { state.newRound() }, onPlayAgain: { state.resetGame() } ) } // Confetti for wins (matching Baccarat pattern) if state.showResultBanner && (state.lastRoundResult?.totalWinnings ?? 0) > 0 { ConfettiView() } // Game over if state.isGameOver && !state.showResultBanner { GameOverView( roundsPlayed: state.roundsPlayed, onPlayAgain: { state.resetGame() } ) } } } // MARK: - Betting private func placeBet(state: GameState) { state.placeBet(amount: selectedChip.rawValue) } } // MARK: - Preview #Preview { GameTableView() }