/**
 * 
 */
package org.yacos.core.users;


import java.util.List;
import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.NonUniqueResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.transaction.UserTransaction;
import org.yacos.core.article.Article;
import org.yacos.core.conferences.Conference;
import org.yacos.core.exceptions.PKAlreadyUsedException;
import org.yacos.core.exceptions.UserEMailAlreadyExistsException;
import org.yacos.core.system.InvitationToken;
import org.yacos.core.system.InvitationTokenPK;
import org.yacos.core.users.Role.RoleType;

/**
 * @author christiancorsano
 *
 */
@Stateless
@TransactionManagement(TransactionManagementType.BEAN)
public class UserManagerBean implements IUserManager{
	@PersistenceContext
	EntityManager em;
	@Resource
	private UserTransaction ut;

	@SuppressWarnings("unchecked")
	public List<User> getUsers(){
		Query query = em.createQuery("from User");
		return query.getResultList();
	}

	/**
	 * Adds a user into the system 
	 * throw PKAlreadyUsedException if login exist in the DB 
	 * @param user
	 */
	public User addUser(String login, String password, String firstName,
			String lastName, String organization, String email)
	throws PKAlreadyUsedException,UserEMailAlreadyExistsException{

		if (!this.exists(login)){
			if(this.getByEmail(email) != null){
				throw new UserEMailAlreadyExistsException(email);
			}
			User user = null;
			try{
				ut.begin();
				user = new User(login, password, firstName, lastName, organization, email);
				em.persist(user);
				ut.commit();
			} catch (Exception e) {
				try {
					ut.rollback();
					e.printStackTrace();
				} catch (Exception e1) {
					e1.printStackTrace();
				}
			}

			// Existing InvitationToken handling
			List<InvitationToken> tokens = getInvitationTokensForEmail(email);
			for(InvitationToken token : tokens){
				useInvitationTokenOnUser(login, token.getId());
			}

			return user;

		}
		else{ throw new PKAlreadyUsedException(); }
	}

	/**
	 * Removes a user and his article from the system
	 * @param user
	 */
	@SuppressWarnings(value="unchecked")
	public void removeUser(String login){
		try {
			ut.begin();
			User persistedUser = em.find(User.class, login);
			
			// Delete the articles for this user
			Query articlesQuery = em.createQuery("from Article where mainauthor=?");
			articlesQuery.setParameter(1, persistedUser);
			List<Article> articles = articlesQuery.getResultList();
			for(Article article : articles){
				em.remove(article);
			}

			em.remove(persistedUser);
			ut.commit();
		} catch (Exception e) {
			try {
				ut.rollback();
				e.printStackTrace();
			} catch (Exception e1) {
				e1.printStackTrace();
			}
		}
	}

	public User getUser(String login) {
		return em.find(User.class, login);
	}

	public User getByEmail(String email){
		Query query = em.createQuery("from User where email=?");
		query.setParameter(1, email);
		try {
			return (User) query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		} catch (NonUniqueResultException e){
			return (User) query.getResultList().get(0);
		}
	}

	public void UpdateUser(User user){
		try {
			ut.begin();
			User updatedUser = em.find(User.class, user.getLogin());
			updatedUser.setEmail(user.getEmail());
			updatedUser.setFirstName(user.getFirstName());
			updatedUser.setLastName(user.getLastName());
			updatedUser.setOrganization(user.getOrganization());
			updatedUser.setPassword(user.getPassword());
			updatedUser.setConferenceCreationTokens(user.getConferenceCreationTokens());
			ut.commit();
		} catch (Exception e) {
			try {
				ut.rollback();
				e.printStackTrace();
			} catch (Exception e1) {
				e1.printStackTrace();
			}
		}
	}

	public Boolean exists(String login){
		return (this.getUser(login)!= null); 
	}

	@SuppressWarnings("unchecked")
	public List<User> getUsers(int conf_id) {
		Query query = em.createQuery("select user from Role role where role.conference = ?");
		Conference conference = em.find(Conference.class, conf_id);
		query.setParameter(1, conference);
		return query.getResultList();
	}

	@SuppressWarnings("unchecked")
	public List<User> getUsers(int conf_id, RoleType type) {
		Query query = em.createQuery("select user from User user, Role role where user.login = role.user and role.type = ? and role.conference = ?)");
		query.setParameter(1, type); 
		Conference conference = em.find(Conference.class, conf_id);
		query.setParameter(2, conference);
		return query.getResultList();
	}

	@SuppressWarnings("unchecked")
	public List<User> getPCMemberForArticle(Integer articleId){		
		Query query = em.createQuery("select a.pcMembers from Article a where a.id=?");
		query.setParameter(1, articleId);
		return query.getResultList();
	}

	
	@SuppressWarnings("unchecked")
	public List<User> getRefereesOfPcMemberForArticle(String PCMemberLogin, Integer articleId){
		Query query = em.createQuery("select delegation.referee from Delegation delegation WHERE delegation.article=? and delegation.pcMember=?");
		Article article = em.find(Article.class, articleId);
		User pcMember = em.find(User.class, PCMemberLogin);
		query.setParameter(1, article);
		query.setParameter(2, pcMember);
		return query.getResultList();
	}

	public Speciality getSpeciality(int SpecialityId){
		Speciality spe = em.find(Speciality.class, SpecialityId);
		return spe;
	}

