package org.richfaces.realworld.domain;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;

import org.hibernate.validator.Length;
import org.hibernate.validator.NotEmpty;
import org.hibernate.validator.NotNull;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;

@Entity
@Name("shelf")
@Scope(ScopeType.CONVERSATION)
@Table(name = "shelves")
//TODO nick - rename table to "shelves"
//TODO nick - should we store this in conversation?
public class Shelf implements Serializable {

    private static final long serialVersionUID = -7042878411608396483L;

    @Id
    @GeneratedValue
    @Column(name = "SHELF_ID")
    private Long id = null;

    @Column(length = 255, nullable = false)
    @NotNull
    @NotEmpty
    @Length(min = 3, max = 50)
    private String name;

    @Column(length = 1024)
    private String description;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "SHELF_USER_ID", referencedColumnName = "USER_ID")
    private User owner;

    @OneToMany(mappedBy = "shelf", cascade = {CascadeType.ALL})
    @org.hibernate.annotations.OrderBy(clause = "NAME asc")
    private List<Album> albums = new ArrayList<Album>();
    @Transient
    private List<Image> unvisitedImages = new ArrayList<Image>();
    @NotNull
    private boolean shared;

    @Temporal(TemporalType.TIMESTAMP)
    private Date created;

    /**
     * Getter for property id
     *
     * @return id of album
     */
    public Long getId() {
        return id;
    }

    /**
     * Getter for property name
     *
     * @return name of album
     */
    public String getName() {
        return name;
    }

    /**
     * Setter for property name
     *
     * @param name -
     *             name of album
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * Getter for property description
     *
     * @return description of album
     */
    public String getDescription() {
        return description;
    }

    /**
     * Setter for property description
     *
     * @param description -
     *                    description of album
     */
    public void setDescription(String description) {
        this.description = description;
    }

    public List<Album> getAlbums() {
        return albums;
    }

    public User getOwner() {
        return owner;
    }

    public void setOwner(User owner) {
        this.owner = owner;
    }

    public void addAlbum(Album album) {
        if (album == null)
            throw new IllegalArgumentException("Null child!");
        if (album.getShelf() != null){
        	//TODO nick - use removeChildAlbum()
            album.getShelf().removeChildAlbum(album);
        }	
        album.setShelf(this);
        albums.add(album);
    }

    public void removeChildAlbum(Album album) {
        if (album == null)
            throw new IllegalArgumentException("Null child!");
		//TODO nick - check that shelf is "this"
        if(album.getShelf().equals(this)){
        	album.setShelf(null);
            albums.remove(album);
        }else{
        	throw new IllegalArgumentException("This Shelf not contain this album!");
        }
    }

    public int getCountAlbums() {
        return this.getAlbums() != null ? this.getAlbums().size() : 0;
    }

    public int getCountImages() {
        int result = 0;
        if (getCountAlbums() > 0) {
            for (Album album : getAlbums()) {
                result += album.getCountImages();
            }
        }
        return result;
    }

    /**
     * @return the created
     */
    public Date getCreated() {
        return created;
    }

    /**
     * @param created the created to set
     */
    public void setCreated(Date created) {
        this.created = created;
    }

    public boolean isShared() {
        return shared;
    }

    public void setShared(boolean shared) {
        this.shared = shared;
    }

    public Long getCountUnvisitedImages() {
        return new Long(unvisitedImages.size());
    }

    public List<Image> getUnvisitedImages() {
        return unvisitedImages;
    }

    public void setUnvisitedImages(List<Image> unvisitedImages) {
        this.unvisitedImages = unvisitedImages;
    }

    public void visitImage(Image image) {
        if (unvisitedImages.contains(image)) {
            unvisitedImages.remove(image);
        }
    }

    public Album getFirstAlbum() {
		//TODO nick - use random album for cover?
        if (this.albums.size() > 0) {
            return this.albums.get(0);
        }
        return null;
    }

    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Shelf shelf = (Shelf) o;

        if (id != null ? !id.equals(shelf.id) : shelf.id != null) return false;
        if (owner != null ? !owner.equals(shelf.owner) : shelf.owner != null) return false;
        if (!name.equals(shelf.name)) return false;

        return true;
    }

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