// // SettingsView.swift // Baccarat // // Settings screen for game customization. // import SwiftUI /// 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(red: 0.08, green: 0.12, blue: 0.08) .ignoresSafeArea() ScrollView { VStack(spacing: 24) { // 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(0.1)) 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(0.1)) SpeedPicker(speed: $settings.dealingSpeed) } } // Reset Button Button { settings.resetToDefaults() hasChanges = true } label: { HStack { Image(systemName: "arrow.counterclockwise") Text("Reset to Defaults") } .font(.system(size: 14, weight: .medium)) .foregroundStyle(.red.opacity(0.8)) .padding() .frame(maxWidth: .infinity) .background( RoundedRectangle(cornerRadius: 12) .fill(Color.red.opacity(0.1)) ) } .padding(.horizontal) .padding(.top, 8) } .padding(.vertical) } } .navigationTitle("Settings") .navigationBarTitleDisplayMode(.inline) .toolbarBackground(Color(red: 0.08, green: 0.12, blue: 0.08), for: .navigationBar) .toolbarBackground(.visible, for: .navigationBar) .toolbarColorScheme(.dark, for: .navigationBar) .toolbar { ToolbarItem(placement: .topBarLeading) { Button("Cancel") { settings.load() // Revert changes dismiss() } .foregroundStyle(.white.opacity(0.7)) } 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: 12) { // Header HStack(spacing: 8) { Image(systemName: icon) .font(.system(size: 12, weight: .semibold)) .foregroundStyle(.yellow.opacity(0.8)) Text(title) .font(.system(size: 12, weight: .bold, design: .rounded)) .tracking(1) .foregroundStyle(.white.opacity(0.6)) } .padding(.horizontal, 4) // Content card VStack(spacing: 0) { content } .padding() .background( RoundedRectangle(cornerRadius: 12) .fill(Color.white.opacity(0.05)) ) } .padding(.horizontal) } } /// Deck count picker with visual options. struct DeckCountPicker: View { @Binding var selection: DeckCount var body: some View { VStack(spacing: 12) { ForEach(DeckCount.allCases) { count in Button { selection = count } label: { HStack { VStack(alignment: .leading, spacing: 2) { Text(count.displayName) .font(.system(size: 16, weight: .semibold)) .foregroundStyle(.white) Text(count.description) .font(.system(size: 12)) .foregroundStyle(.white.opacity(0.5)) } Spacer() if selection == count { Image(systemName: "checkmark.circle.fill") .font(.system(size: 22)) .foregroundStyle(.yellow) } else { Circle() .strokeBorder(Color.white.opacity(0.3), lineWidth: 2) .frame(width: 22, height: 22) } } .padding() .background( RoundedRectangle(cornerRadius: 10) .fill(selection == count ? Color.yellow.opacity(0.1) : Color.clear) ) .overlay( RoundedRectangle(cornerRadius: 10) .strokeBorder( selection == count ? Color.yellow.opacity(0.5) : Color.white.opacity(0.1), lineWidth: 1 ) ) } .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: 10) { ForEach(options, id: \.self) { amount in Button { balance = amount } label: { Text("$\(amount / 1000)K") .font(.system(size: 14, weight: .bold)) .foregroundStyle(balance == amount ? .black : .white) .padding(.vertical, 12) .frame(maxWidth: .infinity) .background( RoundedRectangle(cornerRadius: 8) .fill(balance == amount ? Color.yellow : Color.white.opacity(0.1)) ) } .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: 2) { Text(title) .font(.system(size: 15, weight: .medium)) .foregroundStyle(.white) Text(subtitle) .font(.system(size: 12)) .foregroundStyle(.white.opacity(0.5)) } } .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: 8) { Text("Dealing Speed") .font(.system(size: 15, weight: .medium)) .foregroundStyle(.white) HStack(spacing: 8) { ForEach(options, id: \.1) { option in Button { speed = option.1 } label: { Text(option.0) .font(.system(size: 13, weight: .medium)) .foregroundStyle(speed == option.1 ? .black : .white.opacity(0.7)) .padding(.vertical, 8) .frame(maxWidth: .infinity) .background( Capsule() .fill(speed == option.1 ? Color.yellow : Color.white.opacity(0.1)) ) } .buttonStyle(.plain) } } } } } /// Table limits picker for min/max bets. struct TableLimitsPicker: View { @Binding var selection: TableLimits var body: some View { VStack(spacing: 10) { ForEach(TableLimits.allCases) { limit in Button { selection = limit } label: { HStack { VStack(alignment: .leading, spacing: 2) { Text(limit.displayName) .font(.system(size: 16, weight: .semibold)) .foregroundStyle(.white) Text(limit.detailedDescription) .font(.system(size: 12)) .foregroundStyle(.white.opacity(0.5)) } Spacer() // Limits badge Text(limit.description) .font(.system(size: 12, weight: .bold, design: .rounded)) .foregroundStyle(selection == limit ? .black : .yellow) .padding(.horizontal, 10) .padding(.vertical, 4) .background( Capsule() .fill(selection == limit ? Color.yellow : Color.yellow.opacity(0.2)) ) if selection == limit { Image(systemName: "checkmark.circle.fill") .font(.system(size: 20)) .foregroundStyle(.yellow) } else { Circle() .strokeBorder(Color.white.opacity(0.3), lineWidth: 2) .frame(width: 20, height: 20) } } .padding() .background( RoundedRectangle(cornerRadius: 10) .fill(selection == limit ? Color.yellow.opacity(0.1) : Color.clear) ) .overlay( RoundedRectangle(cornerRadius: 10) .strokeBorder( selection == limit ? Color.yellow.opacity(0.5) : Color.white.opacity(0.1), lineWidth: 1 ) ) } .buttonStyle(.plain) } } } } #Preview { SettingsView(settings: GameSettings()) { } }