	@SuppressWarnings("unchecked")
	public List<Speciality> getSpecialities(){
		Query query = em.createQuery("from Speciality");
		return query.getResultList();
	}


	public void addSpeciality(String name){
		try{
			ut.begin();
			Speciality spe = new Speciality(name);
			if(!specialityExists(spe)){
				em.persist(spe);
				em.flush();
			}
			ut.commit();
		} catch (Exception e) {
			try {
				ut.rollback();
				e.printStackTrace();
			} catch (Exception e1) {
				e1.printStackTrace();
			}
		}
	}

	public void removeSpeciality(int specialityId){
		try {
			ut.begin();
			Speciality spe = em.find(Speciality.class, specialityId);
			em.remove(spe);
			em.flush();
			ut.commit();
		} catch (Exception e) {
			try {
				ut.rollback();
				e.printStackTrace();
			} catch (Exception e1) {
				e1.printStackTrace();
			}
		}
	}

	public void addSpecialityToUser(int specialityId,String login){
		try {
			ut.begin();
			User user = em.find(User.class, login);
			Speciality spe = em.find(Speciality.class, specialityId);
			user.addSpeciality(spe);
			ut.commit();
		} catch (Exception e) {
			try {
				ut.rollback();
				e.printStackTrace();
			} catch (Exception e1) {
				e1.printStackTrace();
			}
		}
	}

	public void removeSpecialityFromUser(int specialityId, String login){
		try {
			ut.begin();
			User user = em.find(User.class, login);
			Speciality spe = em.find(Speciality.class, specialityId);
			user.getSpecialities().remove(spe);
			em.remove(spe);
			ut.commit();
		} catch (Exception e) {
			try {
				ut.rollback();
				e.printStackTrace();
			} catch (Exception e1) {
				e1.printStackTrace();
			}
		}
	}

	@SuppressWarnings("unchecked")
	public List<Speciality> getSpecialitiesFromUser(String login){
		Query query = em.createQuery("select user.specialities from User user WHERE user.login=?");
		query.setParameter(1, login);
		return query.getResultList();
	}

	public boolean specialityExists(Speciality spe){
		List<Speciality> list = this.getSpecialities();
		for (Speciality spe2 : list) {
			if(spe2.getName().equalsIgnoreCase(spe.getName())){
				return true;
			}
		}
		return false;
	}

	/*
	 * 
	 * methodes de Roles
	 */

	public List<Role> getRoles() {
		return null;
	}

	public void addRoleForConference(String login, RoleType role, Integer conferenceId) {
		try {
			User user = em.find(User.class, login);
			Conference conference = em.find(Conference.class, conferenceId);

			ut.begin();

			Role newRole = new Role();
			newRole.setUser(user);
			newRole.setConference(conference);
			newRole.setType(role);
			em.persist(newRole);

			ut.commit();
		} catch (Exception e) {
			try {
				ut.rollback();
				e.printStackTrace();
			} catch (Exception e1) {
				e1.printStackTrace();
			}
		}
	}

	public void removeRole(String userLogin, RoleType roleType, int conferenceId) {
		try {
			ut.begin();
			User user = em.find(User.class, userLogin);
			Conference conference = em.find(Conference.class, conferenceId);
			Role role = em.find(Role.class, new RolePK(user,roleType,conference));
			em.remove(role);
			ut.commit();
		} catch (Exception e) {
			try {
				ut.rollback();
				e.printStackTrace();
			} catch (Exception e1) {
				e1.printStackTrace();
			}
		}
	}

	@SuppressWarnings("unchecked")
	public List<InvitationToken> getInvitationTokensForEmail(String email) {
		Query query = em.createQuery("from InvitationToken where email=?");
		query.setParameter(1, email);
		return query.getResultList();
	}

	public void useInvitationTokenOnUser(String userLogin,
			InvitationTokenPK tokenId) {
		User user = getUserByEmail(tokenId.email);
		if(user==null){
			return;
		}
		try {
			ut.begin();
			InvitationToken token = em.find(InvitationToken.class,tokenId);
			Role role = new Role(token.getRole(),user,token.getConference());
			em.remove(token);
			em.persist(role);
			ut.commit();
		} catch (Exception e) {
			try {
				ut.rollback();
				e.printStackTrace();
			} catch (Exception e1) {
				e1.printStackTrace();
			}
		}
	}

	public boolean hasRoleForConference(String userLogin, RoleType roleType,
			Integer conferenceId) {
		User user = getUser(userLogin);
		Conference conf = em.find(Conference.class, conferenceId);
		RolePK pk = new RolePK(user,roleType,conf);
		Role role = em.find(Role.class, pk);
		return role != null;
	}

	@SuppressWarnings("unchecked")
	public List<Role> getRolesForUser(String userLogin) {
		User user = getUser(userLogin);
		Query aQuery = em.createQuery("from Role where user=?");
		aQuery.setParameter(1, user);
		return aQuery.getResultList();
	}

	public User getUserByEmail(String email) {
		email = email.toLowerCase();
		Query query = em.createQuery("from User where email=?");
		query.setParameter(1, email);
		try{
			User user = (User) query.getSingleResult();
			return user;
		} catch (NoResultException e) {
			return null;
		}
	}
}
