# Parking Lot System

#### Parking Lot: Low-Level Design (LLD) with Multi-Level Support, Layout, and Efficient Management

In this answer, we will design a **Multi-Level Parking Lot** that handles vehicles efficiently across multiple floors. The goal is to manage parking spaces, track available spots, and ensure efficient utilization of the parking lot. We will incorporate **design patterns** for modularity, flexibility, and maintainability, while addressing the design levels, class structures, vehicle management, and real-time space availability tracking.

***

#### **Design Overview**

A **Multi-Level Parking Lot** needs to handle multiple floors, each with different parking spots (compact, standard, and large). To manage this, we will define key components:

1. **ParkingLot**: Represents the entire parking lot, managing all floors.
2. **Floor**: Represents an individual floor in the parking lot, containing parking spots.
3. **ParkingSpot**: Represents an individual parking spot, which may vary in size (compact, standard, large).
4. **Vehicle**: Represents the vehicle occupying a parking spot (can be a car, motorcycle, or bus).
5. **VehicleManager**: Handles vehicle parking and unparking.
6. **PaymentSystem**: Handles the payment based on parking duration (not implemented in the code here but can be added for real-world applications).
7. **ParkingObserver**: Observes the parking lot for events like parking and unparking.

#### **Design Levels and Layout**

* **Multi-Level Structure**: A **multi-level parking lot** will have multiple floors, each represented by a **Floor** class. Each floor contains a set of **ParkingSpot** objects. Floors can vary in size, for example, some floors may have more compact spots for motorcycles, while others may be designed for larger vehicles like buses.
*   **Layout Consideration**: The parking lot should support the following layout:

    * **Compact spots** for small vehicles (motorcycles).
    * **Standard spots** for regular-sized vehicles (cars).
    * **Large spots** for larger vehicles (buses, trucks).

    We will ensure that the parking lot layout provides a balance between flexibility and space optimization by grouping the parking spots into different floor levels based on the vehicle types.

#### **Classes and Data Structures**

**1. ParkingSpot:**

A parking spot should be represented with the following attributes:

* **Size**: Determines whether the spot can fit a motorcycle, car, or bus.
* **Availability**: Tracks whether the spot is occupied or free.
* **Vehicle**: A reference to the vehicle currently occupying the spot (if any).

**2. Floor:**

Each floor will contain:

* A list of **ParkingSpot** objects.
* A counter to track the number of available spots, ensuring that space utilization is efficient.

**3. ParkingLot:**

The parking lot will manage:

* A list of **Floor** objects.
* A map to track vehicles by their **license plate** for easy lookup.
* The available spots count across the entire lot.

**4. Vehicle:**

A vehicle class should contain:

* **License Plate**: Unique identifier for the vehicle.
* **Size**: Whether the vehicle is small, medium, or large.

**5. VehicleManager:**

A class responsible for managing the parking and unparking of vehicles. It will ensure that:

* Vehicles are assigned to parking spots based on their size.
* Parking lot space is utilized efficiently by checking available spots across all floors.

***

#### **Detailed Code Implementation**

