CCCPaste

Parking Lot LLD

enum VehicleType {
   CAR, BIKE, TRUCK
}

abstract class Vehicle {
   private String licenseNumber;
   private VehicleType type;

   public Vehicle(String licenseNumber, VehicleType type) {
       this.licenseNumber = licenseNumber;
       this.type = type;
   }

   public VehicleType getType() {
       return type;
   }

   public String getLicenseNumber() {
       return licenseNumber;
   }
}

class Car extends Vehicle {
   public Car(String licenseNumber) {
       super(licenseNumber, VehicleType.CAR);
   }
}

class Bike extends Vehicle {
   public Bike(String licenseNumber) {
       super(licenseNumber, VehicleType.BIKE);
   }
}

class Truck extends Vehicle {
   public Truck(String licenseNumber) {
       super(licenseNumber, VehicleType.TRUCK);
   }
}

class ParkingSpot {
   private final int spotNumber;
   private final VehicleType supportedType;
   private boolean isOccupied;
   private Vehicle parkedVehicle;

   public ParkingSpot(int spotNumber, VehicleType supportedType) {
       this.spotNumber = spotNumber;
       this.supportedType = supportedType;
       this.isOccupied = false;
   }

   public boolean canPark(Vehicle vehicle) {
       return !isOccupied && vehicle.getType() == supportedType;
   }

   public void park(Vehicle vehicle) {
       if (canPark(vehicle)) {
           this.parkedVehicle = vehicle;
           this.isOccupied = true;
       }
   }

   public void vacate() {
       this.parkedVehicle = null;
       this.isOccupied = false;
   }

   public boolean isOccupied() {
       return isOccupied;
   }

   public int getSpotNumber() {
       return spotNumber;
   }
}

class ParkingFloor {
   private final int floorNumber;
   private List spots;

   public ParkingFloor(int floorNumber, List spots) {
       this.floorNumber = floorNumber;
       this.spots = spots;
   }

   public ParkingSpot findAvailableSpot(Vehicle vehicle) {
       for (ParkingSpot spot : spots) {
           if (spot.canPark(vehicle)) {
               return spot;
           }
       }
       return null;
   }

   public int getFloorNumber() {
       return floorNumber;
   }

   public List getSpots() {
       return spots;
   }
}

class Ticket {
   private Vehicle vehicle;
   private int floorNumber;
   private int spotNumber;
   private LocalDateTime entryTime;
   private LocalDateTime exitTime;
   private double costPerHour = 10.0;

   public Ticket(Vehicle vehicle, int floorNumber, int spotNumber) {
       this.vehicle = vehicle;
       this.floorNumber = floorNumber;
       this.spotNumber = spotNumber;
       this.entryTime = LocalDateTime.now();
   }

   public void markExit() {
       this.exitTime = LocalDateTime.now();
   }

   public double calculateBill() {
       if (exitTime == null) {
           throw new IllegalStateException("Vehicle has not exited yet!");
       }
       long durationInHours = Math.max(1, Duration.between(entryTime, exitTime).toHours());
       return durationInHours * costPerHour;
   }

   public Vehicle getVehicle() {
       return vehicle;
   }

   public int getFloorNumber() {
       return floorNumber;
   }

   public int getSpotNumber() {
       return spotNumber;
   }
}

class ParkingLot {
   private List floors;
   private Map activeTickets = new HashMap<>();

   public ParkingLot(List floors) {
       this.floors = floors;
   }

   public Ticket parkVehicle(Vehicle vehicle) {
       for (ParkingFloor floor : floors) {
           ParkingSpot spot = floor.findAvailableSpot(vehicle);
           if (spot != null) {
               spot.park(vehicle);
               Ticket ticket = new Ticket(vehicle, floor.getFloorNumber(), spot.getSpotNumber());
               activeTickets.put(vehicle.getLicenseNumber(), ticket);
               System.out.println("Vehicle parked at floor " + floor.getFloorNumber() + " in spot " + spot.getSpotNumber());
               return ticket;
           }
       }
       System.out.println("No available spot for vehicle type: " + vehicle.getType());
       return null;
   }

   public double vacateSpot(String licenseNumber) {
       Ticket ticket = activeTickets.get(licenseNumber);
       if (ticket == null) {
           System.out.println("No active ticket found for vehicle: " + licenseNumber);
           return 0.0;
       }
       ParkingFloor floor = floors.get(ticket.getFloorNumber());
       ParkingSpot spot = floor.getSpots().get(ticket.getSpotNumber());
       spot.vacate();

       ticket.markExit();
       double bill = ticket.calculateBill();
       activeTickets.remove(licenseNumber);
       System.out.println("Vehicle with license " + licenseNumber + " exited. Bill: $" + bill);
       return bill;
   }
}

public class ParkingLotDemo {
   public static void main(String[] args) throws InterruptedException {
       List floor1Spots = new ArrayList<>();
       floor1Spots.add(new ParkingSpot(0, VehicleType.CAR));
       floor1Spots.add(new ParkingSpot(1, VehicleType.BIKE));

       List floor2Spots = new ArrayList<>();
       floor2Spots.add(new ParkingSpot(0, VehicleType.TRUCK));
       floor2Spots.add(new ParkingSpot(1, VehicleType.CAR));

       ParkingFloor floor1 = new ParkingFloor(0, floor1Spots);
       ParkingFloor floor2 = new ParkingFloor(1, floor2Spots);

       ParkingLot parkingLot = new ParkingLot(Arrays.asList(floor1, floor2));

       Vehicle car1 = new Car("CAR123");
       Vehicle bike1 = new Bike("BIKE456");
       Vehicle truck1 = new Truck("TRUCK789");

       Ticket ticket1 = parkingLot.parkVehicle(car1);
       Ticket ticket2 = parkingLot.parkVehicle(bike1);
       Ticket ticket3 = parkingLot.parkVehicle(truck1);

       Thread.sleep(2000); // Simulate time spent in parking

       parkingLot.vacateSpot(car1.getLicenseNumber());
       parkingLot.vacateSpot(bike1.getLicenseNumber());
       parkingLot.vacateSpot(truck1.getLicenseNumber());
   }
}

/*
UML Diagram
visit to visualize: https://www.planttext.com/

@startuml

enum VehicleType {
   CAR
   BIKE
   TRUCK
}

abstract class Vehicle {
   - licenseNumber: String
   - type: VehicleType
   + getLicenseNumber(): String
   + getType(): VehicleType
}

class Car
class Bike
class Truck

Vehicle <|-- Car
Vehicle <|-- Bike
Vehicle <|-- Truck

class ParkingSpot {
   - spotNumber: int
   - supportedType: VehicleType
   - isOccupied: boolean
   - parkedVehicle: Vehicle
   + canPark(Vehicle): boolean
   + park(Vehicle): void
   + vacate(): void
}

class ParkingFloor {
   - floorNumber: int
   - spots: List
   + findAvailableSpot(Vehicle): ParkingSpot
   + getFloorNumber(): int
   + getSpots(): List
}

class Ticket {
   - vehicle: Vehicle
   - floorNumber: int
   - spotNumber: int
   - entryTime: LocalDateTime
   - exitTime: LocalDateTime
   - costPerHour: double
   + markExit(): void
   + calculateBill(): double
}

class ParkingLot {
   - floors: List
   - activeTickets: Map
   + parkVehicle(Vehicle): Ticket
   + vacateSpot(String): double
}

Vehicle --> VehicleType
ParkingFloor "1" *-- "*" ParkingSpot
ParkingLot "1" o-- "*" ParkingFloor
ParkingLot --> Ticket
Ticket --> Vehicle

@enduml


*/