package de.rwth.idsg.steve.repository.impl;
import de.rwth.idsg.steve.SteveException;
import de.rwth.idsg.steve.repository.ReservationRepository;
import de.rwth.idsg.steve.repository.ReservationStatus;
import de.rwth.idsg.steve.repository.dto.InsertReservationParams;
import de.rwth.idsg.steve.repository.dto.Reservation;
import de.rwth.idsg.steve.utils.CustomDSL;
import de.rwth.idsg.steve.utils.DateTimeUtils;
import de.rwth.idsg.steve.web.dto.ReservationQueryForm;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.jooq.DSLContext;
import org.jooq.Record1;
import org.jooq.Record10;
import org.jooq.RecordMapper;
import org.jooq.SelectConditionStep;
import org.jooq.SelectQuery;
import org.jooq.exception.DataAccessException;
import org.jooq.impl.DSL;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import java.util.List;
import static jooq.steve.db.tables.ChargeBox.CHARGE_BOX;
import static jooq.steve.db.tables.Connector.CONNECTOR;
import static jooq.steve.db.tables.OcppTag.OCPP_TAG;
import static jooq.steve.db.tables.Reservation.RESERVATION;
/**
* @author Sevket Goekay <goekay@dbis.rwth-aachen.de>
* @since 14.08.2014
*/
@Slf4j
@Repository
public class ReservationRepositoryImpl implements ReservationRepository {
@Autowired private DSLContext ctx;
@Override
@SuppressWarnings("unchecked")
public List<Reservation> getReservations(ReservationQueryForm form) {
SelectQuery selectQuery = ctx.selectQuery();
selectQuery.addFrom(RESERVATION);
selectQuery.addJoin(OCPP_TAG, OCPP_TAG.ID_TAG.eq(RESERVATION.ID_TAG));
selectQuery.addJoin(CONNECTOR, CONNECTOR.CONNECTOR_PK.eq(RESERVATION.CONNECTOR_PK));
selectQuery.addJoin(CHARGE_BOX, CONNECTOR.CHARGE_BOX_ID.eq(CHARGE_BOX.CHARGE_BOX_ID));
selectQuery.addSelect(
RESERVATION.RESERVATION_PK,
RESERVATION.TRANSACTION_PK,
OCPP_TAG.OCPP_TAG_PK,
CHARGE_BOX.CHARGE_BOX_PK,
OCPP_TAG.ID_TAG,
CHARGE_BOX.CHARGE_BOX_ID,
RESERVATION.START_DATETIME,
RESERVATION.EXPIRY_DATETIME,
RESERVATION.STATUS,
CONNECTOR.CONNECTOR_ID
);
if (form.isChargeBoxIdSet()) {
selectQuery.addConditions(CHARGE_BOX.CHARGE_BOX_ID.eq(form.getChargeBoxId()));
}
if (form.isOcppIdTagSet()) {
selectQuery.addConditions(RESERVATION.ID_TAG.eq(form.getOcppIdTag()));
}
if (form.isStatusSet()) {
selectQuery.addConditions(RESERVATION.STATUS.eq(form.getStatus().name()));
}
processType(selectQuery, form);
// Default order
selectQuery.addOrderBy(RESERVATION.EXPIRY_DATETIME.asc());
return selectQuery.fetch().map(new ReservationMapper());
}
@Override
public List<Integer> getActiveReservationIds(String chargeBoxId) {
return ctx.select(RESERVATION.RESERVATION_PK)
.from(RESERVATION)
.where(RESERVATION.CONNECTOR_PK.in(DSL.select(CONNECTOR.CONNECTOR_PK)
.from(CONNECTOR)
.where(CONNECTOR.CHARGE_BOX_ID.equal(chargeBoxId))))
.and(RESERVATION.EXPIRY_DATETIME.greaterThan(CustomDSL.utcTimestamp()))
.and(RESERVATION.STATUS.equal(ReservationStatus.ACCEPTED.name()))
.fetch(RESERVATION.RESERVATION_PK);
}
@Override
public int insert(InsertReservationParams params) {
// Check overlapping
//isOverlapping(startTimestamp, expiryTimestamp, chargeBoxId);
SelectConditionStep<Record1<Integer>> connectorPkQuery =
DSL.select(CONNECTOR.CONNECTOR_PK)
.from(CONNECTOR)
.where(CONNECTOR.CHARGE_BOX_ID.equal(params.getChargeBoxId()))
.and(CONNECTOR.CONNECTOR_ID.equal(params.getConnectorId()));
int reservationId = ctx.insertInto(RESERVATION)
.set(RESERVATION.CONNECTOR_PK, connectorPkQuery)
.set(RESERVATION.ID_TAG, params.getIdTag())
.set(RESERVATION.START_DATETIME, params.getStartTimestamp())
.set(RESERVATION.EXPIRY_DATETIME, params.getExpiryTimestamp())
.set(RESERVATION.STATUS, ReservationStatus.WAITING.name())
.returning(RESERVATION.RESERVATION_PK)
.fetchOne()
.getReservationPk();
log.debug("A new reservation '{}' is inserted.", reservationId);
return reservationId;
}
@Override
public void delete(int reservationId) {
ctx.delete(RESERVATION)
.where(RESERVATION.RESERVATION_PK.equal(reservationId))
.execute();
log.debug("The reservation '{}' is deleted.", reservationId);
}
@Override
public void accepted(int reservationId) {
internalUpdateReservation(reservationId, ReservationStatus.ACCEPTED);
}
@Override
public void cancelled(int reservationId) {
internalUpdateReservation(reservationId, ReservationStatus.CANCELLED);
}
@Override
public void used(int reservationId, int transactionId) {
ctx.update(RESERVATION)
.set(RESERVATION.STATUS, ReservationStatus.USED.name())
.set(RESERVATION.TRANSACTION_PK, transactionId)
.where(RESERVATION.RESERVATION_PK.equal(reservationId))
.execute();
}
// -------------------------------------------------------------------------
// Private helpers
// -------------------------------------------------------------------------
private static class ReservationMapper implements
RecordMapper<Record10<Integer, Integer, Integer, Integer, String,
String, DateTime, DateTime, String, Integer>, Reservation> {
@Override
public Reservation map(Record10<Integer, Integer, Integer, Integer, String,
String, DateTime, DateTime, String, Integer> r) {
return Reservation.builder()
.id(r.value1())
.transactionId(r.value2())
.ocppTagPk(r.value3())
.chargeBoxPk(r.value4())
.ocppIdTag(r.value5())
.chargeBoxId(r.value6())
.startDatetimeDT(r.value7())
.startDatetime(DateTimeUtils.humanize(r.value7()))
.expiryDatetimeDT(r.value8())
.expiryDatetime(DateTimeUtils.humanize(r.value8()))
.status(r.value9())
.connectorId(r.value10())
.build();
}
}
private void internalUpdateReservation(int reservationId, ReservationStatus status) {
try {
ctx.update(RESERVATION)
.set(RESERVATION.STATUS, status.name())
.where(RESERVATION.RESERVATION_PK.equal(reservationId))
.execute();
} catch (DataAccessException e) {
log.error("Updating of reservationId '{}' to status '{}' FAILED.", reservationId, status, e);
}
}
private void processType(SelectQuery selectQuery, ReservationQueryForm form) {
switch (form.getPeriodType()) {
case ACTIVE:
selectQuery.addConditions(RESERVATION.EXPIRY_DATETIME.greaterThan(CustomDSL.utcTimestamp()));
break;
case FROM_TO:
selectQuery.addConditions(
RESERVATION.START_DATETIME.greaterOrEqual(form.getFrom().toDateTime()),
RESERVATION.EXPIRY_DATETIME.lessOrEqual(form.getTo().toDateTime())
);
break;
default:
throw new SteveException("Unknown enum type");
}
}
/**
* Throws exception, if there are rows whose date/time ranges overlap with the input
*/
// private void isOverlapping(DateTime start, DateTime stop, String chargeBoxId) {
// try {
// int count = ctx.selectOne()
// .from(RESERVATION)
// .where(RESERVATION.EXPIRY_DATETIME.greaterOrEqual(start))
// .and(RESERVATION.START_DATETIME.lessOrEqual(stop))
// .and(RESERVATION.CHARGE_BOX_ID.equal(chargeBoxId))
// .execute();
//
// if (count != 1) {
// throw new SteveException("The desired reservation overlaps with another reservation");
// }
//
// } catch (DataAccessException e) {
// log.error("Exception occurred", e);
// }
// }
}