package de.dhbw.humbuch.view;
import java.io.ByteArrayOutputStream;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import com.google.inject.Inject;
import com.vaadin.event.FieldEvents.TextChangeEvent;
import com.vaadin.event.FieldEvents.TextChangeListener;
import com.vaadin.navigator.View;
import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent;
import com.vaadin.server.StreamResource;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.TextField;
import com.vaadin.ui.VerticalLayout;
import de.davherrmann.mvvm.BasicState;
import de.davherrmann.mvvm.State;
import de.davherrmann.mvvm.StateChangeListener;
import de.davherrmann.mvvm.ViewModelComposer;
import de.davherrmann.mvvm.annotations.BindState;
import de.dhbw.humbuch.model.entity.BorrowedMaterial;
import de.dhbw.humbuch.model.entity.Grade;
import de.dhbw.humbuch.model.entity.Student;
import de.dhbw.humbuch.util.PDFHandler;
import de.dhbw.humbuch.util.PDFInformationProcessor;
import de.dhbw.humbuch.util.PDFStudentList;
import de.dhbw.humbuch.view.components.ConfirmDialog;
import de.dhbw.humbuch.view.components.PrintingComponent;
import de.dhbw.humbuch.view.components.StudentMaterialSelector;
import de.dhbw.humbuch.view.components.StudentMaterialSelectorObserver;
import de.dhbw.humbuch.viewmodel.ReturnViewModel;
import de.dhbw.humbuch.viewmodel.ReturnViewModel.ReturnListStudent;
import de.dhbw.humbuch.viewmodel.StudentInformationViewModel;
import de.dhbw.humbuch.viewmodel.StudentInformationViewModel.Students;
/**
* This view displays the Returnscreen. It holds a horizontal headerbar
* containing actions and a StudentMaterialSelector with all information about
* the lent books of students. It is used to return the books and create student
* lists.
*
* @author Henning Muszynski
* */
public class ReturnView extends VerticalLayout implements View,
ViewInformation, StudentMaterialSelectorObserver {
private static final long serialVersionUID = -525078997965992622L;
private static final String TITLE = "Rückgabe";
private static final String SAVE_SELECTED_RETURNING = "Material zurückgegeben";
private static final String MANUAL_RETURN = "Manuelle Rückgabe";
private static final String MANUAL_RETURN_TITLE = "Manuelle Rückgabe";
private static final String STUDENT_LIST = "Schülerliste drucken";
private static final String STUDENT_LIST_PDF = "SchuelerRueckgabeListe.pdf";
private static final String STUDENT_LIST_WINDOW_TITLE = "Schüler Rückgabe Liste";
private static final String FILTER_STUDENT = "Schüler filtern";
private static final String MSG_CONFIRM_RETURN = "Sind alle Listen für die ausgewählten Lehrmaterialien unterschrieben vorhanden?";
private HorizontalLayout horizontalLayoutHeaderBar;
private HorizontalLayout horizontalLayoutActions;
private StudentMaterialSelector studentMaterialSelector;
private TextField textFieldStudentFilter;
private Button buttonManualReturn;
private Button buttonSaveSelectedData;
private Button buttonStudentList;
private ReturnViewModel returnViewModel;
private StudentInformationViewModel studentInformationViewModel;
private ConfirmDialog.Listener confirmListener;
@BindState(ReturnListStudent.class)
private State<Map<Grade, Map<Student, List<BorrowedMaterial>>>> gradeAndStudentsWithMaterials = new BasicState<>(
Map.class);
@BindState(Students.class)
public State<Collection<Student>> students = new BasicState<>(
Collection.class);
/**
* Default constructor gets injected. It initializes all views and builds
* the layout. It connects the viewmodel automatically. All parameter get
* injected.
*
* @param viewModelComposer
* the viewmodel composer
* @param returnViewModel
* the return viewmodel
* @param studentInformationViewModel
* the student information viewmodel
* */
@Inject
public ReturnView(ViewModelComposer viewModelComposer,
ReturnViewModel returnViewModel,
StudentInformationViewModel studentInformationViewModel) {
this.returnViewModel = returnViewModel;
this.studentInformationViewModel = studentInformationViewModel;
init();
buildLayout();
bindViewModel(viewModelComposer, returnViewModel,
studentInformationViewModel);
}
/*
* The init method is responsible for initializing all member variables and
* view components. It configures the components and finally builds the
* layout.
*/
private void init() {
horizontalLayoutHeaderBar = new HorizontalLayout();
horizontalLayoutActions = new HorizontalLayout();
studentMaterialSelector = new StudentMaterialSelector();
buttonSaveSelectedData = new Button(SAVE_SELECTED_RETURNING);
buttonStudentList = new Button(STUDENT_LIST);
buttonManualReturn = new Button(MANUAL_RETURN);
textFieldStudentFilter = new TextField();
buttonSaveSelectedData.setEnabled(false);
buttonStudentList.setEnabled(false);
textFieldStudentFilter.setInputPrompt(FILTER_STUDENT);
textFieldStudentFilter.setWidth("50%");
textFieldStudentFilter.setImmediate(true);
studentMaterialSelector.registerAsObserver(this);
studentMaterialSelector.setSizeFull();
addListeners();
}
/*
* Builds the layout.
*/
private void buildLayout() {
horizontalLayoutHeaderBar.setWidth("100%");
horizontalLayoutHeaderBar.setSpacing(true);
horizontalLayoutActions.setSpacing(true);
setSizeFull();
setSpacing(true);
setMargin(true);
horizontalLayoutActions.addComponent(buttonSaveSelectedData);
horizontalLayoutActions.addComponent(buttonManualReturn);
horizontalLayoutActions.addComponent(buttonStudentList);
horizontalLayoutHeaderBar.addComponent(textFieldStudentFilter);
horizontalLayoutHeaderBar.addComponent(horizontalLayoutActions);
horizontalLayoutHeaderBar.setComponentAlignment(
horizontalLayoutActions, Alignment.MIDDLE_RIGHT);
horizontalLayoutHeaderBar.setComponentAlignment(textFieldStudentFilter,
Alignment.MIDDLE_LEFT);
horizontalLayoutHeaderBar.setExpandRatio(textFieldStudentFilter, 1);
addComponent(horizontalLayoutHeaderBar);
addComponent(studentMaterialSelector);
setExpandRatio(studentMaterialSelector, 1);
}
/*
* General listener method. It adds a listener to the confirm dialog as well
* as to the state for the students and grade and calls a sub method which
* add listeners as well.
*/
private void addListeners() {
confirmListener = new ConfirmDialog.Listener() {
private static final long serialVersionUID = -2819494096932449586L;
@Override
public void onClose(ConfirmDialog dialog) {
if (dialog.isConfirmed()) {
HashSet<BorrowedMaterial> materials = studentMaterialSelector
.getCurrentlySelectedBorrowedMaterials();
returnTeachingMaterials(materials);
}
}
};
gradeAndStudentsWithMaterials
.addStateChangeListener(new StateChangeListener() {
@Override
public void stateChange(Object value) {
if (value == null) {
return;
}
updateReturnList();
}
});
addButtonListeners();
}
/*
* Adds listeners to the buttons. ClickListener are added to the save,
* manual return and student list printing button.
*/
private void addButtonListeners() {
buttonSaveSelectedData.addClickListener(new ClickListener() {
private static final long serialVersionUID = -9208324317096088956L;
@Override
public void buttonClick(ClickEvent event) {
ConfirmDialog.show(MSG_CONFIRM_RETURN, confirmListener);
}
});
buttonStudentList.addClickListener(new ClickListener() {
private static final long serialVersionUID = -7743939402341845477L;
@Override
public void buttonClick(ClickEvent event) {
doStudentListPrinting();
}
});
buttonManualReturn.addClickListener(new ClickListener() {
private static final long serialVersionUID = 6196708024508507923L;
@Override
public void buttonClick(ClickEvent event) {
HashSet<Student> selectedStudents = (HashSet<Student>) studentMaterialSelector
.getCurrentlySelectedStudents();
studentInformationViewModel.refresh();
if (selectedStudents.size() == 0) {
SelectStudentPopupWindow sspw = new SelectStudentPopupWindow(
MANUAL_RETURN_TITLE, ReturnView.this, students
.get());
getUI().addWindow(sspw);
} else if (selectedStudents.size() == 1) {
// This loop runs only once
for (Student student : selectedStudents) {
ManualProcessPopupWindow mlpw = new ManualProcessPopupWindow(
ReturnView.this, student);
getUI().addWindow(mlpw);
}
}
}
});
textFieldStudentFilter.addTextChangeListener(new TextChangeListener() {
private static final long serialVersionUID = -8656489769177447342L;
@Override
public void textChange(TextChangeEvent event) {
studentMaterialSelector.setFilterString(event.getText());
}
});
}
/*
* This method triggers the pdf creation of a student list. The pdf is
* created for the selected students in the StudentMaterialSelector. It is
* possible to create multiple pdfs (meaning the pdf having multiple pages)
* when multiple students or classes are selected.
*/
private void doStudentListPrinting() {
LinkedHashMap<Student, List<BorrowedMaterial>> informationForPdf = getPdfInformationFromStundentMaterialSelector();
if (informationForPdf != null) {
Set<PDFStudentList.Builder> builders = new LinkedHashSet<PDFStudentList.Builder>();
for (Student student : informationForPdf.keySet()) {
PDFStudentList.Builder builder = new PDFStudentList.Builder()
.returnList(informationForPdf.get(student));
builders.add(builder);
}
ByteArrayOutputStream baos = new PDFStudentList(builders)
.createByteArrayOutputStreamForPDF();
if (baos != null) {
String fileNameIncludingHash = "" + new Date().hashCode() + "_"
+ STUDENT_LIST_PDF;
StreamResource sr = new StreamResource(
new PDFHandler.PDFStreamSource(baos),
fileNameIncludingHash);
new PrintingComponent(sr, STUDENT_LIST_WINDOW_TITLE);
}
}
}
/*
* Collects all information needed for the pdf generation from the
* StudentMaterialSelector. The information is collected and processed. It
* gets sorted and applied to the needed data structure.
*
* @return all information needed for the pdf generation from the
* StudentMaterialSelector
*/
private LinkedHashMap<Student, List<BorrowedMaterial>> getPdfInformationFromStundentMaterialSelector() {
HashSet<BorrowedMaterial> allSelectedMaterials = studentMaterialSelector
.getCurrentlySelectedBorrowedMaterials();
HashSet<Student> allSelectedStudents = studentMaterialSelector
.getCurrentlySelectedStudents();
return PDFInformationProcessor.linkStudentsAndMaterials(
allSelectedMaterials, allSelectedStudents);
}
/**
* All given materials get returned. They can belong to different students
* in different classes.
*
* @param materials
* the materials which shall be returned
* */
public void returnTeachingMaterials(Set<BorrowedMaterial> materials) {
returnViewModel.setBorrowedMaterialsReturned(materials);
}
/*
* This method is called from the state change listener and is responsible
* for updating the StudentMaterialSelector accordingly.
*/
private void updateReturnList() {
returnViewModel.refreshStudents();
studentMaterialSelector
.setGradesAndStudentsWithMaterials(gradeAndStudentsWithMaterials
.get());
}
/*
* Binds the view model.
*/
private void bindViewModel(ViewModelComposer viewModelComposer,
Object... viewModels) {
try {
viewModelComposer.bind(this, viewModels);
} catch (IllegalAccessException | NoSuchElementException
| UnsupportedOperationException e) {
e.printStackTrace();
}
}
/**
* This method is alway called when the view is entered (navigated to). It
* refreshes the viewmodel and thus updates the StudentMaterialSelector and
* other view components.
*
* @param event
* the event is not used.
* */
@Override
public void enter(ViewChangeEvent event) {
studentInformationViewModel.refresh();
returnViewModel.refresh();
}
/**
* Returns the title of this view.
*
* @return the title of this view
* */
@Override
public String getTitle() {
return TITLE;
}
/**
* Update procedure from the view model in order to get new information
* without the need to manually refresh.
*/
@Override
public void update() {
HashSet<Student> students = studentMaterialSelector
.getCurrentlySelectedStudents();
HashSet<BorrowedMaterial> materials = studentMaterialSelector
.getCurrentlySelectedBorrowedMaterials();
// Adapt save button
if (materials.size() >= 1) {
buttonSaveSelectedData.setEnabled(true);
} else {
buttonSaveSelectedData.setEnabled(false);
}
// Adapt manual return button
if (students.size() <= 1) {
buttonManualReturn.setEnabled(true);
} else {
buttonManualReturn.setEnabled(false);
}
// Adapt student list button
if (students.size() >= 1) {
buttonStudentList.setEnabled(true);
} else {
buttonStudentList.setEnabled(false);
}
}
}