package de.dhbw.humbuch.viewmodel;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import com.google.inject.Inject;
import de.davherrmann.mvvm.ActionHandler;
import de.davherrmann.mvvm.BasicState;
import de.davherrmann.mvvm.State;
import de.davherrmann.mvvm.annotations.AfterVMBinding;
import de.davherrmann.mvvm.annotations.HandlesAction;
import de.davherrmann.mvvm.annotations.ProvidesState;
import de.dhbw.humbuch.model.DAO;
import de.dhbw.humbuch.model.entity.BorrowedMaterial;
import de.dhbw.humbuch.model.entity.Grade;
import de.dhbw.humbuch.model.entity.SchoolYear;
import de.dhbw.humbuch.model.entity.SchoolYear.Term;
import de.dhbw.humbuch.model.entity.Student;
import de.dhbw.humbuch.model.entity.TeachingMaterial;
/**
* Provides {@link State}s and methods for returning {@link BorrowedMaterial}s
*
* @author David Vitt
*
*/
public class ReturnViewModel {
public interface GenerateStudentReturnList extends ActionHandler {}
public interface SetBorrowedMaterialsReturned extends ActionHandler {}
public interface RefreshStudents extends ActionHandler {}
public interface ReturnListStudent extends State<Map<Grade, Map<Student, List<BorrowedMaterial>>>> {}
@ProvidesState(ReturnListStudent.class)
public State<Map<Grade, Map<Student, List<BorrowedMaterial>>>> returnListStudent = new BasicState<>(Map.class);
private DAO<Grade> daoGrade;
private DAO<BorrowedMaterial> daoBorrowedMaterial;
private DAO<SchoolYear> daoSchoolYear;
private DAO<Student> daoStudents;
private SchoolYear recentlyActiveSchoolYear;
/**
* Constructor
*
* @param daoGrade
* @param daoBorrowedMaterial
* @param daoSchoolYear
* @param daoStudents
*/
@Inject
public ReturnViewModel(DAO<Grade> daoGrade, DAO<BorrowedMaterial> daoBorrowedMaterial, DAO<SchoolYear> daoSchoolYear, DAO<Student> daoStudents) {
this.daoGrade = daoGrade;
this.daoBorrowedMaterial = daoBorrowedMaterial;
this.daoSchoolYear = daoSchoolYear;
this.daoStudents = daoStudents;
}
@AfterVMBinding
public void initialiseStates() {
returnListStudent.set(new HashMap<Grade, Map<Student, List<BorrowedMaterial>>>());
}
public void refresh() {
updateSchoolYear();
generateStudentReturnList();
}
/**
* Generates "list" of all {@link BorrowedMaterial}s that have to returned by a {@link Student}.<br>
* the "list" is returned as {@code Map<Grade, Map<Student, List<BorrowedMaterial>>>} in the state {@link ReturnListStudent}
*
*/
@HandlesAction(GenerateStudentReturnList.class)
public void generateStudentReturnList() {
Date today = new Date();
Term recentlyActiveTerm = recentlyActiveSchoolYear.getRecentlyActiveTerm();
boolean isAfterCurrentTerm = recentlyActiveSchoolYear.getEndOf(recentlyActiveTerm).before(today);
Map<Grade, Map<Student, List<BorrowedMaterial>>> toReturn = new TreeMap<>();
for(Grade gradeEntity : daoGrade.findAll()) {
Map<Student, List<BorrowedMaterial>> studentWithUnreturnedBorrowedMaterials = new TreeMap<>();
for(Student student : gradeEntity.getStudents()) {
int studentsGrade = student.getGrade().getGrade();
List<BorrowedMaterial> unreturnedBorrowedMaterials = new ArrayList<>();
for(BorrowedMaterial borrowedMaterial : student.getReceivedBorrowedMaterials()) {
TeachingMaterial teachingMaterial = borrowedMaterial.getTeachingMaterial();
Date borrowUntilDate = borrowedMaterial.getBorrowUntil();
boolean notNeededNextTerm = borrowedMaterial.getReturnDate() == null && !isNeededNextTerm(borrowedMaterial);
boolean borrowUntilExceeded = borrowUntilDate == null ? false : borrowUntilDate.before(today);
boolean isManualLended = borrowUntilDate == null ? false : true;
boolean toTermEqualsRecentlyActiceTerm = teachingMaterial.getToGrade() == studentsGrade
&& teachingMaterial.getToTerm() == recentlyActiveTerm;
if(!isManualLended && notNeededNextTerm && (toTermEqualsRecentlyActiceTerm ? isAfterCurrentTerm : true)) {
unreturnedBorrowedMaterials.add(borrowedMaterial);
} else if (!borrowedMaterial.isReturned() && borrowUntilExceeded) {
unreturnedBorrowedMaterials.add(borrowedMaterial);
}
}
if(!unreturnedBorrowedMaterials.isEmpty()) {
Collections.sort(unreturnedBorrowedMaterials);
studentWithUnreturnedBorrowedMaterials.put(student, unreturnedBorrowedMaterials);
}
}
if(!studentWithUnreturnedBorrowedMaterials.isEmpty()) {
toReturn.put(gradeEntity, studentWithUnreturnedBorrowedMaterials);
}
}
returnListStudent.set(toReturn);
}
/**
* Marks the given {@link BorrowedMaterial}s as {@code returned}
*
* @param borrowedMaterials that should be marked as {@code returned}
*/
@HandlesAction(SetBorrowedMaterialsReturned.class)
public void setBorrowedMaterialsReturned(Collection<BorrowedMaterial> borrowedMaterials) {
Date today = new Date();
for (BorrowedMaterial borrowedMaterial : borrowedMaterials) {
borrowedMaterial.setReturnDate(today);
}
daoBorrowedMaterial.update(borrowedMaterials);
generateStudentReturnList();
}
@HandlesAction(RefreshStudents.class)
public void refreshStudents() {
daoStudents.findAll();
}
/**
* Checks if the given {@link BorrowedMaterial} is needed in the next {@link Term}.
*
* @param borrowedMaterial
* @return <code>true</code> if needed, <code>false</code> otherwise
*/
private boolean isNeededNextTerm(BorrowedMaterial borrowedMaterial) {
TeachingMaterial teachingMaterial = borrowedMaterial.getTeachingMaterial();
Integer toGrade = teachingMaterial.getToGrade();
int currentGrade = borrowedMaterial.getStudent().getGrade().getGrade();
Term toTerm = teachingMaterial.getToTerm();
Term currentTerm = recentlyActiveSchoolYear.getRecentlyActiveTerm();
if(toGrade == null)
return false;
return (toGrade > currentGrade || (toGrade == currentGrade && (toTerm.compareTo(currentTerm) > 0)));
}
/**
* Updates the recently actice {@link SchoolYear}
*/
private void updateSchoolYear() {
recentlyActiveSchoolYear = daoSchoolYear.findSingleWithCriteria(
Order.desc("toDate"),
Restrictions.le("fromDate", new Date()));
if(recentlyActiveSchoolYear == null) {
recentlyActiveSchoolYear = new SchoolYear.Builder(
"now", getDate(Calendar.AUGUST, 1), getDate(Calendar.JUNE, 31))
.endFirstTerm(getDate(Calendar.JANUARY, 31))
.beginSecondTerm(getDate(Calendar.FEBRUARY, 1))
.build();
}
}
/**
* Returns {@link Date} object with the current year and the given month and day.
*
* @param month
* @param day
* @return {@link Date} object with given information
*/
private Date getDate(int month, int day) {
Calendar calendar = Calendar.getInstance();
calendar.set(calendar.get(Calendar.YEAR), month, day);
return calendar.getTime();
}
}