-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathParkingLotService.java
More file actions
178 lines (143 loc) · 7.24 KB
/
Copy pathParkingLotService.java
File metadata and controls
178 lines (143 loc) · 7.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
package com.parking.service;
import com.parking.model.*;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
/**
* Core Smart Parking Lot Service.
* Manages spot allocation, entry/exit, and analytics.
*/
public class ParkingLotService {
private final Map<String, ParkingSpot> spots = new LinkedHashMap<>();
private final Map<String, ParkingTicket> activeTickets = new HashMap<>();
private final List<ParkingTicket> ticketHistory = new ArrayList<>();
private final PricingService pricingService = new PricingService();
private double totalRevenue = 0;
public ParkingLotService() {
initializeLot();
}
// ─── Lot Initialization ──────────────────────────────────────────────────
private void initializeLot() {
String[] zones = {"A", "B", "C"};
for (String zone : zones) {
for (int level = 1; level <= 2; level++) {
for (int spot = 1; spot <= 10; spot++) {
ParkingSpot.SpotType type = determineSpotType(zone, spot);
ParkingSpot ps = new ParkingSpot(zone, level, spot, type);
spots.put(ps.getId(), ps);
}
}
}
System.out.println("✅ Parking lot initialized with " + spots.size() + " spots.");
}
private ParkingSpot.SpotType determineSpotType(String zone, int spotNumber) {
if (spotNumber == 1) return ParkingSpot.SpotType.HANDICAPPED;
if (spotNumber == 2) return ParkingSpot.SpotType.EV_CHARGING;
if (zone.equals("A") && spotNumber <= 4) return ParkingSpot.SpotType.PREMIUM;
if (spotNumber >= 9) return ParkingSpot.SpotType.COMPACT;
return ParkingSpot.SpotType.REGULAR;
}
// ─── Entry Management ────────────────────────────────────────────────────
public ParkingTicket processEntry(String licensePlate, String ownerName, Vehicle.VehicleType vehicleType) {
if (activeTickets.containsKey(licensePlate)) {
throw new IllegalStateException("Vehicle " + licensePlate + " is already parked.");
}
ParkingSpot spot = findBestSpot(vehicleType);
if (spot == null) {
throw new RuntimeException("No available spots for vehicle type: " + vehicleType);
}
// Occupy the spot
spot.setStatus(ParkingSpot.SpotStatus.OCCUPIED);
spot.setVehicleId(licensePlate);
spot.setOccupiedSince(System.currentTimeMillis());
// Create ticket
ParkingTicket ticket = new ParkingTicket(licensePlate, spot.getId());
activeTickets.put(licensePlate, ticket);
System.out.printf("🚗 Entry: %s → Spot %s (%s)%n", licensePlate, spot.getId(), spot.getType());
return ticket;
}
// ─── Smart Spot Assignment ───────────────────────────────────────────────
private ParkingSpot findBestSpot(Vehicle.VehicleType vehicleType) {
ParkingSpot.SpotType preferred = mapVehicleToSpotType(vehicleType);
// First try exact match
Optional<ParkingSpot> best = spots.values().stream()
.filter(s -> s.isAvailable() && s.getType() == preferred)
.findFirst();
// Fallback to regular
if (best.isEmpty()) {
best = spots.values().stream()
.filter(s -> s.isAvailable() && s.getType() == ParkingSpot.SpotType.REGULAR)
.findFirst();
}
return best.orElse(null);
}
private ParkingSpot.SpotType mapVehicleToSpotType(Vehicle.VehicleType vt) {
switch (vt) {
case EV: return ParkingSpot.SpotType.EV_CHARGING;
case HANDICAPPED: return ParkingSpot.SpotType.HANDICAPPED;
case MOTORCYCLE: return ParkingSpot.SpotType.COMPACT;
case TRUCK: return ParkingSpot.SpotType.PREMIUM;
default: return ParkingSpot.SpotType.REGULAR;
}
}
// ─── Exit Management ─────────────────────────────────────────────────────
public ParkingTicket processExit(String licensePlate) {
ParkingTicket ticket = activeTickets.get(licensePlate);
if (ticket == null) {
throw new IllegalStateException("No active ticket for: " + licensePlate);
}
ParkingSpot spot = spots.get(ticket.getSpotId());
long durationMinutes = ticket.getDurationMinutes();
double charge = pricingService.calculateCharge(spot.getType(), durationMinutes);
ticket.setExitedAt(LocalDateTime.now());
ticket.setAmount(charge);
ticket.setPaid(true);
ticket.setStatus(ParkingTicket.TicketStatus.COMPLETED);
// Free the spot
spot.setStatus(ParkingSpot.SpotStatus.AVAILABLE);
spot.setVehicleId(null);
activeTickets.remove(licensePlate);
ticketHistory.add(ticket);
totalRevenue += charge;
System.out.printf("🏁 Exit: %s | Duration: %d min | Charge: ₹%.2f%n",
licensePlate, durationMinutes, charge);
return ticket;
}
// ─── Queries ─────────────────────────────────────────────────────────────
public List<ParkingSpot> getAllSpots() {
return new ArrayList<>(spots.values());
}
public List<ParkingSpot> getAvailableSpots() {
return spots.values().stream()
.filter(ParkingSpot::isAvailable)
.collect(Collectors.toList());
}
public List<ParkingSpot> getOccupiedSpots() {
return spots.values().stream()
.filter(s -> s.getStatus() == ParkingSpot.SpotStatus.OCCUPIED)
.collect(Collectors.toList());
}
public Map<String, Object> getAnalytics() {
int total = spots.size();
long occupied = spots.values().stream().filter(s -> s.getStatus() == ParkingSpot.SpotStatus.OCCUPIED).count();
long available = total - occupied;
double occupancyRate = (double) occupied / total * 100;
Map<String, Object> analytics = new LinkedHashMap<>();
analytics.put("totalSpots", total);
analytics.put("occupied", occupied);
analytics.put("available", available);
analytics.put("occupancyRate", String.format("%.1f%%", occupancyRate));
analytics.put("totalRevenue", String.format("₹%.2f", totalRevenue));
analytics.put("totalTransactions", ticketHistory.size());
analytics.put("activeVehicles", activeTickets.size());
return analytics;
}
public List<ParkingTicket> getTicketHistory() {
return Collections.unmodifiableList(ticketHistory);
}
public ParkingTicket getActiveTicket(String licensePlate) {
return activeTickets.get(licensePlate);
}
public double getTotalRevenue() { return totalRevenue; }
public int getTotalSpots() { return spots.size(); }
}