package de.dhbw.humbuch.view;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.vaadin.data.Container.Filter;
import com.vaadin.data.Property.ValueChangeEvent;
import com.vaadin.data.Property.ValueChangeListener;
import com.vaadin.data.util.IndexedContainer;
import com.vaadin.data.util.filter.SimpleStringFilter;
import com.vaadin.event.FieldEvents.TextChangeEvent;
import com.vaadin.event.FieldEvents.TextChangeListener;
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.Label;
import com.vaadin.ui.Notification;
import com.vaadin.ui.Notification.Type;
import com.vaadin.ui.PopupDateField;
import com.vaadin.ui.Table;
import com.vaadin.ui.TextField;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;
import de.dhbw.humbuch.model.entity.BorrowedMaterial;
import de.dhbw.humbuch.model.entity.Student;
import de.dhbw.humbuch.model.entity.TeachingMaterial;
import elemental.events.KeyboardEvent.KeyCode;
/**
* Class representing the popup window of a manual lending or return process.
* The constructor defines which process is displayed.
*
* @author Henning Muszynski
* */
public class ManualProcessPopupWindow extends Window {
private static final long serialVersionUID = -6517435259424504689L;
private static final Logger LOG = LoggerFactory
.getLogger(ManualProcessPopupWindow.class);
private static final String SEARCH_MATERIALS = "Materialien durchsuchen";
private static final String SAVE_LEND = "Ausleihen";
private static final String SAVE_RETURN = "Zurückgeben";
private static final String TEACHING_MATERIAL_HEADER = "Lehrmittel";
private static final String BORROW_UNTIL_HEADER = "Ausleihen bis zum";
private static final String MULTI_CHOICE_EXPLANATION = "Mehrfachauswahl mittels Strg. oder Shift möglich";
private static final String NOTIFICATION_CAPTION_INVALID_DATE = "Ungültiges Datum";
private static final String NOTIFICATION_DESCR_INVALID_DATE = "Bitte geben Sie ein gültiges Datum ein.";
private static final String NOTIFICATION_CAPTION_DATE_IN_PAST = "Datum liegt in der Vergangenheit";
private static final String NOTIFICATION_DESCR_DATE_IN_PAST = "Bitte geben Sie ein gültiges Datum in der Zukunft ein.";
private VerticalLayout verticalLayoutContent;
private HorizontalLayout horizontalLayoutHeaderBar;
private TextField textFieldSearchBar;
private Table tableTeachingMaterials;
private Button buttonSave;
private Label labelMultiChoiceExplanation;
private IndexedContainer containerTableTeachingMaterials;
private ArrayList<TeachingMaterial> teachingMaterials;
private HashMap<Object, HashMap<TeachingMaterial, PopupDateField>> idForMaterialsWithDates;
private HashMap<Object, BorrowedMaterial> idForMaterials;
private LendingView lendingView;
private ReturnView returnView;
private Student selectedStudent;
/**
* Constructor taking a LendingView as parameter. When using this
* constructor a click on the save button triggers the manual lending
* process.
*
* @param lendingView
* lending view used for triggering the manual process
* @param selectedStudent
* student for whom the process is triggered
* */
public ManualProcessPopupWindow(LendingView lendingView,
Student selectedStudent) {
super("Manuelle Ausleihe für " + selectedStudent.getFirstname() + " "
+ selectedStudent.getLastname() + " ("
+ selectedStudent.getGrade() + ")");
this.lendingView = lendingView;
this.selectedStudent = selectedStudent;
init();
buildLayout();
}
/**
* Constructor taking a ReturnView as parameter. When using this constructor
* a click on the save button triggers the manual return process.
*
* @param lendingView
* lending view used for triggering the manual process
* @param selectedStudent
* student for whom the process is triggered
* */
public ManualProcessPopupWindow(ReturnView returnView,
Student selectedStudent) {
super("Manuelle Rückgabe für " + selectedStudent.getFirstname() + " "
+ selectedStudent.getLastname() + " ("
+ selectedStudent.getGrade() + ")");
this.returnView = returnView;
this.selectedStudent = selectedStudent;
init();
buildLayout();
}
/*
* Initializes all member variables and configures them.
*/
private void init() {
verticalLayoutContent = new VerticalLayout();
horizontalLayoutHeaderBar = new HorizontalLayout();
textFieldSearchBar = new TextField(SEARCH_MATERIALS);
tableTeachingMaterials = new Table();
idForMaterialsWithDates = new HashMap<Object, HashMap<TeachingMaterial, PopupDateField>>();
idForMaterials = new HashMap<Object, BorrowedMaterial>();
containerTableTeachingMaterials = new IndexedContainer();
labelMultiChoiceExplanation = new Label(MULTI_CHOICE_EXPLANATION);
labelMultiChoiceExplanation.addStyleName("caption");
textFieldSearchBar.focus();
adaptButton();
containerTableTeachingMaterials.addContainerProperty(
TEACHING_MATERIAL_HEADER, String.class, null);
if (lendingView != null) {
containerTableTeachingMaterials.addContainerProperty(
BORROW_UNTIL_HEADER, PopupDateField.class, null);
}
tableTeachingMaterials
.setContainerDataSource(containerTableTeachingMaterials);
tableTeachingMaterials.setWidth("100%");
tableTeachingMaterials.setSelectable(true);
tableTeachingMaterials.setMultiSelect(true);
tableTeachingMaterials.setImmediate(true);
setTableListener();
updateTableContent();
horizontalLayoutHeaderBar.setSpacing(true);
verticalLayoutContent.setSpacing(true);
verticalLayoutContent.setMargin(true);
setWidth("50%");
center();
setCloseShortcut(KeyCode.ESC, null);
setImmediate(true);
setModal(true);
setResizable(false);
addListeners();
}
private void buildLayout() {
horizontalLayoutHeaderBar.addComponent(textFieldSearchBar);
horizontalLayoutHeaderBar.addComponent(buttonSave);
horizontalLayoutHeaderBar.setComponentAlignment(buttonSave,
Alignment.BOTTOM_CENTER);
verticalLayoutContent.addComponent(horizontalLayoutHeaderBar);
verticalLayoutContent.addComponent(tableTeachingMaterials);
verticalLayoutContent.addComponent(labelMultiChoiceExplanation);
setContent(verticalLayoutContent);
}
/*
* Adapts the button label to the given process (return / lend)
*/
private void adaptButton() {
if (lendingView != null) {
buttonSave = new Button(SAVE_LEND);
} else {
buttonSave = new Button(SAVE_RETURN);
}
buttonSave.setEnabled(false);
buttonSave.addStyleName("default");
buttonSave.setClickShortcut(KeyCode.ENTER, null);
}
/*
* Updates the content of the table depending on the process.
*/
private void updateTableContent() {
if (lendingView != null) {
updateTableManualLending();
} else if (returnView != null) {
updateTableManualReturn();
}
}
/*
* Updates the table for manual lending. Adding all teaching materials
* provided from the LendingView and Datefields. When adding the date fields
* a value change listener for validation is added. The default date in the
* table is one month in the future.
*/
private void updateTableManualLending() {
teachingMaterials = lendingView.getTeachingMaterials();
if (teachingMaterials == null) {
return;
}
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MONTH, 1);
final Date comingMonth = calendar.getTime();
ValueChangeListener dateValidator = new ValueChangeListener() {
private static final long serialVersionUID = 5165858023604029960L;
@SuppressWarnings("unchecked")
@Override
public void valueChange(ValueChangeEvent event) {
boolean isValid = validateDate((Date) event.getProperty()
.getValue());
if (!isValid) {
event.getProperty().setValue(comingMonth);
}
}
};
for (TeachingMaterial teachingMaterial : teachingMaterials) {
PopupDateField dateField = new PopupDateField();
dateField.setValue(comingMonth);
dateField.addValueChangeListener(dateValidator);
dateField.setImmediate(true);
HashMap<TeachingMaterial, PopupDateField> materialWithDate = new HashMap<TeachingMaterial, PopupDateField>();
materialWithDate.put(teachingMaterial, dateField);
Object itemId = tableTeachingMaterials.addItem(new Object[] {
teachingMaterial.getName(), dateField }, null);
idForMaterialsWithDates.put(itemId, materialWithDate);
}
}
/*
* Updates the table for manual return. It adds just the borrowed materials
* for the student in the table.
*/
private void updateTableManualReturn() {
List<BorrowedMaterial> materials = selectedStudent
.getUnreturnedBorrowedMaterials();
for (BorrowedMaterial material : materials) {
Object itemId = tableTeachingMaterials.addItem(
new Object[] { material.getTeachingMaterial().getName() },
null);
idForMaterials.put(itemId, material);
}
}
/*
* When an item in the table gets selected the save button is enabled. When
* no item is selected the save button is disabled.
*/
private void setTableListener() {
tableTeachingMaterials
.addValueChangeListener(new ValueChangeListener() {
private static final long serialVersionUID = -8774191239600142741L;
@Override
public void valueChange(ValueChangeEvent event) {
Object selectedIds = tableTeachingMaterials.getValue();
if (selectedIds instanceof Set<?>) {
Set<?> ids = (Set<?>) selectedIds;
if (ids.size() == 0) {
buttonSave.setEnabled(false);
return;
}
buttonSave.setEnabled(true);
} else {
LOG.warn("Table selection is not an instance of Set<?>");
}
}
});
}
/*
* Adds a listener to the save button and one responsible for filtering the
* table.
*/
private void addListeners() {
/*
* Decides whether the manual lending or the manual return is saved.
*/
buttonSave.addClickListener(new ClickListener() {
private static final long serialVersionUID = 4375804067002022079L;
@Override
public void buttonClick(ClickEvent event) {
if (lendingView != null) {
finishManualLendingProcess();
} else if (returnView != null) {
finishManualReturnProcess();
} else {
LOG.warn("Could not determine which process should be finished. Both views are null.");
}
closeMe();
}
});
/*
* Puts a string filter on the table content. The filter is
* case-insensitive and matches anywhere in the teaching material (not
* only prefix matching).
*/
textFieldSearchBar.addTextChangeListener(new TextChangeListener() {
private static final long serialVersionUID = -6281243106168356850L;
@Override
public void textChange(TextChangeEvent event) {
Filter filter = new SimpleStringFilter(
TEACHING_MATERIAL_HEADER, event.getText(), true, false);
containerTableTeachingMaterials.removeAllContainerFilters();
containerTableTeachingMaterials.addContainerFilter(filter);
tableTeachingMaterials.setValue(null);
}
});
}
/*
* Save all selected materials for the student object using
* LendingView.saveTeachingMaterialsForStudents. Afterwards the window is
* closed.
*/
@SuppressWarnings("unchecked")
private void finishManualLendingProcess() {
HashMap<TeachingMaterial, Date> teachingMaterialsWithDates = new HashMap<TeachingMaterial, Date>();
Object selectedIds = tableTeachingMaterials.getValue();
if (selectedIds instanceof Set<?>) {
Set<Object> ids = (Set<Object>) selectedIds;
if (ids.size() == 0) {
return;
}
for (Object selectedId : ids) {
HashMap<TeachingMaterial, PopupDateField> tableRow = idForMaterialsWithDates
.get(selectedId);
// this loop runs only once
for (TeachingMaterial material : tableRow.keySet()) {
PopupDateField dateField = tableRow.get(material);
teachingMaterialsWithDates.put(material,
dateField.getValue());
}
}
} else {
LOG.warn("Table selection is not an instance of Set<?>");
}
HashMap<Student, HashMap<TeachingMaterial, Date>> saveStructure = new HashMap<Student, HashMap<TeachingMaterial, Date>>();
saveStructure.put(selectedStudent, teachingMaterialsWithDates);
lendingView.saveTeachingMaterialsForStudents(saveStructure);
closeMe();
}
/*
* Return all selected materials for the student object using
* ReturnView.returnTeachingMaterialsForStudents. Afterwards the window is
* closed.
*/
@SuppressWarnings("unchecked")
private void finishManualReturnProcess() {
Set<BorrowedMaterial> selectedMaterials = new HashSet<BorrowedMaterial>();
Object selectedIds = tableTeachingMaterials.getValue();
if (selectedIds instanceof Set<?>) {
Set<Object> ids = (Set<Object>) selectedIds;
if (ids.size() == 0) {
return;
}
for (Object selectedId : ids) {
selectedMaterials.add(idForMaterials.get(selectedId));
}
} else {
LOG.warn("Table selection is not an instance of Set<?>");
}
returnView.returnTeachingMaterials(selectedMaterials);
}
/*
* Method for validating a date object.
*
* @return returns false when passing null or a date in the past returns
* true otherwise
*/
private boolean validateDate(Date date) {
if (date == null) {
Notification.show(NOTIFICATION_CAPTION_INVALID_DATE,
NOTIFICATION_DESCR_INVALID_DATE, Type.WARNING_MESSAGE);
return false;
}
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
if (isToday(calendar)) {
return true;
} else if (date.before(new Date())) {
Notification.show(NOTIFICATION_CAPTION_DATE_IN_PAST,
NOTIFICATION_DESCR_DATE_IN_PAST, Type.WARNING_MESSAGE);
return false;
} else {
return true;
}
}
/*
* Checks whether a given calendar object represents today or not.
*
* @return true when then is today
*/
private boolean isToday(Calendar then) {
Calendar when = Calendar.getInstance();
when.setTime(new Date());
if (when.get(Calendar.DAY_OF_YEAR) == then.get(Calendar.DAY_OF_YEAR)) {
return true;
}
return false;
}
/*
* Closes this window. Removing it from the UI and calling the Window.close
* method
*/
private void closeMe() {
UI.getCurrent().removeWindow(this);
close();
}
}