/**
  * The number of seats in the requested level that are neither held nor reserved
  *
  * @param venueLevel a numeric venue level identifier to limit the search
  * @return the number of tickets available on the provided level
  */
 @Override
 public int numSeatsAvailable(Integer venueLevel) {
   resetHolds();
   List<LevelVO> levelList = TemporaryDatabase.getStage();
   int seatsAvailable = 0;
   if (levelList != null && !levelList.isEmpty()) {
     for (LevelVO levelVO : levelList) {
       if (levelVO != null && levelVO.getLevelId() == venueLevel) {
         Map<Integer, List<SeatVO>> rowsAndSeats = levelVO.getRowsAndSeats();
         if (rowsAndSeats != null && !rowsAndSeats.isEmpty()) {
           Set<Integer> rows = rowsAndSeats.keySet();
           if (rows != null && !rows.isEmpty()) {
             for (Integer rowId : rows) {
               if (rowId != -1) {
                 List<SeatVO> seats = rowsAndSeats.get(rowId);
                 if (seats != null && !seats.isEmpty()) {
                   for (SeatVO seatVO : seats) {
                     if (seatVO.getStatusCode() == Constants.SEAT_AVAILABLE) {
                       seatsAvailable++;
                     }
                   }
                 }
               }
             }
           }
         }
       }
     }
   }
   return seatsAvailable;
 }
 /**
  * If holds are not expired, seat status will be changed to reserved.
  *
  * @param levelNumber The level number where seats need to be reserved
  * @param rowNumber row number where seats need to be reserved
  * @param seatNum seat number to be reserved
  * @return reservation status.
  */
 private boolean processSeatReserve(int levelNumber, int rowNumber, int seatNum) {
   List<LevelVO> levels = TemporaryDatabase.getStage();
   for (LevelVO levelVO : levels) {
     if (levelVO != null && levelVO.getLevelId() == levelNumber) {
       Map<Integer, List<SeatVO>> rowsAndSeats = levelVO.getRowsAndSeats();
       Set<Integer> rows = rowsAndSeats.keySet();
       if (rows != null && !rows.isEmpty()) {
         for (Integer row : rows) {
           if (row == rowNumber) {
             List<SeatVO> seats = rowsAndSeats.get(row);
             if (seats != null && !seats.isEmpty()) {
               for (SeatVO seatVO : seats) {
                 if (seatVO != null
                     && seatVO.getSeatNum() == seatNum
                     && seatVO.getStatusCode() == Constants.SEAT_HOLD) {
                   // Update stauts code
                   if (holdNotExpired(seatVO.getHoldTimeStamp().getTime())) {
                     seatVO.setStatusCode(Constants.SEAT_RESERVED);
                     return true;
                   } else {
                     seatVO.setStatusCode(Constants.SEAT_AVAILABLE);
                     return false;
                   }
                 }
               }
             }
           }
         }
       }
     }
   }
   return false;
 }
  /**
   * @param minLevel minimum level where best selection starts
   * @param maxLevel maximum level where best selection ends
   * @param numSeats number of seats need to be put on hold
   * @param seatsToHold List keeps track of seats on hold in this perticualr request.
   * @return SeatHold object which contains hold details.
   */
  private SeatHoldVO processSeatHold(
      int minLevel, int maxLevel, int numSeats, List<SeatHoldVO> seatsToHold) {
    List<LevelVO> levels = TemporaryDatabase.getStage();
    if (numSeats > 0) {
      // Generates a randome SeatHoldId while creating SeatHold object.
      SeatHoldVO seatHold = new SeatHoldVO(generateRandom());
      for (LevelVO levelVO : levels) {
        if (numSeats <= 0) {
          break;
        }
        if (levelVO != null && levelVO.getLevelId() == minLevel && minLevel <= maxLevel) {
          Map<Integer, List<SeatVO>> rowsAndSeats = levelVO.getRowsAndSeats();
          Set<Integer> rows = rowsAndSeats.keySet();
          if (rows != null && !rows.isEmpty()) {
            for (Integer row : rows) {
              if (numSeats <= 0) {
                break;
              }
              List<SeatVO> seats = rowsAndSeats.get(row);
              if (seats != null && !seats.isEmpty()) {
                for (SeatVO seatVO : seats) {
                  if (numSeats <= 0) {
                    break;
                  }
                  if (seatVO != null
                      && seatVO.getStatusCode() == Constants.SEAT_AVAILABLE
                      && numSeats > 0) {
                    Date dt = new Date();
                    // Update with Random ID
                    SeatInfoVO seatInfo =
                        new SeatInfoVO(
                            levelVO.getLevelId(),
                            row,
                            seatVO.getSeatNum(),
                            levelVO.getPrice(),
                            levelVO.getLevelName(),
                            dt);
                    seatHold.addSeat(seatInfo);

                    // Update stauts code
                    seatVO.setHoldTimeStamp(dt);
                    seatVO.setStatusCode(Constants.SEAT_HOLD);
                    numSeats--;
                  }
                }
              }
            }
          }
          minLevel++;
        }
      }
      seatsToHold.add(seatHold);
      return seatHold;
    }
    return null;
  }
  /**
   * Commit seats held for a specific customer
   *
   * @param seatHoldId the seat hold identifier
   * @param customerEmail the email address of the customer to which the seat hold is assigned
   * @return a reservation confirmation code
   */
  @Override
  public String reserveSeats(int seatHoldId, String customerEmail) {
    // Before proceeding with reservation, make sure holds are not expired.
    resetHolds();

    // NOW proceed with reservation logic.
    Map<String, List<SeatHoldVO>> seatsOnHold = TemporaryDatabase.getSeatsOnHold();
    int failureCnt = 0;
    if (seatsOnHold != null && !seatsOnHold.isEmpty()) {
      List<SeatHoldVO> seatHoldList = seatsOnHold.get(customerEmail);
      if (seatHoldList != null && !seatHoldList.isEmpty()) {
        Iterator<SeatHoldVO> seatHoldIter = seatHoldList.iterator();
        while (seatHoldIter.hasNext()) {
          SeatHoldVO seatHold = seatHoldIter.next();
          if (seatHold != null && seatHold.getSeatHoldId() == seatHoldId) {
            List<SeatInfoVO> seatsList = seatHold.getSeatsList();
            if (seatsList != null && !seatsList.isEmpty()) {
              Iterator<SeatInfoVO> seatInfoIter = seatsList.iterator();
              while (seatInfoIter.hasNext()) {
                SeatInfoVO seatInfo = seatInfoIter.next();
                boolean reserveStatus =
                    processSeatReserve(
                        seatInfo.getLevelNum(), seatInfo.getRowNum(), seatInfo.getSeatNum());
                if (!reserveStatus) {
                  failureCnt++;
                }
                seatInfoIter.remove();
              }
              seatHoldIter.remove();
            } else {
              // WE are about to reserve seats, but seats are EMPTY, which means failure.
              failureCnt++;
            }
          }
        }
      } else {
        // WE are about to reserve seats, but seats are EMPTY, which means failure.
        failureCnt++;
      }
    }
    if (failureCnt > 0) {
      return Constants.FAILURE_MSG;
    }
    return Constants.SUCCES_MSG;
  }
 /**
  * Find and hold the best available seats for a customer
  *
  * @param numSeats the number of seats to find and hold
  * @param minLevel the minimum venue level
  * @param maxLevel the maximum venue level
  * @param customerEmail unique identifier for the customer
  * @return a SeatHold object identifying the specific seats and related information
  */
 @Override
 public SeatHoldVO findAndHoldSeats(
     int numSeats, Integer minLevel, Integer maxLevel, String customerEmail) {
   resetHolds();
   List<SeatHoldVO> seatsToHold = new ArrayList<>();
   if (minLevel > 0 && (maxLevel > 0 && maxLevel < 5)) {
     // Va
     Map<String, List<SeatHoldVO>> seatsOnHold = TemporaryDatabase.getSeatsOnHold();
     if (seatsOnHold.isEmpty()) {
       SeatHoldVO seatHold = processSeatHold(minLevel, maxLevel, numSeats, seatsToHold);
       seatsOnHold.put(customerEmail, seatsToHold);
       return seatHold;
     } else {
       seatsToHold = seatsOnHold.get(customerEmail);
       if (seatsToHold == null) {
         seatsToHold = new ArrayList<>();
       }
       SeatHoldVO seatHold = processSeatHold(minLevel, maxLevel, numSeats, seatsToHold);
       seatsOnHold.put(customerEmail, seatsToHold);
       return seatHold;
     }
   }
   return null;
 }
  /**
   * This method updates temporary database maps hold status to AVAILABLE, if Hold expired after 10
   * seconds.
   */
  private void resetHolds() {
    // Reset Holds on Levels
    List<LevelVO> levels = TemporaryDatabase.getStage();
    for (LevelVO levelVO : levels) {
      if (levelVO != null) {
        Map<Integer, List<SeatVO>> rowsAndSeats = levelVO.getRowsAndSeats();
        Set<Integer> rows = rowsAndSeats.keySet();
        if (rows != null && !rows.isEmpty()) {
          for (Integer row : rows) {
            List<SeatVO> seats = rowsAndSeats.get(row);
            if (seats != null && !seats.isEmpty()) {
              for (SeatVO seatVO : seats) {
                if (seatVO != null && seatVO.getStatusCode() == Constants.SEAT_HOLD) {
                  if (!holdNotExpired(seatVO.getHoldTimeStamp().getTime())) {
                    // Update stauts code to available
                    seatVO.setStatusCode(Constants.SEAT_AVAILABLE);
                  }
                }
              }
            }
          }
        }
      }
    }

    // Reset Holds on HoldsAndSeats
    Map<String, List<SeatHoldVO>> holds = TemporaryDatabase.getSeatsOnHold();
    if (holds != null && !holds.isEmpty()) {
      Collection<List<SeatHoldVO>> seatHolds = holds.values();
      if (seatHolds != null && !seatHolds.isEmpty()) {
        for (List<SeatHoldVO> seatHoldList : seatHolds) {
          if (seatHoldList != null && !seatHoldList.isEmpty()) {
            Iterator<SeatHoldVO> seatHoldIter = seatHoldList.iterator();
            while (seatHoldIter.hasNext()) {
              SeatHoldVO seatHold = seatHoldIter.next();
              if (seatHold != null
                  && seatHold.getSeatsList() != null
                  && !seatHold.getSeatsList().isEmpty()) {
                Iterator<SeatInfoVO> seatInfoIter = seatHold.getSeatsList().iterator();
                while (seatInfoIter.hasNext()) {
                  SeatInfoVO seatInfo = seatInfoIter.next();
                  if (!holdNotExpired(seatInfo.getHoldTimeStamp().getTime())) {
                    seatInfoIter.remove();
                  }
                }
              }

              if (seatHold != null && seatHold.getSeatsList().isEmpty()) {
                seatHoldIter.remove();
              }
            }
          }
        }
      }
    }

    //        Map<String, List<SeatHoldVO>> resetHolds = TemporaryDatabase.getSeatsOnHold();
    //        if (resetHolds != null && !resetHolds.isEmpty()) {
    //            Collection<List<SeatHoldVO>> seatHolds = resetHolds.values();
    //            Iterator<List<SeatHoldVO>> seatIter = seatHolds.iterator();
    //            while (seatIter.hasNext()) {
    //                List<SeatHoldVO> seatholdList = seatIter.next();
    //                if (seatholdList != null && !seatholdList.isEmpty()) {
    //                    Iterator<SeatHoldVO> seatholdIter = seatholdList.iterator();
    //                    while (seatholdIter.hasNext()) {
    //
    //                    }
    //                }
    //            }
    //        }

  }