// // StatsView.swift // Gas Man // // Created by Kameron Kenny on 3/19/25. // import SwiftUI import CoreData struct StatsView: View { @Environment(\.managedObjectContext) private var viewContext // Fetch all fuel logs sorted by date (descending) @FetchRequest( sortDescriptors: [NSSortDescriptor(keyPath: \FuelLog.date, ascending: false)], animation: .default) private var fuelLogs: FetchedResults // Fetch all vehicles sorted by make (or any preferred order) @FetchRequest( sortDescriptors: [NSSortDescriptor(keyPath: \Vehicle.make, ascending: true)], animation: .default) private var vehicles: FetchedResults @State private var selectedVehicleID: UUID? = nil // Filter fuel logs by the selected vehicle. private var filteredFuelLogs: [FuelLog] { if let vehicleID = selectedVehicleID { return fuelLogs.filter { log in log.vehicle?.id == vehicleID } } else { return Array(fuelLogs) } } // Total number of fill-ups. private var fillUpCount: Int { filteredFuelLogs.count } // Average MPG computed only for full-tank logs. private var averageMPG: Double? { let logs = filteredFuelLogs guard logs.count > 1 else { return nil } var mpgValues: [Double] = [] for i in 0.. 0, current.odometer > next.odometer { let mpg = (current.odometer - next.odometer) / current.fuelVolume mpgValues.append(mpg) } } guard !mpgValues.isEmpty else { return nil } let sum = mpgValues.reduce(0, +) return sum / Double(mpgValues.count) } // Price per gallon stats: lowest, highest, average. private var pricePerGallonStats: (min: Double?, max: Double?, avg: Double?) { let prices = filteredFuelLogs.map { $0.pricePerGalon } guard !prices.isEmpty else { return (nil, nil, nil) } let minPrice = prices.min() let maxPrice = prices.max() let avgPrice = prices.reduce(0, +) / Double(prices.count) return (minPrice, maxPrice, avgPrice) } // Distance between fill-ups stats: lowest, highest, average. private var distanceStats: (min: Double?, max: Double?, avg: Double?) { let logs = filteredFuelLogs guard logs.count > 1 else { return (nil, nil, nil) } var distances: [Double] = [] for i in 0.. 0 { distances.append(dist) } } guard !distances.isEmpty else { return (nil, nil, nil) } let minDistance = distances.min()! let maxDistance = distances.max()! let avgDistance = distances.reduce(0, +) / Double(distances.count) return (minDistance, maxDistance, avgDistance) } // Gallons per fill-up stats. private var gallonsStats: (min: Double?, max: Double?, avg: Double?) { let gallons = filteredFuelLogs.map { $0.fuelVolume } guard !gallons.isEmpty else { return (nil, nil, nil) } let minGallons = gallons.min() let maxGallons = gallons.max() let avgGallons = gallons.reduce(0, +) / Double(gallons.count) return (minGallons, maxGallons, avgGallons) } var body: some View { NavigationView { Form { // Vehicle Selector Section(header: Text("Select Vehicle")) { Picker("Vehicle", selection: $selectedVehicleID) { ForEach(vehicles, id: \.id) { vehicle in Text("\(vehicle.year ?? "") \(vehicle.make ?? "") \(vehicle.model ?? "")") .tag(vehicle.id) } } .pickerStyle(MenuPickerStyle()) } // Stats for Fuel Logs Section(header: Text("Fill-Up Count")) { Text("\(fillUpCount)") } Section(header: Text("Average MPG")) { if let avgMPG = averageMPG { Text("\(avgMPG, specifier: "%.1f") MPG") } else { Text("N/A") .foregroundColor(.secondary) } } Section(header: Text("Price per Gallon")) { let stats = pricePerGallonStats HStack { Text("Lowest:") Spacer() if let minPrice = stats.min { Text("$\(minPrice, specifier: "%.3f")") } else { Text("N/A") } } HStack { Text("Highest:") Spacer() if let maxPrice = stats.max { Text("$\(maxPrice, specifier: "%.3f")") } else { Text("N/A") } } HStack { Text("Average:") Spacer() if let avgPrice = stats.avg { Text("$\(avgPrice, specifier: "%.3f")") } else { Text("N/A") } } } // Distance Between Fill-Ups Stats Section(header: Text("Distance Between Fill-Ups")) { let dStats = distanceStats HStack { Text("Lowest:") Spacer() if let minDistance = dStats.min { Text("\(minDistance, specifier: "%.0f") miles") } else { Text("N/A") } } HStack { Text("Highest:") Spacer() if let maxDistance = dStats.max { Text("\(maxDistance, specifier: "%.0f") miles") } else { Text("N/A") } } HStack { Text("Average:") Spacer() if let avgDistance = dStats.avg { Text("\(avgDistance, specifier: "%.0f") miles") } else { Text("N/A") } } } Section(header: Text("Gallons per Fill-Up")) { let gStats = gallonsStats HStack { Text("Lowest:") Spacer() if let minGallons = gStats.min { Text("\(minGallons, specifier: "%.3f") gallons") } else { Text("N/A") } } HStack { Text("Highest:") Spacer() if let maxGallons = gStats.max { Text("\(maxGallons, specifier: "%.3f") gallons") } else { Text("N/A") } } HStack { Text("Average:") Spacer() if let avgGallons = gStats.avg { Text("\(avgGallons, specifier: "%.3f") gallons") } else { Text("N/A") } } } } .navigationTitle("Stats") } .onAppear { if selectedVehicleID == nil { selectedVehicleID = vehicles.first?.id } } } }