package gov.nysenate.openleg.model.spotcheck.daybreak;
import com.google.common.base.MoreObjects;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* A container that holds all daybreak documents necessary to form a daybreak report
* Performs checks to make sure all necessary doc types are present
* @param <DaybreakDoc>
*/
public class DaybreakReport <DaybreakDoc extends DaybreakDocument> {
/** A map containing the daybreak documents mapped to their doc type */
private Map<DaybreakDocType, DaybreakDoc> reportDocs;
/** Specifies the accepted temporal margin of error for the report date of each document */
private static final Duration reportDateMarginOfError = Duration.ofHours(23);
/** Is set to the date of the first entry and used as a reference to prevent date piggybacking */
private LocalDateTime anchorDate;
/** The official report date, set to the day that the last daybreak message was sent */
private LocalDate reportDate;
/** --- Constructors --- */
public DaybreakReport(){
reportDocs = new HashMap<>();
anchorDate = null;
reportDate = null;
}
public DaybreakReport(DaybreakDoc daybreakDoc){
reportDocs = new HashMap<>();
reportDocs.put(daybreakDoc.getDaybreakDocType(), daybreakDoc);
anchorDate = daybreakDoc.getReportDateTime();
reportDate = anchorDate.toLocalDate();
}
public DaybreakReport(Collection<DaybreakDoc> docCollection) throws DaybreakReportInsertException{
this();
for(DaybreakDoc daybreakDoc : docCollection){
insertDaybreakDoc(daybreakDoc);
}
}
/** --- Functions --- */
/**
* Attempts to insert a daybreak document into the report.
* If it is the first document to be inserted it is inserted with no issue.
* If the document report date is not close enough to this report date an exception is thrown
* An exception is also thrown if the report already contains a daybreak document of the same type
* @param daybreakDoc
* @throws DaybreakReportInsertException
*/
public void insertDaybreakDoc(DaybreakDoc daybreakDoc) throws DaybreakReportInsertException{
if (anchorDate==null){
reportDocs.put(daybreakDoc.getDaybreakDocType(), daybreakDoc);
anchorDate = daybreakDoc.getReportDateTime();
reportDate = anchorDate.toLocalDate();
}
else if(!dateInMarginOfError(daybreakDoc.getReportDateTime())){
throw new DaybreakReportInsertException("date out of range" + daybreakDoc.getReportDateTime(),
DaybreakReportInsertException.InsertExceptionReason.DATE_OUT_OF_RANGE, daybreakDoc, this.reportDate);
}
else if (reportDocs.keySet().contains(daybreakDoc.getDaybreakDocType())){
throw new DaybreakReportInsertException("report already contains " + daybreakDoc.getDaybreakDocType(),
DaybreakReportInsertException.InsertExceptionReason.REPORT_CONTAINS_TYPE, daybreakDoc, this.reportDate);
}
else {
if(daybreakDoc.getReportDateTime().toLocalDate().isAfter(this.reportDate)){
reportDate = daybreakDoc.getReportDateTime().toLocalDate();
}
reportDocs.put(daybreakDoc.getDaybreakDocType(), daybreakDoc);
}
}
public boolean dateInMarginOfError(LocalDateTime dateTime){
return Duration.between(anchorDate, dateTime).abs().minus(reportDateMarginOfError).isNegative();
}
public boolean isComplete(){
return DaybreakDocType.containsAllDocTypes(reportDocs.keySet());
}
/** --- Functional Getters/Setters --- */
public DaybreakDoc getDaybreakDoc(DaybreakDocType docType){
return reportDocs.get(docType);
}
public DaybreakDoc getDaybreakDoc(String fileName) {
return reportDocs.get(DaybreakDocType.getFileDocType(fileName));
}
public Map<DaybreakDocType, DaybreakDoc> getReportDocs(){
return Collections.unmodifiableMap(reportDocs);
}
/** --- Overrides --- */
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("reportDate", reportDate)
.toString();
}
/** --- Exceptions --- */
public static class DaybreakReportInsertException extends RuntimeException
{
private static final long serialVersionUID = -8868019141371691834L;
private LocalDate reportDate;
private DaybreakDocument document;
private InsertExceptionReason insertExceptionReason;
public enum InsertExceptionReason{
REPORT_CONTAINS_TYPE,
DATE_OUT_OF_RANGE
}
public DaybreakReportInsertException(String reason, InsertExceptionReason insertExceptionReason,
DaybreakDocument document, LocalDate reportDate){
super("Could not insert " + document + " into report-" + reportDate + ": " + reason);
this.insertExceptionReason = insertExceptionReason;
this.document = document;
this.reportDate = reportDate;
}
public InsertExceptionReason getInsertExceptionReason() {
return insertExceptionReason;
}
public LocalDate getReportDate() {
return reportDate;
}
public DaybreakDocument getDocument() {
return document;
}
}
/** --- Getters/Setters --- */
public LocalDate getReportDate() {
return reportDate;
}
}