@Entity
@Table(name="borrowedMaterial")
public class BorrowedMaterial implements de.dhbw.humbuch.model.entity.Entity, Serializable, Comparable<BorrowedMaterial> {
	private static final long serialVersionUID = -7956138735111492455L;

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private int id;
	
	@ManyToOne(fetch=FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
	@JoinColumn(name="studentId", referencedColumnName="id")
	private Student student;
	
	@ManyToOne(fetch=FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
	@JoinColumn(name="teachingMaterialId", referencedColumnName="id")
	private TeachingMaterial teachingMaterial;
	
	private Date borrowFrom;
	private Date borrowUntil;
	private Date returnDate;
	private boolean received;
	
	/**
	 * Required by Hibernate.<p>
	 * Use the {@link Builder} instead.
	 * 
	 * @see Builder
	 */
	@Deprecated
	public BorrowedMaterial() {}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public Student getStudent() {
		return student;
	}

	public void setStudent(Student student) {
		this.student = student;
	}

	public TeachingMaterial getTeachingMaterial() {
		return teachingMaterial;
	}

	public void setTeachingMaterial(TeachingMaterial teachingMaterial) {
		this.teachingMaterial = teachingMaterial;
	}

	public Date getBorrowFrom() {
		return borrowFrom;
	}

	public void setBorrowFrom(Date borrowFrom) {
		this.borrowFrom = borrowFrom;
	}

	public Date getBorrowUntil() {
		return borrowUntil;
	}

	public void setBorrowUntil(Date borrowUntil) {
		this.borrowUntil = borrowUntil;
	}

	public Date getReturnDate() {
		return returnDate;
	}

	public void setReturnDate(Date returnDate) {
		this.returnDate = returnDate;
	}

	public boolean isReceived() {
		return received;
	}
	
	public boolean isReturned() {
		return getReturnDate() != null ? true : false;
	}
	
	public void setReceived(boolean received) {
		this.received = received;
	}

	public static class Builder {
		private final Student student;
		private final TeachingMaterial teachingMaterial;
		private final Date borrowFrom;
		
		private Date borrowUntil;
		private Date returnDate;
		private boolean received;
		
		public Builder(Student student, TeachingMaterial teachingMaterial, Date borrowFrom) {
			this.student = student;
			this.teachingMaterial = teachingMaterial;
			this.borrowFrom = borrowFrom;
		}
		
		public Builder borrowUntil(Date borrowUntil) {
			this.borrowUntil = borrowUntil;
			return this;
		}
		
		public Builder returnDate(Date returnDate) {
			this.returnDate = returnDate;
			return this;
		}
		
		public Builder received(boolean received) {
			this.received = received;
			return this;
		}
		
		public BorrowedMaterial build() {
			return new BorrowedMaterial(this);
		}
	}
	
	private BorrowedMaterial(Builder builder) {
		this.student = builder.student;
		this.teachingMaterial = builder.teachingMaterial;
		this.borrowFrom = builder.borrowFrom;
		
		this.borrowUntil = builder.borrowUntil;
		this.returnDate = builder.returnDate;
		this.received = builder.received;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + id;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof BorrowedMaterial))
			return false;
		BorrowedMaterial other = (BorrowedMaterial) obj;
		if (getId() != other.getId())
			return false;
		return true;
	}

	@Override
	public int compareTo(BorrowedMaterial o) {
		int compareResult = getTeachingMaterial().compareTo(o.getTeachingMaterial());
		if(compareResult != 0) {
			return compareResult;
		}
		
		return Integer.compare(hashCode(), o.hashCode());
	}
	
} @Entity
@Table(name="grade")
public class Grade implements de.dhbw.humbuch.model.entity.Entity, Serializable, Comparable<Grade> {
	private static final long serialVersionUID = 227472334995076701L;

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private int id;
	
	private int grade;
	
	@Column(name="suffix")
	private String suffix;
	
	private String teacher;
	
	@OneToMany(mappedBy="grade")
	private List<Student> students = new ArrayList<>();

	/**
	 * Required by Hibernate.<p>
	 * Use the {@link Builder} instead.
	 * 
	 * @see Builder
	 */
	@Deprecated
	public Grade() {}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public int getGrade() {
		return grade;
	}

	public void setGrade(int grade) {
		this.grade = grade;
	}

	public String getSuffix() {
		return suffix;
	}

	public void setSuffix(String suffix) {
		this.suffix = suffix;
	}

	public List<Student> getStudents() {
		return students;
	}

	public void setStudents(List<Student> students) {
		this.students = students;
	}

	public String getTeacher() {
		return teacher;
	}

	public void setTeacher(String teacher) {
		this.teacher = teacher;
	}
	
	public static class Builder {
		private final int grade;
		private final String suffix;
		
		private String teacher;
		
		public Builder(String gradeString){
			String[] splittedString = splitBetweenCharsAndDigits(gradeString);
			this.grade = Integer.parseInt(splittedString[0]);
			this.suffix = splittedString[1];
		}
		
		public Builder(int grade, String suffix) {
			this.grade = grade;
			this.suffix = suffix;
		}
		
		public Builder teacher(String teacher) {
			this.teacher = teacher;
			return this;
		}
		
		public Grade build() {
			return new Grade(this);
		}
		
		private static String[] splitBetweenCharsAndDigits(String str){	
			return str.split("(?<=\\D)(?=\\d)|(?<=\\d)(?=\\D)");
		}
	}
	
	private Grade(Builder builder) {
		grade = builder.grade;
		suffix = builder.suffix;
		
		teacher = builder.teacher;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + id;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof Grade))
			return false;
		Grade other = (Grade) obj;
		if (getId() != other.getId())
			return false;
		return true;
	}

	@Override
	public String toString() {
		return grade + suffix;
	}

	@Override
	public int compareTo(Grade o) {
		int compareResult = Integer.compare(getGrade(), o.getGrade());
		if(compareResult != 0) {
			return compareResult;
		}
		
		compareResult = getSuffix().compareTo(o.getSuffix());
		if(compareResult != 0) {
			return compareResult;
		}
		
		return Integer.compare(hashCode(), o.hashCode());
	}
	
} @Entity
@Table(name="student")
public class Student implements de.dhbw.humbuch.model.entity.Entity, Serializable, Comparable<Student> {
	private static final long serialVersionUID = -3020872456290703528L;

	@Id
	private int id;
	
	@ManyToOne(fetch=FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
	@JoinColumn(name="gradeId", referencedColumnName="id")
	private Grade grade;
	private String lastname;
	private String firstname;
	private Date birthday;
	private String gender;
	private boolean leavingSchool;
	
	@OneToMany(mappedBy="student", fetch=FetchType.LAZY)
	private List<BorrowedMaterial> borrowedMaterials = new ArrayList<BorrowedMaterial>();

	@ManyToMany(fetch = FetchType.LAZY)
	@JoinTable(
			name="borrowedMaterial",
			joinColumns={@JoinColumn(name="studentId", referencedColumnName="id")},
			inverseJoinColumns={@JoinColumn(name="teachingMaterialId", referencedColumnName="id")})
	private Collection<TeachingMaterial> teachingMaterials = new HashSet<>();
	
	@ElementCollection(targetClass=Subject.class)
	@Enumerated(EnumType.STRING)
	@CollectionTable(name="studentSubject", joinColumns = @JoinColumn(name="studentId"))
	@Column(name="subject")
	private Set<Subject> profile = EnumSet.noneOf(Subject.class);
	
	@ManyToOne(fetch=FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
	@JoinColumn(name="parentId", referencedColumnName="id")
	private Parent parent;
	
	@OneToMany(mappedBy="student", fetch=FetchType.LAZY)
	private List<Dunning> dunnings = new ArrayList<Dunning>();
	
	/**
	 * Required by Hibernate.<p>
	 * Use the {@link Builder} instead.
	 * 
	 * @see Builder
	 */
	@Deprecated
	public Student() {}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public Grade getGrade() {
		return grade;
	}

	public void setGrade(Grade grade) {
		this.grade = grade;
	}

	public String getLastname() {
		return lastname;
	}

	public void setLastname(String lastname) {
		this.lastname = lastname;
	}

	public String getFirstname() {
		return firstname;
	}

	public void setFirstname(String firstname) {
		this.firstname = firstname;
	}

	public Date getBirthday() {
		return birthday;
	}

	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}

	public String getGender() {
		return gender;
	}

	public void setGender(String gender) {
		this.gender = gender;
	}

	public Parent getParent() {
		return parent;
	}

	public void setParent(Parent parent) {
		this.parent = parent;
	}

	public List<BorrowedMaterial> getBorrowedMaterials() {
		return borrowedMaterials;
	}

	public void setBorrowedMaterials(List<BorrowedMaterial> borrowedMaterials) {
		this.borrowedMaterials = borrowedMaterials;
	}
	
	public Collection<TeachingMaterial> getTeachingMaterials() {
		return teachingMaterials;
	}

	public Set<Subject> getProfile() {
		return profile;
	}

	public void setProfile(Set<Subject> profile) {
		this.profile = profile;
	}
	
	public void addSubject(Subject subject) {
		getProfile().add(subject);
	}
	
	public boolean hasSubject(Subject subject) {
		return getProfile().contains(subject);
	}
	
	public boolean isLeavingSchool() {
		return leavingSchool;
	}

	public void setLeavingSchool(boolean leavingSchool) {
		this.leavingSchool = leavingSchool;
	}
	
	public List<Dunning> getDunnings() {
		return dunnings;
	}

	public List<BorrowedMaterial> getUnreceivedBorrowedList() {
		List<BorrowedMaterial> unreceivedBorrowedMaterials = new ArrayList<BorrowedMaterial>();
		for (BorrowedMaterial borrowedMaterial : getBorrowedMaterials()) {
			if(!borrowedMaterial.isReceived()) {
				unreceivedBorrowedMaterials.add(borrowedMaterial);
			}
		}
		
		return unreceivedBorrowedMaterials;
	}
	
	public List<BorrowedMaterial> getReceivedBorrowedMaterials() {
		List<BorrowedMaterial> receivedBorrowedMaterials = getBorrowedMaterials();
		receivedBorrowedMaterials.removeAll(getUnreceivedBorrowedList());
		
		return receivedBorrowedMaterials;
	}
	
	public List<BorrowedMaterial> getUnreturnedBorrowedMaterials() {
		List<BorrowedMaterial> unreturnedBorrowedMaterials = new ArrayList<BorrowedMaterial>();
		
		for(BorrowedMaterial borrowedMaterial : getReceivedBorrowedMaterials()) {
			if(borrowedMaterial.getReturnDate() == null) {
				unreturnedBorrowedMaterials.add(borrowedMaterial);
			}
		}
		
		return unreturnedBorrowedMaterials;
	}
	
	public boolean hasUnreceivedBorrowedMaterials() {
		for(BorrowedMaterial borrowedMaterial : getBorrowedMaterials()) {
			if(!borrowedMaterial.isReceived()) {
				return true;
			}
		}
		
		return false;
	}
	
	public void copyDataFrom(Student student) {
		setBirthday(student.getBirthday());
		setFirstname(student.getFirstname());
		setGender(student.getGender());
		setGrade(student.getGrade());
		setLastname(student.getLastname());
		setParent(student.getParent());
		setProfile(student.getProfile());
	}

	public static class Builder {
		private final int id;
		private final String firstname;
		private final String lastname;
		private final Date birthday;
		private final Grade grade;
		
		private String gender;
		private List<BorrowedMaterial> borrowedList = new ArrayList<BorrowedMaterial>();
		private Set<Subject> profile = EnumSet.noneOf(Subject.class);
		private Parent parent;
		private boolean leavingSchool;
		
		public Builder(int id, String firstname, String lastname, Date birthday, Grade grade) {
			this.id = id;
			this.firstname = firstname;
			this.lastname = lastname;
			this.birthday = birthday;
			this.grade = grade;
		}
		
		public Builder gender(String gender) {
			this.gender = gender;
			return this;
		}
		
		public Builder borrowedList(List<BorrowedMaterial> borrowedList) {
			this.borrowedList = borrowedList;
			return this;
		}
		
		public Builder profile(Set<Subject> profile) {
			this.profile = profile;
			return this;
		}
		
		public Builder parent(Parent parent) {
			this.parent = parent;
			return this;
		}
		
		public Builder leavingSchool(boolean leavingSchool){
			this.leavingSchool = leavingSchool;
			return this;
		}
		
		public Student build() {
			return new Student(this);
		}
	}
	
	private Student(Builder builder) {
		id = builder.id;
		firstname = builder.firstname;
		lastname = builder.lastname;
		birthday = builder.birthday;
		grade = builder.grade;
		leavingSchool = builder.leavingSchool;
		
		gender = builder.gender;
		borrowedMaterials = builder.borrowedList;
		profile = builder.profile;
		parent = builder.parent;
		
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + id;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof Student))
			return false;
		Student other = (Student) obj;
		if (getId() != other.getId())
			return false;
		return true;
	}

	@Override
	public int compareTo(Student o) {
		int compareResult = getLastname().compareTo(o.getLastname());
		if(compareResult != 0) {
			return compareResult;
		}
		
		compareResult = getFirstname().compareTo(o.getFirstname());
		if(compareResult != 0) {
			return compareResult;
		}
		
		return Integer.compare(hashCode(), o.hashCode());
	}
} @Entity
@Table(name="teachingMaterial")
public class TeachingMaterial implements de.dhbw.humbuch.model.entity.Entity, Serializable, Comparable<TeachingMaterial> {
	private static final long serialVersionUID = -6153270685462221761L;

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private int id;
	
