package org.hibernate.auction.model;

import java.io.Serializable;
import java.util.*;

/**
 * Kategorie mogą zawierać podkategorie i przedmioty.
 * <p>
 * Kategorie można zagnieżdżać, co reprezentuje dwukierunkowa asocjacja jeden-do-wielu,
 * która odnosi się do rodzica i potomka. Każda kategoria może mieć 
 * wiele przedmiotów (przydmiot może znajdować się w wielu kategoriach).
 * Jest to prawdziwy związek typu wiele-do-wielu.
 * <p>
 * Opcjonalną klasę <tt>CategorizedItem</tt> można zastosować, jeśli
 * łącze między kategorią i przedmiotem powinno zawierać dodatkowe informacje.
 * Kolekcja <tt>items</tt> będzie wtedy odwzorowana na kolekcję obiektów zależnych
 * w odwzorowaniu dla <tt>Category</tt>.
 *
 * @see Item
 * @see CategorizedItem
 * @author Christian Bauer <christian@hibernate.org>
 */
public class Category implements Serializable, Comparable {

	private Long id = null;
	private int version;
	private String name;
	private Category parentCategory;
	private Set childCategories = new HashSet();
	private Set categorizedItems = new HashSet();
	private Date created = new Date();

	/**
	 * Konstruktor bezparametrowy dla narzędzi JavaBean.
	 */
	Category() {}

	/**
	 * Pełny konstruktor.
	 */
	public Category(String name, Category parentCategory, Set childCategories, Set categorizedItems) {
		this.name = name;
		this.parentCategory = parentCategory;
		this.childCategories = childCategories;
		this.categorizedItems = categorizedItems;
	}

	/**
	 * Prosty konstruktor.
	 */
	public Category(String name) {
		this.name = name;
	}

	// ********************** Metody dostępowe ********************** //

	public Long getId() { return id; }

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

	public Category getParentCategory() { return parentCategory; }
	public void setParentCategory(Category parentCategory) { this.parentCategory = parentCategory; }

	public Set getChildCategories() { return childCategories; }
	public void addChildCategory(Category category) {
		if (category == null)
			throw new IllegalArgumentException("Nie można dodać Category o wartości null jako potomka.");
		// Usuń ze starej kategorii nadrzędnej.
		if (category.getParentCategory() != null)
			category.getParentCategory().getChildCategories()
										 .remove(category);
		// Ustaw rodzica u dziecka.
		category.setParentCategory(this);
		// Ustaw dziecko u rodzica.
		this.getChildCategories().add(category);
	}

	public Set getCategorizedItems() { return categorizedItems; }
	public void addCategorizedItem(CategorizedItem catItem) {
		if (catItem == null)
			throw new IllegalArgumentException("Nie można dodać CategorizedItem o wartości null.");
		this.getCategorizedItems().add(catItem);
	}

	public Date getCreated() { return created; }

	// ********************** Inne typowe metody ********************** //

	public boolean equals(Object o) {
		if (this == o) return true;
		if (!(o instanceof Category)) return false;

		final Category category = (Category) o;

		if (created != null ? !created.equals(category.created) : category.created != null) return false;
		if (name != null ? !name.equals(category.name) : category.name != null) return false;

		return true;
	}

	public int hashCode() {
		int result;
		result = (name != null ? name.hashCode() : 0);
		result = 29 * result + (created != null ? created.hashCode() : 0);
		return result;
	}

	public String toString() {
		return  "Category ('" + getId() + "'), " +
				"Nazwa: '" + getName() + "'";
	}

	public int compareTo(Object o) {
		if (o instanceof Category) {
			return this.getName().compareTo( ((Category)o).getName() );
		}
		return 0;
	}

	// ********************** Metody biznesowe ********************** //

}
