package org.hibernate.auction.model;

import org.hibernate.auction.exceptions.BusinessException;

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

/**
 * Użytkownik systemu aukcyjnego CaveatEmptor.
 * <p>
 * Klasa reprezentuje końcowego użytkownika systemu CaveatEmptor.
 * Asocjacje: <tt>Set</tt> obiektów <tt>Item</tt>, czyli sprzedawanych
 * przedmiotów, <tt>Set</tt> obiektów <tt>Bid</tt>, czyli złożonych ofert i
 * komponent <tt>Address</tt>. Dodatkowy <tt>Set</tt> Obiektów
 * <tt>BuyNow</tt> dotyczy od razu zakupionych przedmiotów.
 * <p>
 * Klasa <tt>billingDetails</tt> służy do obliczania opłat za korzystanie
 * z systemu przez użytkownika. Właściwości <tt>username</tt>
 * i <tt>password</tt> służą do uwierzytelniania. Wartość
 * <tt>ranking</tt> zostaje zwiększona po każdej udanej transakcji kupna lub sprzedaży,
 * ale może też zostać ręcznie zmniejszona lub zwiększona przez administratora.
 *
 *
 * @author Christian Bauer <christian@hibernate.org>
 */
public class User implements Serializable, Comparable {

	private Long id = null;
	private int version;
	private String firstname;
	private String lastname;
	private String username;
	private String password;
	private String email;
	private int ranking = 0;
	private Date created = new Date();
	private Address address;
	private Set items = new HashSet();
	private Set billingDetails = new HashSet();
	private BillingDetails defaultBillingDetails;
	private boolean admin = false;

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

	/**
	 * Pełny konstruktor.
	 */
	public User(String firstname, String lastname, String username,
				String password, String email,
				Address address, Set items,
				Set billingDetails) {
		this.firstname = firstname;
		this.lastname = lastname;
		this.username = username;
		this.password = password;
		this.email = email;
		this.address = address;
		this.items = items;
		this.billingDetails = billingDetails;
	}

	/**
	 * Prosty konstruktor.
	 */
	public User(String firstname, String lastname,
	            String username, String password, String email) {
		this.firstname = firstname;
		this.lastname = lastname;
		this.username = username;
		this.password = password;
		this.email = email;
	}

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

	public Long getId() { return id; }

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

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

	public String getUsername() { return username; }

	public String getPassword() { return password; }
	public void setPassword(String password) { this.password = password; }

	public String getEmail() { return email; }
	public void setEmail(String email) { this.email = email; }

	public int getRanking() { return ranking; }
	public void setRanking(int ranking) { this.ranking = ranking; }

	public Address getAddress() { return address; }
	public void setAddress(Address address) { this.address = address; }

	public Set getItems() { return items; }
	public void addItem(Item item) {
		if (item == null)
			throw new IllegalArgumentException("Nie mogę dodać Item o wartości null.");
		this.getItems().add(item);
	}

	public Set getBillingDetails() { return billingDetails; }
	/**
	  * Dodaje <tt>BillingDetails</tt> do zbioru.
	  * <p>
	  * Metoda sprawdza, czy jest to jedyny sposób płatności.
	  * Jeśli tak, ustawia go jako domyślny,
	  *
	  * @param billingDetails
	  */
	 public void addBillingDetails(BillingDetails billingDetails) {
		 if (billingDetails == null)
			 throw new IllegalArgumentException("Nie mogę dodać BillingDetails o wartości null.");
		 this.getBillingDetails().add(billingDetails);

		 if (getBillingDetails().size() == 1) {
			 setDefaultBillingDetails(billingDetails);
		 }
	}
	/**
 	 * Usuwa <tt>BillingDetails</tt> ze zbioru.
	 * <p>
	 * Metoda sprawdza, czy usuwany sposób płatności jest domyślny i
	 * i zgłasza wyjątek BusinessException, jeśli nie można wybrać innego
	 * domyślnego sposobu płatności. Nie jest to najlepszy sposób obsługi zdarzenia.
	 *
	 * @param billingDetails
	 * @throws BusinessException
	 */
	public void removeBillingDetails(BillingDetails billingDetails)
		throws BusinessException {
		if (billingDetails == null)
			throw new IllegalArgumentException("Nie mogę usunąć BillingDetails o wartości null.");

		if (getBillingDetails().size() >= 2) {
			getBillingDetails().remove(billingDetails);
			setDefaultBillingDetails((BillingDetails)getBillingDetails().iterator().next());
		} else {
			throw new BusinessException("Najpierw określ domyślny sposób płatności.");
		}
	}

	public BillingDetails getDefaultBillingDetails() { return defaultBillingDetails; }
	public void setDefaultBillingDetails(BillingDetails defaultBillingDetails) {
		this.defaultBillingDetails = defaultBillingDetails;
	}

	public Date getCreated() { return created; }

	public boolean isAdmin() { return admin; }
	public void setAdmin(boolean admin) { this.admin = admin; }

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

	public boolean equals(Object o) {
		if (this == o) return true;
		if (!(o instanceof User)) return false;
		final User user = (User) o;
		if (!username.equals(user.username)) return false;
		return true;
	}

	public int hashCode() {
		return username.hashCode();
	}

	public String toString() {
		return  "User ('" + getId() + "'), " +
				"Użytkownik: '" + getUsername() + "'";
	}

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

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

	public void increaseRanking() {
		setRanking(getRanking() + 1);
	}

}