	@ManyToOne(fetch=FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
	@JoinColumn(name="categoryId", referencedColumnName="id")
	private Category category;
	
	@ElementCollection(targetClass=Subject.class)
	@Enumerated(EnumType.STRING)
	@CollectionTable(name="teachingMaterialSubject", joinColumns = @JoinColumn(name="teachingMaterialId"))
	@Column(name="subject")
	private Set<Subject> profile = EnumSet.noneOf(Subject.class);
	
	private String name;
	private String producer;
	private String identifyingNumber;
	private double price;
	private String comment;
	
	private int fromGrade;
	private int toGrade;
	
	@Enumerated(EnumType.ORDINAL)
	private Term fromTerm = Term.FIRST;
	
	@Enumerated(EnumType.ORDINAL)
	private Term toTerm = Term.SECOND;
	
	private Date validFrom;
	private Date validUntil;
	
	/**
	 * Required by Hibernate.<p>
	 * Use the {@link Builder} instead.
	 * 
	 * @see Builder
	 */
	@Deprecated
	public TeachingMaterial() {}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public Category getCategory() {
		return category;
	}

	public void setCategory(Category category) {
		this.category = category;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getProducer() {
		return producer;
	}

	public void setProducer(String producer) {
		this.producer = producer;
	}

	public String getIdentifyingNumber() {
		return identifyingNumber;
	}

	public void setIdentifyingNumber(String identifyingNumber) {
		this.identifyingNumber = identifyingNumber;
	}

	public double getPrice() {
		return price;
	}

	public void setPrice(double price) {
		this.price = price;
	}

	public String getComment() {
		return comment;
	}

	public void setComment(String comment) {
		this.comment = comment;
	}

	public int getFromGrade() {
		return fromGrade;
	}

	public void setFromGrade(int fromGrade) {
		this.fromGrade = fromGrade;
	}

	public int getToGrade() {
		return toGrade;
	}

	public void setToGrade(int toGrade) {
		this.toGrade = toGrade;
	}

	public Term getFromTerm() {
		return fromTerm;
	}

	public void setFromTerm(Term fromTerm) {
		this.fromTerm = fromTerm;
	}

	public Term getToTerm() {
		return toTerm;
	}

	public void setToTerm(Term toTerm) {
		this.toTerm = toTerm;
	}

	public Date getValidFrom() {
		return validFrom;
	}

	public void setValidFrom(Date validFrom) {
		this.validFrom = validFrom;
	}

	public Date getValidUntil() {
		return validUntil;
	}

	public void setValidUntil(Date validUntil) {
		this.validUntil = validUntil;
	}

	public Set<Subject> getProfile() {
		return profile;
	}

	public void setProfile(Set<Subject> profile) {
		this.profile = profile;
	}

	public void addSubject(Subject subject) {
		getProfile().add(subject);
	}
	
	public boolean hasSubject(Subject subject) {
		return getProfile().contains(subject);
	}
	
	public static class Builder {
		private final Category category;
		private final String name;
		private final String identifyingNumber;
		private final Date validFrom;
		
		private Set<Subject> profile;
		private String producer;
		private double price;
		private String comment;
		private int fromGrade;
		private int toGrade;
		private Term fromTerm;
		private Term toTerm;
		private Date validUntil;
		
		public Builder(Category category, String name, String identifyingNumber, Date validFrom) {
			this.category = category;
			this.name = name;
			this.identifyingNumber = identifyingNumber;
			this.validFrom = validFrom;
			this.fromTerm = Term.FIRST;
			this.toTerm = Term.SECOND;
		}
		
		public Builder profile(Set<Subject> profile) {
			this.profile = profile;
			return this;
		}
		
		public Builder producer(String producer) {
			this.producer = producer;
			return this;
		}
		
		public Builder price(double price) {
			this.price = price;
			return this;
		}
		
		public Builder comment(String comment) {
			this.comment = comment;
			return this;
		}
		
		public Builder fromGrade(int fromGrade) {
			this.fromGrade = fromGrade;
			return this;
		}
		
		public Builder toGrade(int toGrade) {
			this.toGrade = toGrade;
			return this;
		}
		
		public Builder fromTerm(Term fromTerm) {
			this.fromTerm = fromTerm;
			return this;
		}
		
		public Builder toTerm(Term toTerm) {
			this.toTerm = toTerm;
			return this;
		}
		
		public Builder validUntil(Date validUntil) {
			this.validUntil = validUntil;
			return this;
		}
		
		public TeachingMaterial build() {
			return new TeachingMaterial(this);
		}
	}
	
	private TeachingMaterial(Builder builder) {
		this.category = builder.category;
		this.name = builder.name;
		this.identifyingNumber = builder.identifyingNumber;
		this.validFrom = builder.validFrom;
		
		this.profile = builder.profile;
		this.producer = builder.producer;
		this.price = builder.price;
		this.comment = builder.comment;
		this.fromGrade = builder.fromGrade;
		this.toGrade = builder.toGrade;
		this.fromTerm = builder.fromTerm;
		this.toTerm = builder.toTerm;
		this.validUntil = builder.validUntil;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + id;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof TeachingMaterial))
			return false;
		TeachingMaterial other = (TeachingMaterial) obj;
		if (getId() != other.getId())
			return false;
		return true;
	}

