// // SettingsView.swift // Baccarat // // Settings screen for game customization. // import SwiftUI import CasinoKit /// The settings screen for customizing game options. struct SettingsView: View { @Bindable var settings: GameSettings @Environment(\.dismiss) private var dismiss let onApplyChanges: () -> Void @State private var hasChanges = false var body: some View { NavigationStack { ZStack { // Background Color.Settings.background .ignoresSafeArea() ScrollView { VStack(spacing: Design.Spacing.xxLarge) { // Table Limits Section (First!) SettingsSection(title: "TABLE LIMITS", icon: "banknote") { TableLimitsPicker(selection: $settings.tableLimits) .onChange(of: settings.tableLimits) { _, _ in hasChanges = true } } // Deck Settings Section SettingsSection(title: "DECK SETTINGS", icon: "rectangle.portrait.on.rectangle.portrait") { DeckCountPicker(selection: $settings.deckCount) .onChange(of: settings.deckCount) { _, _ in hasChanges = true } } // Starting Balance Section SettingsSection(title: "STARTING BALANCE", icon: "dollarsign.circle") { BalancePicker(balance: $settings.startingBalance) .onChange(of: settings.startingBalance) { _, _ in hasChanges = true } } // Display Settings Section SettingsSection(title: "DISPLAY", icon: "eye") { SettingsToggle( title: "Show Cards Remaining", subtitle: "Display deck counter at top", isOn: $settings.showCardsRemaining ) Divider() .background(Color.white.opacity(Design.Opacity.subtle)) SettingsToggle( title: "Show History", subtitle: "Display result road map", isOn: $settings.showHistory ) } // Animation Settings Section SettingsSection(title: "ANIMATIONS", icon: "sparkles") { SettingsToggle( title: "Card Animations", subtitle: "Animate dealing and flipping", isOn: $settings.showAnimations ) if settings.showAnimations { Divider() .background(Color.white.opacity(Design.Opacity.subtle)) SpeedPicker(speed: $settings.dealingSpeed) } } // Reset Button Button { settings.resetToDefaults() hasChanges = true } label: { HStack { Image(systemName: "arrow.counterclockwise") Text("Reset to Defaults") } .font(.system(size: Design.BaseFontSize.medium, weight: .medium)) .foregroundStyle(.red.opacity(Design.Opacity.heavy)) .padding() .frame(maxWidth: .infinity) .background( RoundedRectangle(cornerRadius: Design.CornerRadius.large) .fill(Color.red.opacity(Design.Opacity.subtle)) ) } .padding(.horizontal) .padding(.top, Design.Spacing.small) } .padding(.vertical) } } .navigationTitle("Settings") .navigationBarTitleDisplayMode(.inline) .toolbarBackground(Color.Settings.background, for: .navigationBar) .toolbarBackground(.visible, for: .navigationBar) .toolbarColorScheme(.dark, for: .navigationBar) .toolbar { ToolbarItem(placement: .topBarLeading) { Button("Cancel") { settings.load() // Revert changes dismiss() } .foregroundStyle(.white.opacity(Design.Opacity.strong)) } ToolbarItem(placement: .topBarTrailing) { Button("Done") { settings.save() if hasChanges { onApplyChanges() } dismiss() } .bold() .foregroundStyle(.yellow) } } } } } /// A settings section with a title and content. struct SettingsSection: View { let title: String let icon: String @ViewBuilder let content: Content var body: some View { VStack(alignment: .leading, spacing: Design.Spacing.medium) { // Header HStack(spacing: Design.Spacing.small) { Image(systemName: icon) .font(.system(size: Design.BaseFontSize.body, weight: .semibold)) .foregroundStyle(.yellow.opacity(Design.Opacity.heavy)) Text(title) .font(.system(size: Design.BaseFontSize.body, weight: .bold, design: .rounded)) .tracking(1) .foregroundStyle(.white.opacity(Design.Opacity.accent)) } .padding(.horizontal, Design.Spacing.xSmall) // Content card VStack(spacing: 0) { content } .padding() .background( RoundedRectangle(cornerRadius: Design.CornerRadius.large) .fill(Color.white.opacity(Design.Opacity.verySubtle)) ) } .padding(.horizontal) } } /// Deck count picker with visual options. struct DeckCountPicker: View { @Binding var selection: DeckCount var body: some View { VStack(spacing: Design.Spacing.medium) { ForEach(DeckCount.allCases) { count in Button { selection = count } label: { HStack { VStack(alignment: .leading, spacing: Design.Spacing.xxSmall) { Text(count.displayName) .font(.system(size: Design.BaseFontSize.large, weight: .semibold)) .foregroundStyle(.white) Text(count.description) .font(.system(size: Design.BaseFontSize.body)) .foregroundStyle(.white.opacity(Design.Opacity.medium)) } Spacer() if selection == count { Image(systemName: "checkmark.circle.fill") .font(.system(size: Design.Size.checkmark)) .foregroundStyle(.yellow) } else { Circle() .strokeBorder(Color.white.opacity(Design.Opacity.light), lineWidth: Design.LineWidth.medium) .frame(width: Design.Size.checkmark, height: Design.Size.checkmark) } } .padding() .background( RoundedRectangle(cornerRadius: Design.CornerRadius.medium) .fill(selection == count ? Color.yellow.opacity(Design.Opacity.subtle) : Color.clear) ) .overlay( RoundedRectangle(cornerRadius: Design.CornerRadius.medium) .strokeBorder( selection == count ? Color.yellow.opacity(Design.Opacity.medium) : Color.white.opacity(Design.Opacity.subtle), lineWidth: Design.LineWidth.thin ) ) } .buttonStyle(.plain) } } } } /// Starting balance picker. struct BalancePicker: View { @Binding var balance: Int private let options = [1_000, 5_000, 10_000, 25_000, 50_000, 100_000] var body: some View { LazyVGrid(columns: [ GridItem(.flexible()), GridItem(.flexible()), GridItem(.flexible()) ], spacing: Design.Spacing.small) { ForEach(options, id: \.self) { amount in Button { balance = amount } label: { Text("$\(amount / 1000)K") .font(.system(size: Design.BaseFontSize.medium, weight: .bold)) .foregroundStyle(balance == amount ? .black : .white) .padding(.vertical, Design.Spacing.medium) .frame(maxWidth: .infinity) .background( RoundedRectangle(cornerRadius: Design.CornerRadius.small) .fill(balance == amount ? Color.yellow : Color.white.opacity(Design.Opacity.subtle)) ) } .buttonStyle(.plain) } } } } /// A toggle setting row. struct SettingsToggle: View { let title: String let subtitle: String @Binding var isOn: Bool var body: some View { Toggle(isOn: $isOn) { VStack(alignment: .leading, spacing: Design.Spacing.xxSmall) { Text(title) .font(.system(size: Design.BaseFontSize.subheadline, weight: .medium)) .foregroundStyle(.white) Text(subtitle) .font(.system(size: Design.BaseFontSize.body)) .foregroundStyle(.white.opacity(Design.Opacity.medium)) } } .tint(.yellow) } } /// Animation speed picker. struct SpeedPicker: View { @Binding var speed: Double private let options: [(String, Double)] = [ ("Fast", 0.5), ("Normal", 1.0), ("Slow", 2.0) ] var body: some View { VStack(alignment: .leading, spacing: Design.Spacing.small) { Text("Dealing Speed") .font(.system(size: Design.BaseFontSize.subheadline, weight: .medium)) .foregroundStyle(.white) HStack(spacing: Design.Spacing.small) { ForEach(options, id: \.1) { option in Button { speed = option.1 } label: { Text(option.0) .font(.system(size: Design.BaseFontSize.callout, weight: .medium)) .foregroundStyle(speed == option.1 ? .black : .white.opacity(Design.Opacity.strong)) .padding(.vertical, Design.Spacing.small) .frame(maxWidth: .infinity) .background( Capsule() .fill(speed == option.1 ? Color.yellow : Color.white.opacity(Design.Opacity.subtle)) ) } .buttonStyle(.plain) } } } } } /// Table limits picker for min/max bets. struct TableLimitsPicker: View { @Binding var selection: TableLimits var body: some View { VStack(spacing: Design.Spacing.small) { ForEach(TableLimits.allCases) { limit in Button { selection = limit } label: { HStack { VStack(alignment: .leading, spacing: Design.Spacing.xxSmall) { Text(limit.displayName) .font(.system(size: Design.BaseFontSize.large, weight: .semibold)) .foregroundStyle(.white) Text(limit.detailedDescription) .font(.system(size: Design.BaseFontSize.body)) .foregroundStyle(.white.opacity(Design.Opacity.medium)) } Spacer() // Limits badge Text(limit.description) .font(.system(size: Design.BaseFontSize.body, weight: .bold, design: .rounded)) .foregroundStyle(selection == limit ? .black : .yellow) .padding(.horizontal, Design.Spacing.small) .padding(.vertical, Design.Spacing.xSmall) .background( Capsule() .fill(selection == limit ? Color.yellow : Color.yellow.opacity(Design.Opacity.hint)) ) if selection == limit { Image(systemName: "checkmark.circle.fill") .font(.system(size: Design.Size.checkmark - 2)) .foregroundStyle(.yellow) } else { Circle() .strokeBorder(Color.white.opacity(Design.Opacity.light), lineWidth: Design.LineWidth.medium) .frame(width: Design.Size.checkmark - 2, height: Design.Size.checkmark - 2) } } .padding() .background( RoundedRectangle(cornerRadius: Design.CornerRadius.medium) .fill(selection == limit ? Color.yellow.opacity(Design.Opacity.subtle) : Color.clear) ) .overlay( RoundedRectangle(cornerRadius: Design.CornerRadius.medium) .strokeBorder( selection == limit ? Color.yellow.opacity(Design.Opacity.medium) : Color.white.opacity(Design.Opacity.subtle), lineWidth: Design.LineWidth.thin ) ) } .buttonStyle(.plain) } } } } #Preview { SettingsView(settings: GameSettings()) { } }