          lessonInfoSeq)))
 
 (defn- get-largest-consecutive-time-slot-freed-up-by-lesson
-  "Given a removed lessonInfo, find the largest interval which is freed up due
-   to the its removal.
+  "Given a removed lesson, find the largest interval on the same row which is
+   freed up due to the its removal.
 
    For instance, given this timetable row:
 

   2 empty slots to its right will be freed up.

   Given that the first slot occupied by the label 'x' is the index 0, this
   function will return the vector [3 11]."
  [removedLessonInfo]
  (let [day        (:day removedLessonInfo)
        row        (:rowNum removedLessonInfo)
        startTime  (:startTime removedLessonInfo)
        endTime    (:endTime removedLessonInfo)
        ttRow      (get-in Timetable [day row])
        minTimeIdx (time-helper/TIME-INDEX-MIN)
        maxTimeIdx (time-helper/TIME-INDEX-MAX)]

    [
      ; Find the minimum timeslot that's free
      (loop [timeIdx     (dec startTime)
             occupiedVec (:occupied ttRow)]
        (cond (< timeIdx minTimeIdx)           minTimeIdx
              (nil? (nth occupiedVec timeIdx)) (recur (dec timeIdx) occupiedVec)
              :else                            (inc timeIdx)))

      ; Find the maximum timeslot that's free
      (loop [timeIdx     (inc endTime)
             occupiedVec (:occupied ttRow)]
        (cond (> timeIdx maxTimeIdx)          maxTimeIdx
              (nil? (nth occupiedVec timeIdx) (recur (inc timeIdx) occupiedVec))
              :else                           (dec timeIdx)))
    ]))

(defn- find-replacement-lessons-within-time
  "Given a lessonInfo which has been removed, find other lessons
   on the same day but in a row below the removed lesson, which could be shifted
   upwards and take the timeslots freed up by the removed lesson."
  [removedLessonInfo]
  (let [day       (:day removedLessonInfo)
        row       (:rowNum removedLessonInfo)
        startTime (:startTime removedLessonInfo)
        endTime   (:endTime removedLessonInfo)

        ; This is the time range freed up by removal of the lesson
        [lowTimeIdx highTimeIdx]
        (get-largest-consecutive-time-slot-freed-up-by-lesson
          removedLessonInfo)

        ModulesMap (aget js/window "ModulesMap")]

    (:lessonsToShiftUp
      (reduce
        (fn [reduceResult ttRow]
          (if (<= (:nrLessons ttRow) 0)
              ; Skip empty row
              reduceResult
              ; Non-empty row
              (let [ttRowOccupied      (:occupied ttRow)
                    currentOccupiedVec (:occupiedVec reduceResult)
  
                    ; get any lessons overlapping with [lowTimeIdx, highTimeIdx]
                    ttRowLessonsPartiallyInTimeRange
                    (reduce (fn [lessonsSet lesson] (conj lessonsSet lesson))
                            #{}
                            ; get everything in [lowTimeIdx, highTimeIdx] and
                            ; remove all `nil`
                            (filter #(not (nil? %1))
                                    (map #(nth ttRowOccupied %1)
                                         (range lowTimeIdx (inc highTimeIdx)))))
  
                    ; Filter those that are within the time range and can fit in
                    ; and unoccupied time interval
                    ttRowLessonsToShift
                    (filter #(let [lesson
                                   (get-in ModulesMap
                                           [(:moduleCode %1) "lessons"
                                            (:lessonType %1) (:lessonGroup %1)
                                            (:index %1)])

                                   lessonStart (:startTime lesson)
                                   lessonEnd   (:endTime lesson)]
                               (and
                                 ; check that lesson is in time range
                                 (>= lessonStart lowTimeIdx)
                                 (<= (dec lessonEnd) highTimeIdx)
                                 ; and is within an unoccupied interval
                                 (every? #(nil? %1)
                                         (map #(nth currentOccupiedVec %1)
                                              (range lessonStart lessonEnd)))))
                            ttRowLessonsPartiallyInTimeRange)]

                {:lessonToShiftUp
                 (reduce (fn [ttRowLessonsSet ttRowLesson]
                           (conj ttRowLessonsSet ttRowLesson))
                         (:lessonsToShiftUp reduceResult)
                         ttRowLessonsToShift)

                 :occupiedVec
                 (reduce (fn [occupiedVec ttRowLesson]
                           (reduce (fn [occVec timeIdx]
                                     (assoc occVec timeIdx true))
                                   occupiedVec
                                   ; get time range of the lesson
                                   (let [lesson
                                         (get-in ModulesMap
                                                 [(:moduleCode ttRowLesson)
                                                  "lessons"
                                                  (:lessonType ttRowLesson)
                                                  (:lessonGroup ttRowLesson)
                                                  (:index ttRowLesson)])]
                                     (range (:startTime lesson)
                                            (:endTime lesson)))))
                         currentOccupiedVec
                         ttRowLessonsToShift)})))
        {:lessonsToShiftUp #{},
         ; nil = free slot, true = occupied slot
         :occupiedVec      [vec (map (fn [t] nil) (range 800 2400 50))]}
        ; go through each row in the day, below the current row
        (drop (inc row) (get-timetable-day day))))))

(defn- shift-lesson-to-row
  "Shifts a lesson from its original row up to the given row."
  [ttRowLesson rowNum]
  j
  )

(defn- shift-lessons-upwards-to-replace-empty-slots
  "For each removed lesson, see if there are any lessons in rows below it that
   can be shifted upwards to occupy the empty slots resulting from its removal."
  [removedLessonInfoSeq]
  (let [; we do not consider any lessons on empty rows for ease of
        ; implementation
        exclude-lessons-on-empty-rows

          (filter #(let [day   (:day %1)
                         row   (:rowNum %1)
                         ttRow (get-in Timetable [day row])]
                     (not (empty? ttRow)))
                  lessonInfoSeq))

        sort-lesson-info-seq
        (fn [lessonInfoSeq]
          (sort #(let [dayA (:day %1)
                       dayB (:day %2)]
                   (if (= dayA dayB)
                       (< (:rowNum %1) (:rowNum %2))
                       (< dayA dayB)))
                lessonInfoSeq))]

    (loop [sortedAugTTLessonInfoSeq
           (sort-lesson-info-seq (exclude-lessons-on-empty-rows
                                   removedAugmentedTTLessonInfoSeq))

           nextLessonInfoVec    []]
      (cond (and (empty? sortedAugTTLessonInfoSeq) (empty? nextLessonInfoVec))
            nil

            ; we're done for the current set of lessons. Move on to the
            ; set of lessons that were shifted upwards.
            (empty? sortedAugTTLessonInfoSeq)
            (recur (sort-lesson-info-seq (exclude-lessons-on-empty-rows
                                           nextLessonInfoVec))
                   [])

            ; find timeslot to shift up to replace gap
            :else
            (let [augTTLessonInfo         (first sortedAugTTLessonInfoSeq)
                  rowNum                  (:rowNum augTTLessonInfo)
                  ; TODO: Rename this
                  replacementTTRowLessons (find-replacement-lessons-within-time
                                            augTTLessonInfo)]
              ; TODO: Continue editing here
              (doseq [ttRowLesson replacementTTRowLessons]
                (shift-lesson-to-row ttRowLesson rowNum))
              ; TODO: `replacementLessons` is not in the format we want. Make it so.
              (recur (rest sortedAugTTLessonInfoSeq)
                     ; append newly shifted lessons to nextLessonInfoVec
                     (loop [newlyShiftedLessons    replacementLessons
                            nextLessonInfoVecPrime nextLessonInfoVec]