	@Override
	public int compareTo(TeachingMaterial o) {
		int compareResult = getName().compareTo(o.getName());
		if(compareResult != 0) {
			return compareResult;
		}
		
		return Integer.compare(hashCode(), o.hashCode());
	}
	
} public final class PDFClassList extends PDFHandler {

	private Grade grade;
	private Map<Grade, Map<TeachingMaterial, Integer>> gradesMap;

	/**
	 * Prints all teachingMaterials and their amount of each grade in the map.
	 * 
	 * @param gradesMap
	 *            To each grade in the map another map belongs that contains all
	 *            teachingMaterials of the grade and the amount of their
	 *            occurrences.
	 */
	public PDFClassList(Map<Grade, Map<TeachingMaterial, Integer>> gradesMap) {
		super();
		this.gradesMap = gradesMap;
	}

	protected void insertDocumentParts(Document document) {
		if (this.gradesMap != null) {
			for (Grade grade : this.gradesMap.keySet()) {
				this.grade = grade;
				this.addHeading(document);
				this.addGradeInformation(document);
				this.addInformationAboutDocument(document, "Klassen-Liste");
				this.addContent(document);
				document.newPage();
				this.resetPageNumber();
			}
		}
	}

	protected void addContent(Document document) {
		PdfPTable table = this.createTableWithRentalInformationHeaderForClass();

		Map<TeachingMaterial, Integer> map = this.gradesMap.get(this.grade);

		for (TeachingMaterial teachingMaterial : map.keySet()) {
			String[] contentArray = { teachingMaterial.getName(), "" + map.get(teachingMaterial) };
			new PDFHandler.TableBuilder(table, contentArray).withBorder(true).isCenterAligned(true).padding(PDFHandler.CELL_PADDING).fillTable();
		}

		try {
			document.add(table);
		}
		catch (DocumentException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Inserts information about the grade.
	 * 
	 * @param document
	 *            represents the PDF before it is saved
	 */
	private void addGradeInformation(Document document) {
		PdfPTable table = PDFHandler.createMyStandardTable(2, new float[] { 1f, 6f });

		String[] contentArray = { "Klasse: ", "" + this.grade.toString() };
		new PDFHandler.TableBuilder(table, contentArray).withBorder(false).isCenterAligned(false).fillTable();
		
		try {
			document.add(table);
			PDFHandler.addEmptyLineToDocument(document, 1);
		}
		catch (DocumentException e) {
			e.printStackTrace();
		}
	}

	public PDFClassList() {

	}
} public abstract class PDFHandler {

	private Document document;
	private HeaderFooter event;
	protected final static float TABLEWIDTH = 460f;
	protected final static float CELL_PADDING = 7f;

	/**
	 * 
	 * @param path
	 *            links to the directory where the PDF file should be saved
	 */
	public PDFHandler(String path) {
		this.document = new Document();
	}

	/**
	 * Create a document with the specified sizes and margins. This document is the internal representation 
	 * of the PDF.
	 */
	public PDFHandler() {
		this.document = new Document(new RectangleReadOnly(595,842), 30f, 30f, 25f, 35f);
	}

	/**
	 * Creates the pdf with the information in the object that was passed to the
	 * constructor previously.
	 * 
	 * @param path
	 *            where the file will be saved
	 */
	public void savePDF(String path) {
		try {
			PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(path));
			event = new HeaderFooter();
			writer.setBoxSize("art", new Rectangle(36, 54, 559, 788));
			writer.setPageEvent(event);
		}
		catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		catch (DocumentException e) {
			e.printStackTrace();
		}
		this.document.open();
		this.addMetaData(document);
		this.insertDocumentParts(document);
		this.document.close();
	}

	/**
	 * Creates a ByteArrayOutputStream which contains the PDF as a byte array.
	 * 
	 * @return the byteArrayOutputStream the PDF is stored in, null if an error
	 *         occurred.
	 */
	public ByteArrayOutputStream createByteArrayOutputStreamForPDF() {
		ByteArrayOutputStream byteArrayOutputStream;
		try {
			byteArrayOutputStream = new ByteArrayOutputStream();
			PdfWriter writer = PdfWriter.getInstance(document, byteArrayOutputStream);
			event = new HeaderFooter();
			writer.setBoxSize("art", new Rectangle(36, 54, 559, 788));
			writer.setPageEvent(event);

			this.document.open();
			this.addMetaData(document);
			int initialDocumentSize = writer.getCurrentDocumentSize();
			this.insertDocumentParts(document);

			if (byteArrayOutputStream.size() > 0 || writer.getCurrentDocumentSize() > initialDocumentSize) {
				this.document.close();
			}
			else {
				return null;
			}

			return byteArrayOutputStream;
		}
		catch (DocumentException e) {
			System.err.println("Could not create ByteArrayOutputStream of PDF data. " + e.getMessage());
		}

		return null;
	}

	/**
	 * Adds meta data to the PDF document. The information of using iText must
	 * be part of the meta data due to the license of the iText library!
	 * 
	 * @param document
	 *            represents the PDF before it is saved
	 */
	private void addMetaData(Document document) {
		document.addTitle("Humbuch Schule");
		document.addSubject("Using iText");
		document.addKeywords("Java, PDF, iText");
		document.addAuthor("Schlager");
		document.addCreator("Schlager");
	}

	/**
	 * Set the logo of Humboldt on the left corner and the current date on the
	 * right corner
	 * 
	 * @param document
	 *            reference of the pdfDocument object
	 */
	protected void addHeading(Document document) {
		Paragraph paragraph = new Paragraph();
		PdfPTable table = createMyStandardTable(2);

		table.setTotalWidth(TABLEWIDTH);
		PdfPCell cell;

		Image img = new ResourceLoader("pdf/humboldt_logo.png").getImage();
		img.setAlignment(Element.ALIGN_BOTTOM);
		img.scaleToFit(205f, 65f);
		cell = new PdfPCell(img);

		cell.setBorder(0);
		table.addCell(cell);

		String date = new SimpleDateFormat("dd.MM.yyyy", Locale.GERMAN).format(Calendar.getInstance().getTime());

		cell = new PdfPCell(new Phrase(date));
		cell.setBorder(0);
		cell.setHorizontalAlignment(Element.ALIGN_RIGHT);
		cell.setVerticalAlignment(Element.ALIGN_BOTTOM);
		table.addCell(cell);

		cell = new PdfPCell(new Phrase(""));
		cell.setBorder(Rectangle.BOTTOM);
		table.addCell(cell);
		cell = new PdfPCell(new Phrase(""));
		cell.setBorder(Rectangle.BOTTOM);
		table.addCell(cell);

		paragraph.add(table);
		addEmptyLine(paragraph, 1);

		try {
			document.add(paragraph);
		}
		catch (DocumentException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Adds a signature field with a date field to the document. Should be the
	 * last part that is added to the document.
	 * 
	 * @param document
	 *            represents the PDF before it is saved
	 * @param role
	 *            word for the kind of person that shall sign the paper
	 */
	protected void addSignatureField(Document document, String role) {
		Paragraph paragraph = new Paragraph();

		//this table contains the signatureTable and the dataTable.
		// this purpose makes it easier to format
		PdfPTable table = createMyStandardTable(2);

		//the first column is double times greater than the second column
		try {
			table.setWidths(new float[] { 10f, 20f });
		}
		catch (DocumentException e) {
			e.printStackTrace();
		}

		//create and fill date table
		PdfPTable dateTable = new PdfPTable(1);

		PdfPCell cell = new PdfPCell(new Phrase(""));
		//just the bottom border will be displayed (line for date)
		cell.setBorderWidthTop(0);
		cell.setBorderWidthLeft(0);
		cell.setBorderWidthRight(0);

		dateTable.addCell(cell);

		cell = new PdfPCell(new Phrase("Datum"));
		cell.setBorder(0);

		dateTable.addCell(cell);

		//put date table into the 'parent' table
		cell = new PdfPCell(dateTable);
		cell.setBorder(0);
		table.addCell(cell);

		//create and fill signature table
		PdfPTable signatureTable = new PdfPTable(1);
		cell = new PdfPCell(new Phrase(""));
		//just the bottom border will be displayed (line for signature)
		cell.setBorderWidthTop(0);
		cell.setBorderWidthLeft(0);
		cell.setBorderWidthRight(0);

		signatureTable.addCell(cell);

		cell = new PdfPCell(new Phrase("Unterschrift " + role));
		cell.setBorder(0);

		signatureTable.addCell(cell);

		//put signature table into the 'parent' table
		cell = new PdfPCell(signatureTable);
		cell.setBorder(0);
		table.addCell(cell);

		paragraph.add(table);
		try {
			document.add(paragraph);
		}
		catch (DocumentException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * A table is generated with the header: Klasse, Bezeichnung Lehrmittel, Unterschrift
	 * 
	 * @return PdfPTable
	 */
	protected PdfPTable createTableWithRentalInformationHeader() {
		PdfPTable table = createMyStandardTable(3, new float[] { 2.25f, 1f, 1f });
		Font font = FontFactory.getFont("Helvetica", 12, Font.BOLD);
		new PDFHandler.TableBuilder(table, new String[] { "Bezeichnung Lehrmittel", "bis Klasse", "Unterschrift" }).withBorder(true)
				.isCenterAligned(true).font(font).padding(CELL_PADDING).fillTable();

		return table;
	}
	
	/**
	 * A table is generated with the header: Klasse, Bezeichnung Lehrmittel
	 * 
	 * @return PdfPTable
	 */
	protected PdfPTable createTableWithRentalInformationHeaderWithoutSignColumn() {
		PdfPTable table = createMyStandardTable(2, new float[] { 3f, 1f});
		Font font = FontFactory.getFont("Helvetica", 12, Font.BOLD);
		new PDFHandler.TableBuilder(table, new String[] { "Bezeichnung Lehrmittel", "bis Klasse"}).withBorder(true)
				.isCenterAligned(true).font(font).padding(CELL_PADDING).fillTable();

		return table;
	}

	/**
	 * A table is generated with the header: Bezeichnung Lehrmittel, Anzahl
	 * 
	 * @return PdfPTable
	 */
	protected PdfPTable createTableWithRentalInformationHeaderForClass() {
		PdfPTable table = createMyStandardTable(2, new float[] { 3f, 1f });
		Font font = FontFactory.getFont("Helvetica", 12, Font.BOLD);
		new PDFHandler.TableBuilder(table, new String[] { "Bezeichnung Lehrmittel", "Anzahl" }).withBorder(true).font(font).isCenterAligned(true).padding(CELL_PADDING).fillTable();

		return table;
	}

	protected void addInformationAboutDocument(Document document, String informationText) {
		PdfPTable table = createMyStandardTable(1);
		new PDFHandler.TableBuilder(table, new String[] { informationText })
				.font(FontFactory.getFont("Times New Roman", 14, Font.BOLD)).fillTable();
		try {
			document.add(table);
			PDFHandler.addEmptyLineToDocument(document, 1);
		}
		catch (DocumentException e) {
			e.printStackTrace();
		}
	}

	protected void resetPageNumber() {
		this.event.resetPageNumber();
	}

	protected static void addEmptyLine(Paragraph paragraph, int number) {
		for (int i = 0; i < number; i++) {
			paragraph.add(new Paragraph(" "));
		}
	}

	protected static void addEmptyLineToDocument(Document document, int number) {
		Paragraph paragraph = new Paragraph();
		for (int i = 0; i < number; i++) {
			paragraph.add(new Paragraph(" "));
		}

		try {
			document.add(paragraph);
		}
		catch (DocumentException e) {
			System.err.println("Could not add empty line to documnet " + e.getStackTrace());
		}
	}

	/**
	 * Create a standard table with constant table width.
	 * 
	 * @param columnNumber
	 *            set how many columns the table will have
	 * @return table
	 */
	protected static PdfPTable createMyStandardTable(int columnNumber) {
		PdfPTable table = new PdfPTable(columnNumber);
		table.setLockedWidth(true);
		table.setTotalWidth(TABLEWIDTH);

		return table;
	}

	/**
	 * Create a standard table with constant table width.
	 * 
	 * @param columnNumber
	 *            set how many columns the table will have
	 * @param columnWidths
	 *            set the ratio between the columns. If null, all columns will
	 *            be equal.
	 * @return table
	 */
	protected static PdfPTable createMyStandardTable(int columnNumber, float[] columnWidths) {
		PdfPTable table = new PdfPTable(columnNumber);
		table.setLockedWidth(true);
		table.setTotalWidth(TABLEWIDTH);

		if (!(columnWidths == null) && (columnWidths.length != 0)) {
			try {
				table.setWidths(columnWidths);
			}
			catch (DocumentException e) {
				System.err.println("Could not set columnWidths of standardTable " + e.getStackTrace());
			}
		}

		return table;
	}

	
	/**
	 * Use the configuration information to fill the table cells
	 * @param tableBuilder Table configuration
	 */
	private static void fillTableWithContent(TableBuilder tableBuilder) {
		PdfPCell cell = null;
		for (int i = 0; i < tableBuilder.contentArray.length; i++) {
			if(tableBuilder.contentArray[i] == null || tableBuilder.contentArray[i].equals("null")){
				tableBuilder.contentArray[i] = "";
			}
			if (tableBuilder.font != null) {
				cell = new PdfPCell(new Phrase(tableBuilder.contentArray[i], tableBuilder.font));
			}
			else {
				cell = new PdfPCell(new Phrase(tableBuilder.contentArray[i]));
			}
			if (tableBuilder.isAlignedCentrally) {
				cell.setHorizontalAlignment(Element.ALIGN_CENTER);
				cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
			}
			if (!tableBuilder.withBorder) {
				cell.setBorder(0);
			}
			if (tableBuilder.padding != 0f) {
				cell.setPadding(tableBuilder.padding);
			}
			if (tableBuilder.leading != 0f) {
				cell.setLeading(tableBuilder.leading, tableBuilder.leading);
			}
			tableBuilder.table.addCell(cell);
		}
	}

	/**
	 * In this method all parts of the document shall be 'put' together.
	 * 
	 * @param document
	 *            represents the PDF before it is saved
	 */
	protected abstract void insertDocumentParts(Document document);

	/**
	 * In this method the PDF-specific information shall be inserted into the
	 * document.
	 * 
	 * @param document
	 *            represents the PDF before it is saved
	 */
	protected abstract void addContent(Document document);


	class TableBuilder {

		private PdfPTable table;
		private String[] contentArray;
		private boolean withBorder;
		private boolean isAlignedCentrally;
		private float padding;
		private Font font;
		private float leading;

		public TableBuilder(PdfPTable table, String[] contentArray) {
			this.table = table;
			this.contentArray = contentArray;
		}

		public TableBuilder withBorder(boolean withBorder) {
			this.withBorder = withBorder;
			return this;
		}

		public TableBuilder isCenterAligned(boolean isCenterAligned) {
			this.isAlignedCentrally = isCenterAligned;
			return this;
		}

		public TableBuilder padding(float padding) {
			this.padding = padding;
			return this;
		}

		public TableBuilder font(Font font) {
			this.font = font;
			return this;
		}

		public TableBuilder leading(float leading) {
			this.leading = leading;
			return this;
		}

		public void fillTable() {
			PDFHandler.fillTableWithContent(this);
		}
	}


	/** Inner class to add a header and a footer. */
	class HeaderFooter extends PdfPageEventHelper {

		/** Alternating phrase for the header. */
		Phrase[] header = new Phrase[2];
		/** Current page number (will be reset for every chapter). */
		int pagenumber;

		/**
		 * Initialize one of the headers.
		 * 
		 * @see com.itextpdf.text.pdf.PdfPageEventHelper#onOpenDocument(com.itextpdf.text.pdf.PdfWriter,
		 *      com.itextpdf.text.Document)
		 */
		public void onOpenDocument(PdfWriter writer, Document document) {
			header[0] = new Phrase("Movie history");
		}

		/**
		 * Initialize one of the headers, based on the chapter title; reset the
		 * page number.
		 * 
		 * @see com.itextpdf.text.pdf.PdfPageEventHelper#onChapter(com.itextpdf.text.pdf.PdfWriter,
		 *      com.itextpdf.text.Document, float, com.itextpdf.text.Paragraph)
		 */
		public void onChapter(PdfWriter writer, Document document,
				float paragraphPosition, Paragraph title) {
			header[1] = new Phrase(title.getContent());
			pagenumber = 1;
		}

		/**
		 * Increase the page number.
		 * 
		 * @see com.itextpdf.text.pdf.PdfPageEventHelper#onStartPage(com.itextpdf.text.pdf.PdfWriter,
		 *      com.itextpdf.text.Document)
		 */
		public void onStartPage(PdfWriter writer, Document document) {
			pagenumber++;
		}

		/**
		 * Adds the header and the footer.
		 * 
		 * @see com.itextpdf.text.pdf.PdfPageEventHelper#onEndPage(com.itextpdf.text.pdf.PdfWriter,
		 *      com.itextpdf.text.Document)
		 */
		public void onEndPage(PdfWriter writer, Document document) {
			Rectangle rect = writer.getBoxSize("art");
			//            switch(writer.getPageNumber() % 2) {
			//            case 0:
			//                ColumnText.showTextAligned(writer.getDirectContent(),
			//                        Element.ALIGN_RIGHT, header[0],
			//                        rect.getRight(), rect.getTop(), 0);
			//                break;
			//            case 1:
			//                ColumnText.showTextAligned(writer.getDirectContent(),
			//                        Element.ALIGN_LEFT, header[0],
			//                        rect.getLeft(), rect.getTop(), 0);
			//                break;
			//            }
			ColumnText.showTextAligned(writer.getDirectContent(),
					Element.ALIGN_CENTER, new Phrase(String.format("- Seite %d -", pagenumber)),
					(rect.getLeft() + rect.getRight()) / 2, 20, 0);
		}
		
		public void resetPageNumber() {
			this.pagenumber = 1;
		}
	}


	public static class PDFStreamSource implements StreamSource {

		private static final long serialVersionUID = 1L;
		ByteArrayOutputStream byteArrayOutputstream;

		public PDFStreamSource(ByteArrayOutputStream byteArrayOutputStream) {
			this.byteArrayOutputstream = byteArrayOutputStream;
		}

		@Override
		public InputStream getStream() {
			// Here we return the pdf contents as a byte-array
			return new ByteArrayInputStream(this.byteArrayOutputstream.toByteArray());
		}
	}
} public class PDFInformationProcessor {

	/**
	 * It sorts the final data structure for grades and students and links
	 * together all students and their materials.
	 * 
	 * @param allSelectedMaterials
	 *            all selected materials which should be linked to their
	 *            students
	 * @param allSelectedStudents
	 *            all selected students which should be linked to their
	 *            materials
	 * @return the students and their materials linked together in one data
	 *         structure
	 * */
	public static LinkedHashMap<Student, List<BorrowedMaterial>> linkStudentsAndMaterials(
			HashSet<BorrowedMaterial> allSelectedMaterials,
			HashSet<Student> allSelectedStudents) {
		LinkedHashMap<Student, List<BorrowedMaterial>> studentsWithMaterials = new LinkedHashMap<Student, List<BorrowedMaterial>>();

		// Sort for grades and students
		TreeMap<Grade, List<Student>> treeToSortForGrades = new TreeMap<Grade, List<Student>>();
		for (Student student : allSelectedStudents) {
			if (treeToSortForGrades.containsKey(student.getGrade())) {
				List<Student> studentsInGrade = treeToSortForGrades.get(student
						.getGrade());
				if (studentsInGrade.contains(student)) {
					continue;
				}
				studentsInGrade.add(student);
				Collections.sort(studentsInGrade);
				treeToSortForGrades.put(student.getGrade(), studentsInGrade);
			} else {
				List<Student> studentList = new ArrayList<Student>();
				studentList.add(student);
				treeToSortForGrades.put(student.getGrade(), studentList);
			}
		}

		// Extract all the informationen needed to create the pdf
		for (Grade grade : treeToSortForGrades.keySet()) {
			List<Student> studentsInGrade = treeToSortForGrades.get(grade);
			for (Student student : studentsInGrade) {
				for (BorrowedMaterial material : allSelectedMaterials) {
					if (student.equals(material.getStudent())) {
						if (studentsWithMaterials.containsKey(student)) {
							List<BorrowedMaterial> currentlyAddedMaterials = studentsWithMaterials
									.get(student);
							currentlyAddedMaterials.add(material);
							Collections.sort(currentlyAddedMaterials);
							studentsWithMaterials.put(student,
									currentlyAddedMaterials);
						} else {
							List<BorrowedMaterial> materialList = new ArrayList<BorrowedMaterial>();
							materialList.add(material);
							studentsWithMaterials.put(student, materialList);
						}
					}
				}
			}
		}

		return studentsWithMaterials;
	}
} public final class PDFStudentList extends PDFHandler {

	private Student student;
	private List<BorrowedMaterial> borrowedMaterialList;
	private List<BorrowedMaterial> returnList;
	private List<BorrowedMaterial> lendingList;
	private boolean needEmpyLine = false;

	private Set<Builder> builders;

	/**
	 * For each student in the builder objects a PDF is created. This PDF can
	 * contain three different kinds of lists.
	 * 
	 * @param builder
	 *            can contain three kinds of lists. One list of materials the
	 *            student already received, one list of materials the student
	 *            will receive and one list of materials the student has to
	 *            return
	 * 
	 */
	public PDFStudentList(Builder... builder) {
		super();
		this.builders = new LinkedHashSet<>();
		for (Builder b : builder) {
			this.builders.add(b);
		}
	}

	/**
	 * For each student in the builder objects a PDF is created. This PDF can
	 * contain three different kinds of lists.
	 * 
	 * @param builder
	 *            can contain three kinds of lists. One list of materials the
	 *            student already received, one list of materials the student
	 *            will receive and one list of materials the student has to
	 *            return
	 * 
	 */
	public PDFStudentList(Set<Builder> builder) {
		super();
		this.builders = builder;
	}

	protected void insertDocumentParts(Document document) {
		for (Builder builder : builders) {
			if (builder.student != null) {
				this.student = builder.student;
			}
			else {
				continue;
			}
			this.borrowedMaterialList = builder.borrowedMaterialList;
			this.lendingList = builder.lendingList;
			this.returnList = builder.returnList;

			this.addHeading(document);
			this.addStudentInformation(document);
			
			this.addInformationAboutDocument(document, this.getDocumentTitle());
			this.addContent(document);
			document.newPage();
			this.resetPageNumber();
		}
	}

	private String getDocumentTitle() {
		if(this.borrowedMaterialList != null && !this.borrowedMaterialList.isEmpty()
				&& (this.lendingList == null || this.lendingList.isEmpty()) 
				&& (this.returnList == null || this.returnList.isEmpty())){
			return "Ausgeliehene Materialien";
		}
		if(this.lendingList != null && !this.lendingList.isEmpty()
				&& (this.borrowedMaterialList == null || this.borrowedMaterialList.isEmpty()) 
				&& (this.returnList == null || this.returnList.isEmpty())){
			return "Auszuleihende Materialien";
		}
		if(this.returnList != null && !this.returnList.isEmpty()
				&& (this.lendingList == null || this.lendingList.isEmpty()) 
				&& (this.borrowedMaterialList == null || this.borrowedMaterialList.isEmpty())){
			return "Zurückzugebende Materialien";
		}
		else{
			return "Material-Informationen";
		}
	}

	protected void addContent(Document document) {
		if (this.borrowedMaterialList != null && !this.borrowedMaterialList.isEmpty()) {
			PdfPTable table = PDFHandler.createMyStandardTable(1);
			new PDFHandler.TableBuilder(table,
					new String[] { "\nDie folgenden Lehrmittel befinden sich im Besitz des Schülers/der Schülerin:" })
					.font(FontFactory.getFont("Helvetica", 10, Font.BOLD)).fillTable();

			try {
				document.add(table);
				addEmptyLineToDocument(document, 1);
			}
			catch (DocumentException e) {
				e.printStackTrace();
			}

			table = this.createTableWithRentalInformationHeaderWithoutSignColumn();

			for (BorrowedMaterial borrowedMaterial : this.borrowedMaterialList) {
				String[] contentArray = { borrowedMaterial.getTeachingMaterial().getName(),
											"" + borrowedMaterial.getTeachingMaterial().getToGrade()};
				new PDFHandler.TableBuilder(table, contentArray).withBorder(true).isCenterAligned(true).padding(PDFHandler.CELL_PADDING).fillTable();
			}
			try {
				document.add(table);
				PDFHandler.addEmptyLineToDocument(document, 1);
			}
			catch (DocumentException e) {
				e.printStackTrace();
			}
		}		
		if (this.lendingList != null && !this.lendingList.isEmpty()) {
			PdfPTable table = PDFHandler.createMyStandardTable(1);

			new PDFHandler.TableBuilder(table, new String[] { "\nDie folgenden Lehrmittel sollen ausgeliehen werden:" })
					.font(FontFactory.getFont("Helvetica", 10, Font.BOLD)).fillTable();

			try {
				document.add(table);
				addEmptyLineToDocument(document, 1);
			}
			catch (DocumentException e) {
				e.printStackTrace();
			}

			table = this.createTableWithRentalInformationHeader();

			for (BorrowedMaterial borrowedMaterial : this.lendingList) {
				String[] contentArray = { borrowedMaterial.getTeachingMaterial().getName(),
											"" + borrowedMaterial.getTeachingMaterial().getToGrade(),
											"" };

				new PDFHandler.TableBuilder(table, contentArray).isCenterAligned(true).withBorder(true).padding(PDFHandler.CELL_PADDING).fillTable();
			}

			try {
				document.add(table);
				this.needEmpyLine = true;
			}
			catch (DocumentException e) {
				e.printStackTrace();
			}
		}
		if (this.returnList != null && !this.returnList.isEmpty()) {
			if(this.needEmpyLine){
				addEmptyLineToDocument(document, 1);
				this.needEmpyLine = false;
			}
			
			PdfPTable table = PDFHandler.createMyStandardTable(1);
			new PDFHandler.TableBuilder(table, new String[] { "\nDie folgenden Lehrmittel müssen zurückgegeben werden:" })
					.font(FontFactory.getFont("Helvetica", 10, Font.BOLD)).fillTable();

			try {
				document.add(table);
				addEmptyLineToDocument(document, 1);
			}
			catch (DocumentException e) {
				e.printStackTrace();
			}

			table = this.createTableForReturnPDF();

			for (BorrowedMaterial borrowedMaterial : this.returnList) {
				String[] contentArray = { borrowedMaterial.getTeachingMaterial().getName(),
											"" + borrowedMaterial.getTeachingMaterial().getToGrade(),
											"", "" };

				new PDFHandler.TableBuilder(table, contentArray).withBorder(true).isCenterAligned(true).padding(PDFHandler.CELL_PADDING).fillTable();
			}

			try {
				document.add(table);
				PDFHandler.addEmptyLineToDocument(document, 1);
				this.addRentalDisclosure(document);
				this.addSignatureField(document, "");
			}
			catch (DocumentException e) {
				e.printStackTrace();
			}
		}

	}

	/**
	 * Inserts information about the student like grade, language, name etc.
	 * 
	 * @param document
	 *            represents the PDF before it is saved
	 */
	private void addStudentInformation(Document document) {
		PdfPTable table = PDFHandler.createMyStandardTable(2, new float[] { 1f, 6f });

		String[] contentArray = { "Schüler: ", this.student.getFirstname() + " " + this.student.getLastname(),
									"Klasse: ", "" + this.student.getGrade().toString(),
									"Sprachen: ", SubjectHandler.getLanguageProfile(this.student.getProfile()),
									"Religion: ", SubjectHandler.getReligionProfile(this.student.getProfile()) };

		new PDFHandler.TableBuilder(table, contentArray).fillTable();

		try {
			document.add(table);
			PDFHandler.addEmptyLineToDocument(document, 1);

		}
		catch (DocumentException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 
	 * @param document
	 *            represents the PDF before it is saved
	 */
	private void addRentalDisclosure(Document document) {
		PdfPTable table = PDFHandler.createMyStandardTable(1);
		
		new PDFHandler.TableBuilder(table, 
				new String[] { "\nDie oben markierten Lehrmittel hat der Schüler/die Schülerin zurückgegeben.\n" +
						"Die ausgeliehenen Lehrmittel wurden auf Vollständigkeit und Beschädigung überprüft. " +
						"Beschädigte oder verlorengegangene Lehrmittel wurden ersetzt." }).leading(1.25f).fillTable();

		try {
			document.add(table);
			addEmptyLineToDocument(document, 2);
		}
		catch (DocumentException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * A table is generated with the header: Klasse, Bezeichnung Lehrmittel, Zurückgegeben (Ja, Nein)
	 * 
	 * @return PdfPTable
	 */
	protected PdfPTable createTableForReturnPDF() {		
		PdfPTable table = createMyStandardTable(4, new float[] { 2.25f, 1f, 0.5f, 0.5f });
		Font font = FontFactory.getFont("Helvetica", 12, Font.BOLD);
		
		PdfPCell cell = null;
		cell = new PdfPCell(new Phrase("Bezeichnung Lehrmittel", font));
		cell.setHorizontalAlignment(Element.ALIGN_CENTER);
		cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
		cell.setBorderWidthBottom(0);
		cell.setPadding(CELL_PADDING);
		table.addCell(cell);
		
		cell = new PdfPCell(new Phrase("bis Klasse", font));
		cell.setHorizontalAlignment(Element.ALIGN_CENTER);
		cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
		cell.setBorderWidthBottom(0);
		cell.setPadding(CELL_PADDING);
		table.addCell(cell);
		
		cell = new PdfPCell(new Phrase("Zurückgegeben", font));
		cell.setHorizontalAlignment(Element.ALIGN_CENTER);
		cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
		cell.setColspan(2);
		cell.setPadding(CELL_PADDING);
		table.addCell(cell);
		
		cell = new PdfPCell(new Phrase(""));
		cell.setHorizontalAlignment(Element.ALIGN_CENTER);
		cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
		cell.setBorderWidthTop(0);
		cell.setPadding(CELL_PADDING);
		table.addCell(cell);
		
		cell = new PdfPCell(new Phrase(""));
		cell.setHorizontalAlignment(Element.ALIGN_CENTER);
		cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
		cell.setBorderWidthTop(0);
		cell.setPadding(CELL_PADDING);
		table.addCell(cell);
		
		cell = new PdfPCell(new Phrase("Ja", font));
		cell.setHorizontalAlignment(Element.ALIGN_CENTER);
		cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
		cell.setPadding(CELL_PADDING);
		table.addCell(cell);
		
		cell = new PdfPCell(new Phrase("Nein", font));
		cell.setHorizontalAlignment(Element.ALIGN_CENTER);
		cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
		cell.setPadding(CELL_PADDING);
		table.addCell(cell);

		return table;
	}


	public static class Builder {

		private Student student;
		private List<BorrowedMaterial> borrowedMaterialList;
		private List<BorrowedMaterial> lendingList;
		private List<BorrowedMaterial> returnList;

		/**
		 * A builder object can contain three different kinds of lists. Each
		 * list is optional. The builder can be passed to the constructor of
		 * PDFStudentList and the lists the builder contains will be printed
		 */
		public Builder() {
		}

		/**
		 * 
		 * @param borrowedMaterialList
		 *            list of materials the student already received
		 * @return the builder object that has to be passed to the
		 *         PDFStudentList constructor
		 */
		public Builder borrowedMaterialList(List<BorrowedMaterial> borrowedMaterialList) {
			this.borrowedMaterialList = borrowedMaterialList;
			if (borrowedMaterialList != null) {
				this.student = borrowedMaterialList.get(0).getStudent();
			}
			return this;
		}

		/**
		 * 
		 * @param borrowedMaterialList
		 *            list of materials the student will receive
		 * @return the builder object that has to be passed to the
		 *         PDFStudentList constructor
		 */
		public Builder lendingList(List<BorrowedMaterial> borrowedMaterialList) {
			this.lendingList = borrowedMaterialList;
			if (borrowedMaterialList != null) {
				this.student = borrowedMaterialList.get(0).getStudent();
			}
			return this;
		}

		/**
		 * 
		 * @param borrowedMaterialList
		 *            list of materials the student has to return
		 * @return
		 */
		public Builder returnList(List<BorrowedMaterial> borrowedMaterialList) {
			this.returnList = borrowedMaterialList;
			if (borrowedMaterialList != null) {
				this.student = borrowedMaterialList.get(0).getStudent();
			}
			return this;
		}

		@Deprecated
		public PDFStudentList build() {
			return new PDFStudentList(this);
		}

	}
} public class ConfirmDialog extends Window {
	private static final long serialVersionUID = 5531902128467189080L;

	private static final String DEFAULT_WINDOW_CAPTION = "Bestätigen";
	private static final String DEFAULT_CONFIRM_CAPTION = "Ok";
	private static final String DEFAULT_CANCEL_CAPTION = "Abbrechen";
	
	public interface Listener extends Serializable {
		void onClose(ConfirmDialog dialog);
	}
	
	private Listener confirmListener = null;
	private boolean isConfirmed = false;
	
	public static void show(String message, Listener listener) {
		show(DEFAULT_WINDOW_CAPTION, message, listener);
	}
	
	public static void show(String caption, String message, Listener listener) {
		show(caption, message, DEFAULT_CONFIRM_CAPTION, DEFAULT_CANCEL_CAPTION, listener);
	}
	
	public static void show(String caption, String message, String confirmCaption, String cancelCaption, Listener listener) {
		new ConfirmDialog(caption, message, confirmCaption, cancelCaption, listener).show(UI.getCurrent());
	}
	
	private void show(UI parentWindow) {
		parentWindow.addWindow(this);
	}
	
	private ConfirmDialog(String caption, String message, String confirmCaption, String cancelCaption, Listener listener) {
		setListener(listener);
		setCaption(caption);

		addCloseListener(new Window.CloseListener() {
			private static final long serialVersionUID = -1251710433868337579L;

			@Override
			public void windowClose(CloseEvent e) {
				if(ConfirmDialog.this.isEnabled()) {
					ConfirmDialog.this.setEnabled(false);
					
					ConfirmDialog.this.setConfirmed(false);
					if(ConfirmDialog.this.getListener() != null) {
						ConfirmDialog.this.getListener().onClose(ConfirmDialog.this);
					}
				}
			}
		});
		
		// Create content
        VerticalLayout content = new VerticalLayout();
        content.setSpacing(true);
        content.setMargin(true);
        
        // Label for message
        Label text = new Label();
        text.setValue(message);
        text.setContentMode(ContentMode.HTML);
        
        // Panel for message
        Panel messagePanel = new Panel(text);
        messagePanel.setSizeFull();
        content.addComponent(messagePanel);
        content.setExpandRatio(messagePanel, 1f);

        // Layout for buttons
        HorizontalLayout buttons = new HorizontalLayout();
        buttons.setSpacing(true);
        content.addComponent(buttons);
        content.setComponentAlignment(buttons, Alignment.MIDDLE_RIGHT);
        
        // Cancel button
        final Button cancelBtn = new Button(cancelCaption);
        cancelBtn.setClickShortcut(KeyCode.ESCAPE, null);
        cancelBtn.focus();
        buttons.addComponent(cancelBtn);
        
        // Confirm button
        final Button confirmBtn = new Button(confirmCaption);
        confirmBtn.addStyleName("default");
        buttons.addComponent(confirmBtn);
        

        // ClickListener for buttons
        Button.ClickListener cl = new Button.ClickListener() {
			private static final long serialVersionUID = 165308935170840683L;

			@Override
			public void buttonClick(ClickEvent event) {
				if(ConfirmDialog.this.isEnabled()) {
					ConfirmDialog.this.setEnabled(false);
					
					ConfirmDialog.this.setConfirmed(event.getButton() == confirmBtn);
					ConfirmDialog.this.close();
					
					if (ConfirmDialog.this.getListener() != null) {
						ConfirmDialog.this.getListener().onClose(ConfirmDialog.this);
                    }
				}
			}
		};
		confirmBtn.addClickListener(cl);
		cancelBtn.addClickListener(cl);
		
		setContent(content);
		setResizable(false);
		setModal(true);
	}
	
	private void setConfirmed(boolean confirmed) {
		isConfirmed = confirmed;
	}
	
	private Listener getListener() {
		return confirmListener;	
	}
	
	private void setListener(Listener listener) {
		confirmListener = listener;
	}
	
	public boolean isConfirmed() {
		return isConfirmed;
	}
} public class PrintingComponent extends CustomComponent {

	private static final long serialVersionUID = 7241708455406233966L;

	private StreamResource streamResource;
	private String windowTitle;
	private MIMEType mimeType;
	private Window window;

	public PrintingComponent(StreamResource streamResource, String windowTitle) {
		this(streamResource, windowTitle, MIMEType.PDF);
	}

	public PrintingComponent(StreamResource streamResource, String windowTitle, MIMEType mimeType) {
		this.streamResource = streamResource;
		this.windowTitle = windowTitle;
		this.mimeType = mimeType;
		showWindow();
	}

	private void showWindow() {
		window = new Window(windowTitle);
		window.setWidth("70%");
		window.setHeight("90%");

		window.setResizable(false);
		window.setDraggable(true);
		window.setModal(true);
		window.setCloseShortcut(KeyCode.ESC, null);
		
		BrowserFrame embedded = new BrowserFrame();
		embedded.setWidth("100%");
		embedded.setHeight("99%");
		// Set the right MIME type
		streamResource.setMIMEType(mimeType.toString());

		embedded.setSource(streamResource);
		window.setContent(embedded);
		UI.getCurrent().addWindow(window);
	}
	
	public enum MIMEType {
		PDF("application/pdf"),
		HTML("text/html");
		
		private String value;
		
		private MIMEType(String value) {
			this.value = value;
		}
		
		@Override
		public String toString() {
			return value;
		}
	}
} public class StudentMaterialSelector extends CustomComponent {

	private static final long serialVersionUID = -618911643102742679L;

	private static final int MAX_STUDENTS_BEFORE_COLLAPSE = 12;
	private static final String TREE_TABLE_HEADER_DATA = "Daten auswählen";
	private static final String TREE_TABLE_HEADER_DATE = "Ausgeliehen bis";
	private static final String GRADE = "Klasse ";
	private static final String EVERYTHING_SELECT = "Alle Klassen auswählen";
	private static final String EVERYTHING_DESELECT = "Auswahl aufheben";

	private TreeTable treeTableContent;
	private Map<Grade, Map<Student, List<BorrowedMaterial>>> gradeAndStudentsWithMaterials;
	private ArrayList<StudentMaterialSelectorObserver> registeredObservers;
	private CheckBox checkBoxEverything;
	private Object checkBoxEverythingId;
	private LinkedHashMap<CheckBox, Object> allGradeCheckBoxes;
	private LinkedHashMap<CheckBox, Object> allStudentCheckBoxes;
	private LinkedHashMap<CheckBox, Object> allMaterialCheckBoxes;
	private String filterString;
	private boolean collapseGrades;
	private boolean changedCollapseFlag;
	private ValueChangeListener gradeListener;
	private ValueChangeListener studentListener;
	private ValueChangeListener materialListener;

	/**
	 * Default constructor. Instantiiates a new StudentMaterialSelector
	 * instance.
	 * */
	public StudentMaterialSelector() {
		init();
		buildLayout();
	}

	/*
	 * Initialize and configure member variables
	 */
	private void init() {
		treeTableContent = new TreeTable();
		gradeAndStudentsWithMaterials = new LinkedHashMap<Grade, Map<Student, List<BorrowedMaterial>>>();
		checkBoxEverything = new CheckBox(EVERYTHING_SELECT);
		allGradeCheckBoxes = new LinkedHashMap<CheckBox, Object>();
		allStudentCheckBoxes = new LinkedHashMap<CheckBox, Object>();
		allMaterialCheckBoxes = new LinkedHashMap<CheckBox, Object>();
		collapseGrades = false;
		changedCollapseFlag = false;

		treeTableContent.setSizeFull();
		treeTableContent.setWidth("100%");
		treeTableContent.setImmediate(true);
		treeTableContent.addContainerProperty(TREE_TABLE_HEADER_DATA,
				CheckBox.class, null);
		treeTableContent.addContainerProperty(TREE_TABLE_HEADER_DATE,
				String.class, "");

		implementListeners();
	}

	private void buildLayout() {
		setCompositionRoot(treeTableContent);
	}

	/*
	 * Implement the code for the different listeners on the CheckBox components
	 * displayed in the TreeTable Whenever a CheckBox changes its value (gets
	 * selected / deselected) all registered observers get notified.
	 */
	private void implementListeners() {
		/*
		 * The everything listener is above the top level elements to select /
		 * deselect all elements at once
		 */
		checkBoxEverything.addValueChangeListener(new ValueChangeListener() {

			private static final long serialVersionUID = -7395293342050339912L;

			@Override
			public void valueChange(ValueChangeEvent event) {
				boolean state = checkBoxEverything.getValue();

				if (state == true) {
					checkBoxEverything.setCaption(EVERYTHING_DESELECT);
				} else {
					checkBoxEverything.setCaption(EVERYTHING_SELECT);
				}

				for (CheckBox checkBoxGrade : allGradeCheckBoxes.keySet()) {
					checkBoxGrade.setValue(state);
				}
				notifyObserver();
			}
		});

		/*
		 * The grade listener is added to all top level elements (grades) in the
		 * tree. Whenever a grade is selected / deselected all Students
		 * belonging to that grade are selected / deselected, too.
		 */
		gradeListener = new ValueChangeListener() {

			private static final long serialVersionUID = -7395293342050339912L;

			@Override
			public void valueChange(ValueChangeEvent event) {
				CheckBox checkBoxGrade = (CheckBox) event.getProperty();
				Grade grade = (Grade) checkBoxGrade.getData();
				boolean gradeSelected = checkBoxGrade.getValue();

				for (CheckBox checkBoxStudent : allStudentCheckBoxes.keySet()) {
					Student student = (Student) checkBoxStudent.getData();
					if (student.getGrade().equals(grade)) {
						checkBoxStudent.setValue(gradeSelected);
					}
				}
				notifyObserver();
			}
		};

		/*
		 * The student listener is added to all student elements in the tree.
		 * When a student is selected / deselected all of its materials are
		 * selected / deselected, too.
		 */
		studentListener = new ValueChangeListener() {

			private static final long serialVersionUID = 5610970965368269295L;

			@Override
			public void valueChange(ValueChangeEvent event) {
				CheckBox checkBoxStudent = (CheckBox) event.getProperty();
				Student student = (Student) checkBoxStudent.getData();
				boolean studentSelected = checkBoxStudent.getValue();

				for (CheckBox checkBoxMaterial : allMaterialCheckBoxes.keySet()) {
					BorrowedMaterial material = (BorrowedMaterial) checkBoxMaterial
							.getData();
					if (material.getStudent().equals(student)) {
						checkBoxMaterial.setValue(studentSelected);
					}
				}
				notifyObserver();
			}
		};

		/*
		 * The material listener is added to all leafes (materials) in the tree.
		 * It only notifies all registered observers about the value change.
		 */
		materialListener = new ValueChangeListener() {

			private static final long serialVersionUID = 614006288313075687L;

			@Override
			public void valueChange(ValueChangeEvent event) {
				notifyObserver();
			}
		};
	}

	/**
	 * Call this method when the data which you want to display within the
	 * StudentMaterialSelector changes. This methods not only saves the new data
	 * in a member variable but also updates the TreeTable which is responsible
	 * for showing the data.
	 * 
	 * @param newGradeAndStudentsWithMaterials
	 *            the new data to be displayed by the StudentMaterialSelector
	 * */
	public void setGradesAndStudentsWithMaterials(
			Map<Grade, Map<Student, List<BorrowedMaterial>>> newGradeAndStudentsWithMaterials) {
		this.gradeAndStudentsWithMaterials = newGradeAndStudentsWithMaterials;
		rebuildTable(newGradeAndStudentsWithMaterials);
	}

	/**
	 * Returns all currently selected grades of the StudentMaterialSelector or
	 * an empty HashSet. Will never return <code>null</code>.
	 * 
	 * @return the currently selected grades of the StudentMaterialSelector or
	 *         an empty HashSet if none are selected
	 * */
	public HashSet<Grade> getCurrentlySelectedGrades() {
		HashSet<Grade> currentlySelectedGrades = new HashSet<Grade>();
		for (CheckBox checkBox : allGradeCheckBoxes.keySet()) {
			if (checkBox.getValue() == true) {
				currentlySelectedGrades.add((Grade) checkBox.getData());
			}
		}

		return currentlySelectedGrades;
	}

	/**
	 * Returns all currently selected students of the StudentMaterialSelector or
	 * an empty HashSet. Will never return <code>null</code>.
	 * 
	 * @return the currently selected students of the StudentMaterialSelector or
	 *         an empty HashSet if none are selected
	 * */
	public HashSet<Student> getCurrentlySelectedStudents() {
		HashSet<Student> currentlySelectedStudents = new HashSet<Student>();
		for (CheckBox checkBox : allStudentCheckBoxes.keySet()) {
			if (checkBox.getValue() == true) {
				currentlySelectedStudents.add((Student) checkBox.getData());
			}
		}

		return currentlySelectedStudents;
	}

	/**
	 * Returns all currently selected borrowed materials of the
	 * StudentMaterialSelector or an empty HashSet. Will never return
	 * <code>null</code>.
	 * 
	 * @return the currently selected materials of the StudentMaterialSelector
	 *         or an empty HashSet if none are selected
	 * */
	public HashSet<BorrowedMaterial> getCurrentlySelectedBorrowedMaterials() {
		HashSet<BorrowedMaterial> currentlySelectedBorrowedMaterials = new HashSet<BorrowedMaterial>();
		for (CheckBox checkBox : allMaterialCheckBoxes.keySet()) {
			if (checkBox.getValue() == true) {
				currentlySelectedBorrowedMaterials
						.add((BorrowedMaterial) checkBox.getData());
			}
		}

		return currentlySelectedBorrowedMaterials;
	}

	/**
	 * Register any class implementing the StudentMaterialSelectorObserver
	 * interface to get notified when any selection changes.
	 * 
	 * @param observer
	 *            the class implementing the interface which should be notified
	 *            of all changes
	 * */
	public void registerAsObserver(StudentMaterialSelectorObserver observer) {
		if (registeredObservers == null) {
			registeredObservers = new ArrayList<StudentMaterialSelectorObserver>();
		} else if (registeredObservers.contains(observer)) {
			return;
		}
		registeredObservers.add(observer);
	}

	/**
	 * Call this method to filter for specific students. The filtering is
	 * case-insensitve and filters for the firstname and lastname of any student
	 * in the StudentMaterialSelector. It does not only saves the filter string
	 * in a member variable but updates the TreeTable to only show students
	 * matching the filter. When passing null it is treated as an empty string.
	 * 
	 * @param filterString
	 *            the string on which filtering should be based upon
	 * */
	public void setFilterString(String filterString) {
		if (filterString == null) {
			filterString = "";
		}
		this.filterString = filterString;
		filterTableContent();
	}

	/*
	 * Method used for filtering. It filters the currently used data structure
	 * inside the StudentMaterialSelector and filters every students first and
	 * lastname (ignoring case). After filtering is done this method rebuilds
	 * the TreeTable to visualize the result of the filter process.
	 */
	private void filterTableContent() {
		LinkedHashMap<Grade, Map<Student, List<BorrowedMaterial>>> gradeAndStudentsWithMaterialsFiltered = new LinkedHashMap<Grade, Map<Student, List<BorrowedMaterial>>>();
		for (Grade grade : gradeAndStudentsWithMaterials.keySet()) {
			Map<Student, List<BorrowedMaterial>> entry = gradeAndStudentsWithMaterials
					.get(grade);
			Map<Student, List<BorrowedMaterial>> filteredEntries = new LinkedHashMap<Student, List<BorrowedMaterial>>();
			for (Student student : entry.keySet()) {
				// match firstname and lastname ignoring case
				String fullName = student.getFirstname() + " "
						+ student.getLastname();
				fullName = fullName.toLowerCase();
				if (fullName.contains(filterString.toLowerCase())) {
					filteredEntries.put(student, entry.get(student));
				}
			}

			if (filteredEntries.size() != 0) {
				gradeAndStudentsWithMaterialsFiltered.put(grade,
						filteredEntries);
			}
		}
		rebuildTable(gradeAndStudentsWithMaterialsFiltered);
	}

	/*
	 * Helper method used for rebuilding the TreeTable. It removes all items
	 * currently showing and resets the member variables holding information
	 * about the current state. It adds all grades, students and materials
	 * within the provided structure to the newly build TreeTable. This method
	 * resets the expand states of the components in the TreeTable.
	 * 
	 * @param newGradeAndStudentsWithMaterials the new data structure which
	 * should be visualized by the StudentMaterialSelector
	 */
	private void rebuildTable(
			Map<Grade, Map<Student, List<BorrowedMaterial>>> newGradeAndStudentWithMaterials) {
		treeTableContent.removeAllItems();
		allGradeCheckBoxes.clear();
		allStudentCheckBoxes.clear();
		allMaterialCheckBoxes.clear();
		collapseGrades = false;
		changedCollapseFlag = false;

		for (Grade grade : newGradeAndStudentWithMaterials.keySet()) {
			addGradeToTree(grade);
			Map<Student, List<BorrowedMaterial>> entry = newGradeAndStudentWithMaterials
					.get(grade);
			for (Student student : entry.keySet()) {
				addStudentToTree(student);
				for (BorrowedMaterial material : entry.get(student)) {
					addMaterialToTree(material);
				}
			}
		}

		validateTableContent();
	}

	/*
	 * Helper method used to update or rebuild or the TreeTable. This method
	 * takes care of adding the grade to the tree and storing all information in
	 * the corresponding member variables.
	 * 
	 * @return the item id of the newly added grade or null if there has been an
	 * error
	 */
	private Object addGradeToTree(Grade grade) {
		if (grade == null) {
			return null;
		}
		if (checkBoxEverythingId == null
				|| !treeTableContent.containsId(checkBoxEverythingId)) {
			checkBoxEverythingId = treeTableContent.addItem(new Object[] {
					checkBoxEverything, "" }, null);
			treeTableContent.setCollapsed(checkBoxEverythingId, false);
		}

		CheckBox checkBoxGrade = new CheckBox(GRADE + grade.getGrade()
				+ grade.getSuffix());
		checkBoxGrade.setData(grade);
		checkBoxGrade.addValueChangeListener(gradeListener);
		Object gradeItemId = treeTableContent.addItem(new Object[] {
				checkBoxGrade, "" }, null);
		treeTableContent.setCollapsed(gradeItemId, collapseGrades);

		treeTableContent.setParent(gradeItemId, checkBoxEverythingId);

		allGradeCheckBoxes.put(checkBoxGrade, gradeItemId);

		return gradeItemId;
	}

	/*
	 * @see StudentMaterialSelector.addGradeToTree(Grade grade) This method
	 * takes care of putting the student under right grade to maintain a valid
	 * hierarchy.
	 * 
	 * @return the item id of the newly added student or null if there has been
	 * an error
	 */
	private Object addStudentToTree(Student student) {
		if (student == null) {
			return null;
		}
		CheckBox checkBoxStudent = new CheckBox(student.getFirstname() + " "
				+ student.getLastname());
		checkBoxStudent.setData(student);
		checkBoxStudent.addValueChangeListener(studentListener);
		Object studentItemId = treeTableContent.addItem(new Object[] {
				checkBoxStudent, "" }, null);

		Object parentGradeItemId = null;
		for (CheckBox checkBoxGrade : allGradeCheckBoxes.keySet()) {
			Grade grade = (Grade) checkBoxGrade.getData();
			if (grade.equals(student.getGrade())) {
				parentGradeItemId = allGradeCheckBoxes.get(checkBoxGrade);
				break;
			}
		}
		if (parentGradeItemId == null) {
			parentGradeItemId = addGradeToTree(student.getGrade());
		}
		treeTableContent.setParent(studentItemId, parentGradeItemId);

		allStudentCheckBoxes.put(checkBoxStudent, studentItemId);

		if (allStudentCheckBoxes.size() > MAX_STUDENTS_BEFORE_COLLAPSE
				&& changedCollapseFlag == false) {
			collapseGrades = true;
			changedCollapseFlag = true;
			for (CheckBox checkBoxGrade : allGradeCheckBoxes.keySet()) {
				treeTableContent.setCollapsed(
						allGradeCheckBoxes.get(checkBoxGrade), collapseGrades);
			}
		}

		return studentItemId;
	}

	/*
	 * @see StudentMaterialSelector.addGradeToTree(Grade grade) This method
	 * takes care of putting the material under right student to maintain a
	 * valid hierarchy.
	 * 
	 * @return the item id for the newly added material or null if there has
	 * been an error
	 */
	private Object addMaterialToTree(BorrowedMaterial material) {
		if (material == null) {
			return null;
		}
		CheckBox checkBoxMaterial = new CheckBox(material.getTeachingMaterial()
				.getName());
		checkBoxMaterial.setData(material);
		checkBoxMaterial.addValueChangeListener(materialListener);

		Date dateBorrowedUntil = material.getBorrowUntil();
		String borrowedUntil = "";
		if (dateBorrowedUntil != null) {
			borrowedUntil = new SimpleDateFormat("dd.MM.YYYY")
					.format(dateBorrowedUntil);
		} else {
			TeachingMaterial teachingMaterial = material.getTeachingMaterial();
			String to = GRADE + teachingMaterial.getToGrade() + ", "
					+ teachingMaterial.getToTerm();
			borrowedUntil = to;
		}

		Object materialItemId = treeTableContent.addItem(new Object[] {
				checkBoxMaterial, borrowedUntil }, null);
		treeTableContent.setChildrenAllowed(materialItemId, false);

		Object parentStudentItemId = null;
		for (CheckBox checkBoxStudent : allStudentCheckBoxes.keySet()) {
			Student student = (Student) checkBoxStudent.getData();
			if (student.equals(material.getStudent())) {
				parentStudentItemId = allStudentCheckBoxes.get(checkBoxStudent);
				break;
			}
		}
		if (parentStudentItemId == null) {
			parentStudentItemId = addStudentToTree(material.getStudent());
		}
		treeTableContent.setParent(materialItemId, parentStudentItemId);

		allMaterialCheckBoxes.put(checkBoxMaterial, materialItemId);

		return materialItemId;
	}

	/*
	 * Validates the tree table so that empty grades / students etc. are not
	 * displayed.
	 */
	private void validateTableContent() {
		if (allGradeCheckBoxes.isEmpty()) {
			treeTableContent.removeItem(checkBoxEverythingId);
		}

		removeEmptyGrades();
		removeEmptyStudents();
	}

	private void removeEmptyGrades() {
		for (CheckBox checkBoxGrade : allGradeCheckBoxes.keySet()) {
			Object gradeItemId = allGradeCheckBoxes.get(checkBoxGrade);
			if (treeTableContent.getChildren(gradeItemId) == null) {
				treeTableContent.removeItem(gradeItemId);
			}
		}
	}

	private void removeEmptyStudents() {
		for (CheckBox checkBoxStudent : allStudentCheckBoxes.keySet()) {
			Object studentItemId = allStudentCheckBoxes.get(checkBoxStudent);
			if (treeTableContent.getChildren(studentItemId) == null) {
				treeTableContent.removeItem(studentItemId);
			}
		}
	}

	/*
	 * Notifies all registered observers that the selection changed by calling
	 * their update method.
	 */
	private void notifyObserver() {
		if (registeredObservers == null) {
			return;
		}

		for (StudentMaterialSelectorObserver observer : registeredObservers) {
			observer.update();
		}
	}
} public interface StudentMaterialSelectorObserver {
	/**
	 * This method is called whenever the StudentMaterialSelector gets updated.
	 * */
	public void update();
} public class LendingViewModel {

	public interface GenerateMaterialListGrades extends ActionHandler {};
	public interface SetBorrowedMaterialsReceived extends ActionHandler {};
	public interface DoManualLending extends ActionHandler {};
	
	public interface StudentsWithUnreceivedBorrowedMaterials extends State<Map<Grade, Map<Student, List<BorrowedMaterial>>>> {};
	public interface MaterialListGrades extends State<Map<Grade, Map<TeachingMaterial, Integer>>> {};
	public interface TeachingMaterials extends State<Collection<TeachingMaterial>> {};

	@ProvidesState(StudentsWithUnreceivedBorrowedMaterials.class)
	public State<Map<Grade, Map<Student, List<BorrowedMaterial>>>> studentsWithUnreceivedBorrowedMaterials = new BasicState<>(Map.class);
	
	@ProvidesState(MaterialListGrades.class)
	public State<Map<Grade, Map<TeachingMaterial, Integer>>> materialListGrades = new BasicState<>(Map.class);

	@ProvidesState(TeachingMaterials.class)
	public State<Collection<TeachingMaterial>> teachingMaterials = new BasicState<>(Collection.class);
	
	private DAO<Grade> daoGrade;
	private DAO<Student> daoStudent;
	private DAO<TeachingMaterial> daoTeachingMaterial;
	private DAO<BorrowedMaterial> daoBorrowedMaterial;
	private DAO<SchoolYear> daoSchoolYear; 
	
	private SchoolYear recentlyActiveSchoolYear;
	
	/**
	 * Constructor
	 * 
	 * @param daoStudent
	 * @param daoTeachingMaterial
	 * @param daoGrade
	 * @param daoBorrowedMaterial
	 * @param daoSchoolYear
	 */
	@Inject
	public LendingViewModel(DAO<Student> daoStudent, DAO<TeachingMaterial> daoTeachingMaterial, DAO<Grade> daoGrade, 
			DAO<BorrowedMaterial> daoBorrowedMaterial, DAO<SchoolYear> daoSchoolYear) {
		this.daoStudent = daoStudent;
		this.daoTeachingMaterial = daoTeachingMaterial;
		this.daoGrade = daoGrade;
		this.daoBorrowedMaterial = daoBorrowedMaterial;
		this.daoSchoolYear = daoSchoolYear;
	}
	
	@AfterVMBinding
	public void initialiseStates() {
		studentsWithUnreceivedBorrowedMaterials.set(new HashMap<Grade, Map<Student, List<BorrowedMaterial>>>());
		materialListGrades.set(new HashMap<Grade, Map<TeachingMaterial, Integer>>());
		teachingMaterials.set(new ArrayList<TeachingMaterial>());
	}
	
	public void refresh() {
		updateSchoolYear();
		updateTeachingMaterials();	
		updateAllStudentsBorrowedMaterials();
	}
	
	/**
	 * Generates "list" of all {@link TeachingMaterial}s required by the given {@link Grade}s.<br>
	 * The "list" is returned as {@code Map<Grade, Map<TeachingMaterial, Integer>>} in the state {@link MaterialListGrades}
	 * 
	 * @param selectedGrades {@link Set} of {@link Grade}s
	 */
	@HandlesAction(GenerateMaterialListGrades.class)
	public void generateMaterialListGrades(Set<Grade> selectedGrades) {
		Map<Grade, Map<TeachingMaterial, Integer>> materialList = new TreeMap<Grade, Map<TeachingMaterial, Integer>>();
		
		for (Grade grade : selectedGrades) {
			Map<TeachingMaterial, Integer> gradeMap = new TreeMap<TeachingMaterial, Integer>();
			
			for(Student student : daoStudent.findAllWithCriteria(Restrictions.eq("leavingSchool", false), Restrictions.eq("grade", grade))) {
				for(BorrowedMaterial borrowedMaterial : student.getUnreceivedBorrowedList()) {
					TeachingMaterial teachingMaterial = borrowedMaterial.getTeachingMaterial();
					if(gradeMap.containsKey(teachingMaterial)) {
						gradeMap.put(teachingMaterial, gradeMap.get(teachingMaterial) + 1);
					} else {
						gradeMap.put(teachingMaterial, 1);
					}
				}
			}
			
			materialList.put(grade, gradeMap);
		}
		
		materialListGrades.set(materialList);
	}
	
	/**
	 * Marks the given {@link BorrowedMaterial}s as {@code received}.
	 * 
	 * @param borrowedMaterials {@link Collection} of {@link BorrowedMaterial}s that should be marked {@code received} 
	 */
	@HandlesAction(SetBorrowedMaterialsReceived.class)
	public void setBorrowedMaterialsReceived(Collection<BorrowedMaterial> borrowedMaterials) {
		for (BorrowedMaterial borrowedMaterial : borrowedMaterials) {
			borrowedMaterial.setReceived(true);
		}
		daoBorrowedMaterial.update(borrowedMaterials);
		
		updateAllStudentsBorrowedMaterials();
	}
	
	/**
	 * Lends the given {@link TeachingMaterial} for the given {@link Student} until the given {@link Date}.
	 * 
	 * @param student {@link Student} to lend this...
	 * @param toLend {@link TeachingMaterial} ...
	 * @param borrowUntil until this {@link Date}
	 */
	@HandlesAction(DoManualLending.class)
	public void doManualLending(Student student, TeachingMaterial toLend, Date borrowUntil) {
		persistBorrowedMaterial(student, toLend, borrowUntil);
		updateAllStudentsBorrowedMaterials();
	}
	
	/**
	 * Updates the list of {@link BorrowedMaterial}s for all {@link Student}s
	 */
	private void updateAllStudentsBorrowedMaterials() {
		persistBorrowedMaterials(getNewTeachingMaterials(daoGrade.findAll()));
		updateUnreceivedBorrowedMaterialsState();
	}

	/**
	 * Updates the {@link State} {@link TeachingMaterials}
	 */
	private void updateTeachingMaterials() {
		teachingMaterials.set(daoTeachingMaterial.findAll());
	}
	
	/**
	 * 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();
	}
	
	/**
	 * Updates the {@link State} of all {@link Student}s unreceived {@link BorrowedMaterial}s.<br>
	 * Returned as {@code Map<Grade, Map<Student, List<BorrowedMaterial>>>} in {@link StudentsWithUnreceivedBorrowedMaterials}
	 * 
	 */
	private void updateUnreceivedBorrowedMaterialsState() {
		Map<Grade, Map<Student, List<BorrowedMaterial>>> unreceivedMap = new TreeMap<Grade, Map<Student, List<BorrowedMaterial>>>();
		
		for (Grade grade : daoGrade.findAll()) {
			Map<Student, List<BorrowedMaterial>> studentsWithUnreceivedBorrowedMaterials = new TreeMap<Student, List<BorrowedMaterial>>();
			for (Student student : grade.getStudents()) {
				if (student.hasUnreceivedBorrowedMaterials()) {
					List<BorrowedMaterial> unreceivedBorrowedList = student.getUnreceivedBorrowedList();
					Collections.sort(unreceivedBorrowedList);
					studentsWithUnreceivedBorrowedMaterials.put(student, unreceivedBorrowedList);
				}
			}
			
			if(!studentsWithUnreceivedBorrowedMaterials.isEmpty()) {
				unreceivedMap.put(grade, studentsWithUnreceivedBorrowedMaterials);
			}
		}

		studentsWithUnreceivedBorrowedMaterials.set(unreceivedMap);
	}
	
	/**
	 * Returns map of {@link TeachingMaterial}s that have to be lended by the {@link Student}s of the given {@link Grade}s.
	 * 
	 * @param grades
	 * @return {@link Map} of {@link Student}s and their {@link TeachingMaterial}s that have to be lended
	 */
	private Map<Student, List<TeachingMaterial>> getNewTeachingMaterials(List<Grade> grades) {
		Map<Student, List<TeachingMaterial>> studentsNewTeachingMaterialMap = new HashMap<Student, List<TeachingMaterial>>();

		Date today = new Date();
		
		//No TeachingMaterials to lend when not in an active SchoolYear
		if(recentlyActiveSchoolYear.getToDate().before(today)) {
			return studentsNewTeachingMaterialMap;
		}
		
		Term recentlyActiveTerm = recentlyActiveSchoolYear.getRecentlyActiveTerm();
		
		SimpleExpression restrictionValidFrom = Restrictions.le("validFrom", today);
		SimpleExpression restrictionFromTerm = Restrictions.le("fromTerm", recentlyActiveTerm);
		SimpleExpression restrictionToTerm = Restrictions.ge("toTerm", recentlyActiveTerm);
		LogicalExpression restrictionValidUntil = Restrictions.or(
				Restrictions.ge("validUntil", today), 
				Restrictions.isNull("validUntil"));
		
		for (Grade gradeEntity : grades) {
			int grade = gradeEntity.getGrade();
			Collection<TeachingMaterial> teachingMaterials = daoTeachingMaterial.findAllWithCriteria(
					Restrictions.and(
							Restrictions.le("fromGrade", grade)
							, Restrictions.ge("toGrade", grade)
							, restrictionValidFrom
							, restrictionFromTerm
							, restrictionToTerm
							, restrictionValidUntil
					));
			
			for (Student student : gradeEntity.getStudents()) {
				if (!student.isLeavingSchool()) {
					List<TeachingMaterial> teachingMaterialsOfStudent = new ArrayList<>(student.getTeachingMaterials());
					List<TeachingMaterial> toLend = new ArrayList<TeachingMaterial>();

					Set<Subject> studentsProfile = student.getProfile();
					for (TeachingMaterial teachingMaterial : teachingMaterials) {
						if (studentsProfile.containsAll(teachingMaterial.getProfile())
								&& !teachingMaterialsOfStudent.contains(teachingMaterial)) {
							toLend.add(teachingMaterial);
						}
					}
					
					studentsNewTeachingMaterialMap.put(student, toLend);
				}
			}
		}
		
		return studentsNewTeachingMaterialMap;
	}
	
	/**
	 * Persists the {@link TeachingMaterial}s for the {@link Student}s as {@link BorrowedMaterial}s in the database.
	 * 
	 * @param newTeachingMaterials {@link Map} of {@link Student}s and their new {@link TeachingMaterial}s
	 */
	private void persistBorrowedMaterials(Map<Student, List<TeachingMaterial>> newTeachingMaterials) {
		Collection<BorrowedMaterial> borrowedMaterials = new ArrayList<>();
		for(Student student : newTeachingMaterials.keySet()) {
			borrowedMaterials.addAll(buildBorrowedMaterials(student, newTeachingMaterials.get(student)));
		}
		
		daoBorrowedMaterial.insert(borrowedMaterials);
	}
	
	/**
	 * Builds a {@link Collection} of {@link BorrowedMaterial}s
	 * 
	 * @param student
	 * @param teachingMaterials
	 * @return {@link Collection} of {@link BorrowedMaterial}s
	 */
	private Collection<BorrowedMaterial> buildBorrowedMaterials(Student student, List<TeachingMaterial> teachingMaterials) {
		Date borrowFrom = new Date();
		Collection<BorrowedMaterial> borrowedMaterials = new ArrayList<>();
		for (TeachingMaterial teachingMaterial : teachingMaterials) {
			borrowedMaterials.add(new BorrowedMaterial.Builder(student, teachingMaterial, borrowFrom).build());
		}

		return borrowedMaterials;
	}
	
	/**
	 * Persists a single {@link TeachingMaterial} as {@link BorrowedMaterial}.<br>
	 * Is needed for manual lending.
	 * 
	 * @param student
	 * @param teachingMaterial
	 * @param borrowUntil
	 */
	private void persistBorrowedMaterial(Student student, TeachingMaterial teachingMaterial, Date borrowUntil) {
		daoBorrowedMaterial.insert(new BorrowedMaterial.Builder(student, teachingMaterial, new Date()).borrowUntil(borrowUntil).build());
	}
} public interface MaterialListGrades extends State<Map<Grade, Map<TeachingMaterial, Integer>>> {}; public interface StudentsWithUnreceivedBorrowedMaterials extends State<Map<Grade, Map<Student, List<BorrowedMaterial>>>> {}; public interface TeachingMaterials extends State<Collection<TeachingMaterial>> {}; public class StudentInformationViewModel {
	private final static Logger LOG = LoggerFactory.getLogger(StudentInformationView.class);

	public interface PersistStudents extends ActionHandler {}
	public interface Students extends State<Collection<Student>> {}

	@ProvidesState(Students.class)
	public State<Collection<Student>> students = new BasicState<>(Collection.class);

	private EventBus eventBus;
	private DAO<Student> daoStudent;
	private DAO<Grade> daoGrade;
	private DAO<BorrowedMaterial> daoBorrowedMaterial;

	/**
	 * Constructor
	 * 
	 * @param daoTeachingMaterial
	 *            DAO implementation to access TeachingMaterial entities
	 */
	@Inject
	public StudentInformationViewModel(DAO<Student> daoStudent,	DAO<Grade> daoGrade, DAO<BorrowedMaterial> daoBorrowedMaterial, 
			EventBus eventBus) {
		this.daoStudent = daoStudent;
		this.daoGrade = daoGrade;
		this.daoBorrowedMaterial = daoBorrowedMaterial;
		this.eventBus = eventBus;
	}

	@AfterVMBinding
	public void initialiseStates() {
		students.set(new ArrayList<Student>());
	}
	
	public void refresh() {
		updateStudents();
	}

	private void updateStudents() {
		students.set(daoStudent.findAll());
	}

	/**
	 * Imports the given {@link List} of {@link Student}s.<br>
	 * If {@code fullImport} is set, all {@link Student}s in the database will be deleted which aren't included in the {@link List}.
	 * Existing {@link Student}s will be updated.<br>
	 * If {@code fullImport} is not set, new {@link Student}s will be persisted and the existing {@link Student}s will be updated.  
	 * 
	 * @param csvStudents
	 * @param fullImport
	 */
	@HandlesAction(PersistStudents.class)
	public void persistStudents(List<Student> csvStudents, boolean fullImport) {

		int deletedStudents = 0;
		int changedStudents = 0;

		if (fullImport) {
			deletedStudents = deleteReturnedMarkUnreturned(csvStudents);
		}
		
		changedStudents = updateInsertStudents(csvStudents);
		
		eventBus.post(new MessageEvent("Import erfolgreich", 
				"Es wurden " + changedStudents + " Schüler hinzugefügt oder aktualisiert und " + deletedStudents + " gelöscht.",
				Type.TRAYINFO));
		
		updateStudents();
	}

	/**
	 * Receives the OutputStream provided by an upload.
	 * 
	 * @param outputStream
	 * @param fullImport 
	 */
	public void receiveUploadByteOutputStream(ByteArrayOutputStream outputStream, boolean fullImport) {
		try {
			String encoding = checkEncoding(outputStream);
			CSVReader reader;
			LOG.warn(encoding);
			if (encoding != null) {
				reader = new CSVReader(new InputStreamReader(new ByteArrayInputStream(outputStream.toByteArray()), encoding), ';', '\'', 0);
				
			}
			else{
				reader = new CSVReader(new InputStreamReader(new ByteArrayInputStream(outputStream.toByteArray())), ';', '\'', 0);
			}
			List<Student> students = CSVHandler.createStudentObjectsFromCSV(reader);
			persistStudents(students, fullImport);
		} catch (UnsupportedOperationException uoe) {
			eventBus.post(new MessageEvent("Import nicht möglich.", uoe.getMessage(), Type.ERROR));
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Checks the encoding of the given {@link ByteArrayOutputStream}.
	 * 
	 * @param outputStream
	 * @return The encoding of the {@link ByteArrayOutputStream}. If no encoding can be deteced: <code>null</code>. 
	 */
	private String checkEncoding(ByteArrayOutputStream outputStream) {
		byte[] buf = new byte[4096];
		ByteArrayInputStream bais;
		try {
			bais = new ByteArrayInputStream(outputStream.toByteArray());

			// (1)
			UniversalDetector detector = new UniversalDetector(null);

			// (2)
			int nread;
			while ((nread = bais.read(buf)) > 0 && !detector.isDone()) {
				detector.handleData(buf, 0, nread);
			}
			// (3)
			detector.dataEnd();

			// (4)
			String encoding = detector.getDetectedCharset();

			// (5)
			detector.reset();
			bais.close();
			return encoding;
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * @return {@link List} of {@link Student}s which have {@code received} {@link BorrowedMaterial}s with {@code isReturned == false}
	 */
	private List<Student> getStudentsWithUnreturnedBorrowedMaterials() {
		Collection<BorrowedMaterial> unreturnedBorrowedMaterials = this.daoBorrowedMaterial.findAllWithCriteria(
				Restrictions.eq("received", true),
				Restrictions.isNull("returnDate"));
		
		List<Student> studentsWithUnreturnedBorrowedMaterials = new ArrayList<>();
		for (BorrowedMaterial borrowedMaterial : unreturnedBorrowedMaterials) {
			studentsWithUnreturnedBorrowedMaterials.add(borrowedMaterial.getStudent());
		}
		
		return studentsWithUnreturnedBorrowedMaterials;
	}
	
	/**
	 * Deletes the given {@link Student}s if they don't have any {@code unreturned} {@link BorrowedMaterial}s left.<br>
	 * A {@link Student} will be marked as {@code leavingSchool} if he has {@code unreturned} {@link BorrowedMaterial}s.
	 * 
	 * @param csvStudents
	 * @return Number of deleted or updated {@link Student}s
	 */
	private int deleteReturnedMarkUnreturned(List<Student> csvStudents) {
		List<Student> toDelete = new ArrayList<>(daoStudent.findAll());
		toDelete.removeAll(csvStudents);
		
		List<Student> studentsWithUnreturnedBorrowedMaterials = getStudentsWithUnreturnedBorrowedMaterials();
		studentsWithUnreturnedBorrowedMaterials.retainAll(toDelete);
		for (Student student : studentsWithUnreturnedBorrowedMaterials) {
			student.setLeavingSchool(true);
		}
		daoStudent.update(studentsWithUnreturnedBorrowedMaterials);
		
		toDelete.removeAll(studentsWithUnreturnedBorrowedMaterials);
		daoStudent.delete(toDelete);
		return toDelete.size();
	}
	
	/**
	 * Checks if one of the given {@link Student}s is already existing in the database.
	 * In this case, the {@link Student} will be updated, otherwise it will be persisted. 
	 * 
	 * @param csvStudents
	 * @return Number of updated or inserted {@link Student}s
	 */
	private int updateInsertStudents(List<Student> csvStudents) {
		int changedStudents = 0;
		for(Student student : csvStudents) {
			Grade grade = daoGrade.findSingleWithCriteria(
					Restrictions.and(
							Restrictions.like("grade", student.getGrade().getGrade()),
							Restrictions.like("suffix", student.getGrade().getSuffix())
						));

			if (grade == null) {
				grade = daoGrade.insert(student.getGrade());
			}

			student.setGrade(grade);
			
			Student existingStudent = daoStudent.find(student.getId());
			if(existingStudent != null) {
				existingStudent.copyDataFrom(student);
				daoStudent.update(existingStudent);
			} else {	
				daoStudent.insert(student);
			}
			
			changedStudents++;
		}
		
		return changedStudents;
	}
} public interface Students extends State<Collection<Student>> {}
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.MenuBar;
import com.vaadin.ui.MenuBar.Command;
import com.vaadin.ui.MenuBar.MenuItem;
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.model.entity.TeachingMaterial;
import de.dhbw.humbuch.util.PDFClassList;
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.LendingViewModel;
import de.dhbw.humbuch.viewmodel.LendingViewModel.MaterialListGrades;
import de.dhbw.humbuch.viewmodel.LendingViewModel.StudentsWithUnreceivedBorrowedMaterials;
import de.dhbw.humbuch.viewmodel.LendingViewModel.TeachingMaterials;
import de.dhbw.humbuch.viewmodel.StudentInformationViewModel;
import de.dhbw.humbuch.viewmodel.StudentInformationViewModel.Students;
package de.dhbw.humbuch.view;





/**
 * This view displays the Lendingscreen. It holds a horizontal headerbar
 * containing actions and a StudentMaterialSelector with all information about
 * the lent books of students. It is used to lent books and print student and
 * class lists.
 * 
 * @author Henning Muszynski
 * */
public class LendingView extends VerticalLayout implements View,
		ViewInformation, StudentMaterialSelectorObserver {

	private static final long serialVersionUID = -6400075534193735694L;

	private final static Logger LOG = LoggerFactory
			.getLogger(LendingView.class);

	private static final String TITLE = "Ausleihe";
	private static final String MANUAL_LENDING_TITLE = "Manuelle Ausleihe";
	private static final String SAVE_SELECTED_LENDING = "Material erhalten";
	private static final String MANUAL_LENDING = "Manuelle Ausleihe";
	private static final String MENU_PRINT = "Listen drucken";
	private static final String MENU_ITEM_STUDENT_LIST = "Schüler Liste";
	private static final String MENU_ITEM_CLASS_LIST = "Klassen Liste";
	private static final String CLASS_LIST_PDF = "KlassenListe.pdf";
	private static final String CLASS_LIST_WINDOW_TITLE = "Klassen Liste";
	private static final String STUDENT_LIST_PDF = "SchuelerAusleihListe.pdf";
	private static final String STUDENT_LIST_WINDOW_TITLE = "Schüler Ausleih Liste";
	private static final String FILTER_STUDENT = "Schüler filtern";
	private static final String MSG_CONFIRM_RECEIVE = "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 buttonSaveSelectedData;
	private Button buttonManualLending;
	private MenuBar menuBarPrinting;
	private MenuItem menuItemPrinting;
	private MenuItem subMenuItemClassList;
	private MenuItem subMenuItemStudentList;
	private Command menuCommandClassList;
	private Command menuCommandStudentList;
	private LendingViewModel lendingViewModel;