{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This notebook was prepared by [Donne Martin](https://github.com/donnemartin). Source and license info is on [GitHub](https://github.com/donnemartin/system-design-primer-primer)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Design a parking lot"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Constraints and assumptions\n",
    "\n",
    "* What types of vehicles should we support?\n",
    "    * Motorcycle, Car, Bus\n",
    "* Does each vehicle type take up a different amount of parking spots?\n",
    "    * Yes\n",
    "    * Motorcycle spot -> Motorcycle\n",
    "    * Compact spot -> Motorcycle, Car\n",
    "    * Large spot -> Motorcycle, Car\n",
    "    * Bus can park if we have 5 consecutive \"large\" spots\n",
    "* Does the parking lot have multiple levels?\n",
    "    * Yes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Solution"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Overwriting parking_lot.py\n"
     ]
    }
   ],
   "source": [
    "%%writefile parking_lot.py\n",
    "from abc import ABCMeta, abstractmethod\n",
    "\n",
    "\n",
    "class VehicleSize(Enum):\n",
    "\n",
    "    MOTORCYCLE = 0\n",
    "    COMPACT = 1\n",
    "    LARGE = 2\n",
    "\n",
    "\n",
    "class Vehicle(metaclass=ABCMeta):\n",
    "\n",
    "    def __init__(self, vehicle_size, license_plate, spot_size):\n",
    "        self.vehicle_size = vehicle_size\n",
    "        self.license_plate = license_plate\n",
    "        self.spot_size\n",
    "        self.spots_taken = []\n",
    "\n",
    "    def clear_spots(self):\n",
    "        for spot in self.spots_taken:\n",
    "            spot.remove_vehicle(self)\n",
    "        self.spots_taken = []\n",
    "\n",
    "    def take_spot(self, spot):\n",
    "        self.spots_taken.append(spot)\n",
    "\n",
    "    @abstractmethod\n",
    "    def can_fit_in_spot(self, spot):\n",
    "        pass\n",
    "\n",
    "\n",
    "class Motorcycle(Vehicle):\n",
    "\n",
    "    def __init__(self, license_plate):\n",
    "        super(Motorcycle, self).__init__(VehicleSize.MOTORCYCLE, license_plate, spot_size=1)\n",
    "\n",
    "    def can_fit_in_spot(self, spot):\n",
    "        return True\n",
    "\n",
    "\n",
    "class Car(Vehicle):\n",
    "\n",
    "    def __init__(self, license_plate):\n",
    "        super(Car, self).__init__(VehicleSize.COMPACT, license_plate, spot_size=1)\n",
    "\n",
    "    def can_fit_in_spot(self, spot):\n",
    "        return True if (spot.size == LARGE or spot.size == COMPACT) else False\n",
    "\n",
    "\n",
    "class Bus(Vehicle):\n",
    "\n",
    "    def __init__(self, license_plate):\n",
    "        super(Bus, self).__init__(VehicleSize.LARGE, license_plate, spot_size=5)\n",
    "\n",
    "    def can_fit_in_spot(self, spot):\n",
    "        return True if spot.size == LARGE else False\n",
    "\n",
    "\n",
    "class ParkingLot(object):\n",
    "\n",
    "    def __init__(self, num_levels):\n",
    "        self.num_levels = num_levels\n",
    "        self.levels = []\n",
    "\n",
    "    def park_vehicle(self, vehicle):\n",
    "        for level in levels:\n",
    "            if level.park_vehicle(vehicle):\n",
    "                return True\n",
    "        return False\n",
    "\n",
    "\n",
    "class Level(object):\n",
    "\n",
    "    SPOTS_PER_ROW = 10\n",
    "\n",
    "    def __init__(self, floor, total_spots):\n",
    "        self.floor = floor\n",
    "        self.num_spots = total_spots\n",
    "        self.available_spots = 0\n",
    "        self.parking_spots = []\n",
    "\n",
    "    def spot_freed(self):\n",
    "        self.available_spots += 1\n",
    "\n",
    "    def park_vehicle(self, vehicle):\n",
    "        spot = self._find_available_spot(vehicle)\n",
    "        if spot is None:\n",
    "            return None\n",
    "        else:\n",
    "            spot.park_vehicle(vehicle)\n",
    "            return spot\n",
    "\n",
    "    def _find_available_spot(self, vehicle):\n",
    "        \"\"\"Find an available spot where vehicle can fit, or return None\"\"\"\n",
    "        # ...\n",
    "\n",
    "    def _park_starting_at_spot(self, spot, vehicle):\n",
    "        \"\"\"Occupy starting at spot.spot_number to vehicle.spot_size.\"\"\"\n",
    "        # ...\n",
    "\n",
    "\n",
    "class ParkingSpot(object):\n",
    "\n",
    "    def __init__(self, level, row, spot_number, spot_size, vehicle_size):\n",
    "        self.level = level\n",
    "        self.row = row\n",
    "        self.spot_number = spot_number\n",
    "        self.spot_size = spot_size\n",
    "        self.vehicle_size = vehicle_size\n",
    "        self.vehicle = None\n",
    "\n",
    "    def is_available(self):\n",
    "        return True if self.vehicle is None else False\n",
    "\n",
    "    def can_fit_vehicle(self, vehicle):\n",
    "        if self.vehicle is not None:\n",
    "            return False\n",
    "        return vehicle.can_fit_in_spot(self)\n",
    "\n",
    "    def park_vehicle(self, vehicle):  # ...\n",
    "    def remove_vehicle(self):  # ..."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.4.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
