package org.yacos.core.users;

import java.io.Serializable;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;

/**
 * A unique User of the system.
 * The user is registered globally in the application and would be able to get different roles for
 * different conferences.
 * @author christiancorsano
 *
 */
@Entity
@Table(
		name="user",
		uniqueConstraints=@UniqueConstraint(columnNames={"login","email"}))
public class User implements Serializable{
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	/**
	 * Unique login of the user
	 * Is used to identify the user, should be unique and can't be modified
	 */
	@Id
	@Column(name = "login")
	private String login;
	/**
	 * Password : used for authentication purpose only, is only writable
	 * Should be stored Hashed using SHA-256 algorithm.
	 * Thus the size should always be 64 characters long
	 */
	@Column(name="password",length=64)
	private String password;
	/**
	 * First Name of the user
	 */
	private String firstName;
	/**
	 * Last name of the user
	 */
	private String lastName;
	/**
	 * Organization or lab the user belongs to
	 */
	private String organization;
	/**
	 * Active email (has to be used frequently) of the user
	 */
	private String email;
	
	@ManyToMany(targetEntity=Speciality.class)
	@JoinTable(
		name="speciality_user_map",
		joinColumns=@JoinColumn(name="specialityId"),
		inverseJoinColumns=@JoinColumn(name="userId")
	)
	private List<Speciality> specialities;
	
	/**
	 * Roles for this user
	 */
	//@OneToMany(targetEntity=Role.class,mappedBy="user",fetch=FetchType.EAGER,cascade=CascadeType.ALL)
	@OneToMany(cascade=CascadeType.ALL, targetEntity=Role.class,mappedBy="user")
	private List<Role> roles;
	
	/**
	 * How many conference can this user create ?
	 */
	@Column(nullable=false)
	private Integer conferenceCreationTokens;
	
	/*@OneToMany(cascade=CascadeType.ALL, targetEntity=Delegation.class, mappedBy="user")
	private List<Delegation> delegation;
	*/
	public User(String login, String password, String firstName,
			String lastName, String organization, String email) {
		this();
		this.login = login;
		this.password = password;
		this.firstName = firstName;
		this.lastName = lastName;
		this.organization = organization;
		this.email = email;	
		this.conferenceCreationTokens = 0;
	}

	public User() {
		this.roles = new ArrayList<Role>();
		this.conferenceCreationTokens = 0;
	}

	@Id
	@Column(name = "login")
	public String getLogin() {
		return login;
	}
	
	public void setPassword(String password) {
		this.password = password;
	}

	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 getOrganization() {
		return organization;
	}

	public void setOrganization(String organization) {
		this.organization = organization;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email.toLowerCase();
	}
	
	public void addSpeciality(Speciality spe){
		this.specialities.add(spe);
	}
	
	
	public void removeSpeciality(Speciality spe){
		this.specialities.remove(spe);
	}
	
	public void addRole(Role role){
		roles.add(role);
	}
	
	/*public void addRoleForConference(Role.RoleType roleType, Conference conference){
		if(! hasRoleForConference(roleType,conference)){
			
		} else {
			
		}
	}*/

	//@OneToMany(targetEntity=Role.class,mappedBy="user",fetch=FetchType.EAGER)
	@OneToMany(cascade=CascadeType.ALL, targetEntity=Role.class,mappedBy="user")
	public List<Role> getRoles(){
		return roles;
	}

	/**
	 * @param roles the roles to set
	 */
	//@OneToMany(targetEntity=Role.class,mappedBy="user",fetch=FetchType.EAGER)
	@OneToMany(cascade=CascadeType.ALL, targetEntity=Role.class,mappedBy="user")
	public void setRoles(List<Role> roles) {
		this.roles = roles;
	}
	
	/**
	 * @return the password
	 */
	public String getPassword() {
		return password;
	}

	/**
	 * @param login the login to set
	 */
	@Column(name = "login")
	public void setLogin(String login) {
		this.login = login;
	}
	
	public static String hashPassword(String password){
		try {
			MessageDigest algorithm = MessageDigest.getInstance("SHA-256");
			algorithm.update(password.getBytes());
			byte digest[] = algorithm.digest();
			StringBuffer hexString = new StringBuffer();

			// String to hex conversion
			for (int i = 0; i < digest.length; i++){
				String hex = Integer.toHexString(0xFF & digest[i]);
				if (hex.length() == 1)
				{
					hexString.append('0');
				}
				hexString.append(hex);
			}
			
			return hexString.toString();
		} catch (NoSuchAlgorithmException e) {
			// Should never happen, SHA-256 support is built in Java
		}
		return null;
	}

	@ManyToMany(targetEntity=Speciality.class)
	@JoinTable(
		name="speciality_user_map",
		joinColumns=@JoinColumn(name="specialityId"),
		inverseJoinColumns=@JoinColumn(name="userId")
	)
	public List<Speciality> getSpecialities() {
		return specialities;
	}

	@ManyToMany(targetEntity=Speciality.class)
	@JoinTable(
		name="speciality_user_map",
		joinColumns=@JoinColumn(name="specialityId"),
		inverseJoinColumns=@JoinColumn(name="userId")
	)
	public void setSpecialities(List<Speciality> specialities) {
		this.specialities = specialities;
	}
	
	/**
	 * How many conferences can this user create ?
	 * @return The number of conferences this user can create
	 */
	@Column(nullable=false)
	public Integer getConferenceCreationTokens() {
		return conferenceCreationTokens;
	}
	
	/**
	 * Sets the number of conferences this user can create
	 * @param conferenceCreationTokens The number of conferences this user can create
	 */
	@Column(nullable=false)
	public void setConferenceCreationTokens(Integer conferenceCreationTokens) {
		this.conferenceCreationTokens = conferenceCreationTokens;
	}
/*
	@OneToMany(cascade=CascadeType.ALL, targetEntity=Delegation.class, mappedBy="user")
	public List<Delegation> getDelegation() {
		return delegation;
	}

	@OneToMany(cascade=CascadeType.ALL, targetEntity=Delegation.class, mappedBy="user")
	public void setDelegation(List<Delegation> delegation) {
		this.delegation = delegation;
	}
	*/
	
	@Override
	public boolean equals(Object obj){
		if (this.getClass() == obj.getClass())
			return this.login.equals(((User)obj).getLogin());
		else return false;
		
	}
}