```java
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

// Step 1: Define Vehicle Types (Strategy Pattern)
abstract class Vehicle {
    protected String licensePlate;
    protected VehicleSize size;

    public Vehicle(String licensePlate, VehicleSize size) {
        this.licensePlate = licensePlate;
        this.size = size;
    }

    public VehicleSize getSize() {
        return size;
    }

    public String getLicensePlate() {
        return licensePlate;
    }
}

enum VehicleSize {
    SMALL, MEDIUM, LARGE;
}

class Car extends Vehicle {
    public Car(String licensePlate) {
        super(licensePlate, VehicleSize.MEDIUM);
    }
}

class Motorcycle extends Vehicle {
    public Motorcycle(String licensePlate) {
        super(licensePlate, VehicleSize.SMALL);
    }
}

class Bus extends Vehicle {
    public Bus(String licensePlate) {
        super(licensePlate, VehicleSize.LARGE);
    }
}

// Step 2: Define Parking Spot (Strategy Pattern)
abstract class ParkingSpot {
    protected VehicleSize size;
    protected boolean isOccupied;
    protected Vehicle vehicle;

    public ParkingSpot(VehicleSize size) {
        this.size = size;
        this.isOccupied = false;
    }

    public boolean parkVehicle(Vehicle vehicle) {
        if (!isOccupied && vehicle.getSize() == size) {
            this.vehicle = vehicle;
            isOccupied = true;
            return true;
        }
        return false;
    }

    public void freeSpot() {
        isOccupied = false;
        vehicle = null;
    }

    public boolean isOccupied() {
        return isOccupied;
    }

    public Vehicle getVehicle() {
        return vehicle;
    }
}

class CompactSpot extends ParkingSpot {
    public CompactSpot() {
        super(VehicleSize.SMALL);
    }
}

class StandardSpot extends ParkingSpot {
    public StandardSpot() {
        super(VehicleSize.MEDIUM);
    }
}

class LargeSpot extends ParkingSpot {
    public LargeSpot() {
        super(VehicleSize.LARGE);
    }
}

// Step 3: Define Floor (Singleton Pattern)
class Floor {
    private List<ParkingSpot> spots;
    private AtomicInteger availableSpots;

    public Floor(int numSmall, int numMedium, int numLarge) {
        spots = new ArrayList<>();
        availableSpots = new AtomicInteger(numSmall + numMedium + numLarge);

        // Create parking spots for each type
        for (int i = 0; i < numSmall; i++) spots.add(new CompactSpot());
        for (int i = 0; i < numMedium; i++) spots.add(new StandardSpot());
        for (int i = 0; i < numLarge; i++) spots.add(new LargeSpot());
    }

    public boolean parkVehicle(Vehicle vehicle) {
        for (ParkingSpot spot : spots) {
            if (spot.parkVehicle(vehicle)) {
                availableSpots.decrementAndGet();
                return true;
            }
        }
        return false;
    }

    public void freeSpot(ParkingSpot spot) {
        spot.freeSpot();
        availableSpots.incrementAndGet();
    }

    public int getAvailableSpots() {
        return availableSpots.get();
    }
}

// Step 4: Define ParkingLot (Singleton Pattern)
class ParkingLot {
    private static ParkingLot instance;
    private List<Floor> floors;
    private Map<String, Vehicle> parkedVehicles;

    private ParkingLot() {
        floors = new ArrayList<>();
        parkedVehicles = new HashMap<>();
    }

    public static synchronized ParkingLot getInstance() {
        if (instance == null) {
            instance = new ParkingLot();
        }
        return instance;
    }

    public void addFloor(Floor floor) {
        floors.add(floor);
    }

    public boolean parkVehicle(Vehicle vehicle) {
        for (Floor floor : floors) {
            if (floor.parkVehicle(vehicle)) {
                parkedVehicles.put(vehicle.getLicensePlate(), vehicle);
                return true;
            }
        }
        return false;
    }

    public boolean freeVehicle(String licensePlate) {
        Vehicle vehicle = parkedVehicles.get(licensePlate);
        if (vehicle != null) {
            for (Floor floor : floors) {
                for (ParkingSpot spot : floor.spots) {
                    if (spot.getVehicle() == vehicle) {
                        floor.freeSpot(spot);
                        parkedVehicles.remove(licensePlate);
                        return true;
                    }
                }
            }
        }
        return false;
    }

    public int getAvailableSpots() {
        int available = 0;
        for (Floor floor : floors) {
            available += floor.getAvailableSpots();
        }
        return available;
    }
}

// Step 5: Vehicle Manager for Parking/Unparking Vehicles
class VehicleManager {
    private ParkingLot parkingLot;

    public VehicleManager(ParkingLot parkingLot) {
        this.parkingLot = parkingLot;
    }

    public boolean parkVehicle(Vehicle vehicle) {
        return parkingLot.parkVehicle(vehicle);
    }

    public boolean unparkVehicle(String licensePlate) {
        return parkingLot.freeVehicle(licensePlate);
    }

    public int getAvailableSpots() {
        return parkingLot.getAvailableSpots();
    }
}

// Step 6: Observer Pattern for Logging and Monitoring
interface ParkingObserver {
    void update(String message);
}

class ParkingLogger implements ParkingObserver {
    @Override
    public void update(String message) {
        System.out.println("Parking Event: " + message);
    }
}

// Step 7: Factory Method for Vehicle Creation
class VehicleFactory {
    public static Vehicle createVehicle(String type, String licensePlate) {
        switch (type) {
            case "Car":
                return new Car(licensePlate);
            case "Motorcycle":
                return new Motorcycle(licensePlate);
            case "Bus":
                return new Bus(licensePlate);
            default:
                throw new IllegalArgumentException("Unknown vehicle type");
        }
    }
}

// Step 8: Main Class for Demonstration
public class Main {
    public static void main(String[] args) {
        // Create a parking lot instance (Singleton)
        ParkingLot parkingLot = ParkingLot.getInstance();

        // Add some floors to the parking lot
        Floor floor1 = new Floor(10, 10, 5); // 10 small, 10 medium, 5 large
        parkingLot.addFloor(floor1);

        // Add observers (logging)
        ParkingLogger logger = new ParkingLogger();

        // Create vehicle manager for parking/unparking vehicles
        VehicleManager manager = new VehicleManager(parkingLot);

        // Park some vehicles
        Vehicle car1 = VehicleFactory.createVehicle("Car", "ABC123");
        Vehicle motorcycle1 = VehicleFactory.createVehicle("Motorcycle", "XYZ456");

        System.out.println("Available spots before parking: " + parkingLot.getAvailableSpots());

        manager.parkVehicle(car1);
        manager.parkVehicle(motorcycle1);

        System.out.println("Available spots after parking: " + parkingLot.getAvailableSpots());

        // Free a vehicle
        manager.unparkVehicle("ABC123");
        System.out.println("Available spots after freeing car1: " + parkingLot.getAvailableSpots());
    }
}
```

***

#### **Explanation of Design**

**1. Multi-Level Structure:**

* **ParkingLot** contains multiple **Floor** objects, each representing a different floor in the multi-level parking lot.
* The **Floor** class has different parking spots (compact, standard, large), and space availability is tracked dynamically.

**2. Efficient Space Utilization:**

* The **ParkingManager** ensures that the correct size vehicle is parked in a suitable spot. It dynamically checks all floors to find the most suitable parking spot.
* The **Floor** class maintains an `AtomicInteger` counter to track the available spots, updating in real-time when spots are taken or freed.

**3. Real-Time Tracking of Available Parking Space:**

* The **ParkingLot** provides an easy way to get the total number of available spots across all floors. Each **Floor** keeps track of its own available spots, and when
