/**
 * License Agreement.
 *
 *  JBoss RichFaces - Ajax4jsf Component Library
 *
 * Copyright (C) 2007  Exadel, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1 as published by the Free Software Foundation.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 */
package org.richfaces.realworld.domain;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
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.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
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.Role;

/**
 * Class for representing Album Entity EJB3 Entity Bean
 * 
 * @author Andrey Markhel
 */
@NamedQueries(
		{ 
			@NamedQuery(name = "album-byAlbumNameAndLogin", query = "from Album a where a.name=:albumName and a.owner.login=:login") 
			}
		)
@Entity
@Name("album")
@Table(name = "albums")
@Role(name = "selectedAlbum", scope = ScopeType.CONVERSATION)
public class Album implements Serializable {

	private static final long serialVersionUID = -7042878411608396483L;

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

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

	@Column(length = 1024)
	private String description;

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

	@NotNull
	private boolean shared;

	@Transient
	private String changedName;

	@OneToMany(cascade = CascadeType.ALL, mappedBy = "album")
	@org.hibernate.annotations.Fetch(org.hibernate.annotations.FetchMode.SUBSELECT)
	private List<Image> images = new ArrayList<Image>();

	@ManyToMany
	@JoinTable(name = "SHARED_ALBUMS", joinColumns = @JoinColumn(name = "ALBUM_ID"), inverseJoinColumns = @JoinColumn(name = "USER_ID"))
	private List<User> sharedOwners = new ArrayList<User>();

	@OneToMany(mappedBy = "parent", cascade = { CascadeType.ALL })
	@org.hibernate.annotations.OrderBy(clause = "NAME asc")
	private List<Album> childAlbums = new ArrayList<Album>();

	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(name = "PARENT_ALBUM_ID", nullable = true)
	@org.hibernate.annotations.ForeignKey(name = "FK_ALBUM_PARENT_ID")
	private Album parent;

	// ********************** Accessor Methods ********************** //

	public List<Album> getChildAlbums() {
		return childAlbums;
	}

	public void addChildAlbum(Album album) {
		if (album == null)
			throw new IllegalArgumentException("Null child!");
		if (album.getParent() != null)
			album.getParent().getChildAlbums().remove(album);
		album.setParent(this);
		childAlbums.add(album);
	}

	public void removeChildAlbum(Album album) {
		if (album == null)
			throw new IllegalArgumentException("Null child!");
		album.setParent(null);
		childAlbums.remove(album);
	}

	public Album getParent() {
		return parent;
	}

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

	public String getAlbumPathFromParents(Album album,
			String delimiter, boolean includeSelf) {
		return this.getAlbumPathFromParents(album, new ArrayList<String>(), delimiter, includeSelf);
	}
	
	public String getAlbumPathFromParents(Album album, List<String> list,
			String delimiter, boolean includeSelf) {
		if (album.getParent() == null) {
			//If this album haven't parent - it is root, so add them first
			list.add(album.getName() + delimiter);
			//If it's method invoke on root album returned value will be dependent on flag
			return includeSelf ? album.getName() + delimiter : "";
		} else {
			//recursive call
			album.getParent().getAlbumPathFromParents(album.getParent(), list,
					delimiter, includeSelf);
		}
		if(includeSelf){
			list.add(this.getName() + delimiter);
		}
		String path = "";
		//Create string representation of path
		for (int i = 0; i < list.size(); i++) {
			path += list.get(i);
		}
		return path;
	}

	public List<Album> getArrayOfParents() {
		return getArrayOfParents(new ArrayList<Album>());
	}

	public List<Album> getArrayOfParents(List<Album> albums) {
		if (this.getParent() == null) {
			Collections.reverse(albums);
			return albums;
		} else {
			albums.add(this.getParent());
			this.getParent().getArrayOfParents(albums);
		}
		return albums;
	}

	/**
	 * 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;
	}

	// ********************** Business Methods ********************** //

	/**
	 * This method add image to collection of images of current album
	 * 
	 * @param image -
	 *            image to add
	 */
	public void addImage(Image image) {
		if (image == null) {
			throw new IllegalArgumentException("Null image!");
		}
		if (image.getAlbum() != null && !this.equals(image.getAlbum())) {
			image.getAlbum().getImages().remove(image);
		}
		image.setAlbum(this);
		images.add(image);
	}

	/**
	 * This method remove image from collection of images of album
	 * 
	 * @param image -
	 *            image to remove
	 */
	public void removeImage(Image image) {
		if (image == null) {
			throw new IllegalArgumentException("Null image");
		}
		image.setAlbum(null);
		images.remove(image);
	}

	/**
	 * This method return count of images of current album
	 * 
	 * @return count of images of this album
	 */
	public int getCountImages() {
		return this.getImages() != null ? this.getImages().size() : 0;

	}

	public boolean isShared() {
		return shared;
	}

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

	public User getOwner() {
		return owner;
	}

	public List<Image> getImages() {
		return images;
	}

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

	public String getChangedName() {
		if (changedName != null) {
			return changedName;
		}
		return this.getName();
	}

	public void setChangedName(String changedName) {
		this.changedName = changedName;
	}

	public List<User> getSharedOwners() {
		return sharedOwners;
	}

	public void setSharedOwners(List<User> sharedOwners) {
		this.sharedOwners = sharedOwners;
	}

	public int getIndex(Image image) {
		for (int i = 0; i < this.images.size(); i++) {
			if (this.images.get(i).equals(image)) {
				return i;
			}
		}
		return -1;
	}
}
