package org.yacos.core.conferences;

import java.util.ArrayList;
import java.util.Date;
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.PersistenceContext;
import javax.persistence.Query;
import javax.transaction.UserTransaction;
import org.yacos.core.evaluation.Criterion;
import org.yacos.core.exceptions.ConferenceDoesntExistException;
import org.yacos.core.exceptions.NoConferenceCreationTokenLeftException;
import org.yacos.core.system.InvitationToken;
import org.yacos.core.system.InvitationTokenPK;
import org.yacos.core.users.Role;
import org.yacos.core.users.RolePK;
import org.yacos.core.users.User;
import org.yacos.core.users.Role.RoleType;


@Stateless
@TransactionManagement(TransactionManagementType.BEAN)
public class ConferenceManagerBean implements IConferenceManager {
	@PersistenceContext
    EntityManager em;
	@Resource
	UserTransaction ut;

	public Conference addConference(
			String titre,
			String chairmanLogin,
			String description,
			String complementaryInfo,
			Date dataAbstract,
			Date dateArticle,
			Date dateEvaluation,
			Date dateStart,
			Date dateEnd) throws NoConferenceCreationTokenLeftException {
		Conference conf = new Conference(titre, description, complementaryInfo, dataAbstract, dateArticle, dateEvaluation, dateStart, dateEnd);
		if( ! canCreateConference(chairmanLogin)){
			throw new NoConferenceCreationTokenLeftException();
		}
		try {
			ut.begin();
			User chairman = em.find(User.class, chairmanLogin);
			em.persist(conf);
			conf.setChairman(chairman);
			chairman.setConferenceCreationTokens(chairman.getConferenceCreationTokens()-1);
			ut.commit();
		} catch (Exception e) {
			try {
				ut.rollback();
			} catch (Exception e1) {
				e1.printStackTrace();
			}
			
		}
		return conf;
	}

	public Conference getConference(Integer id) {
		 return em.find(Conference.class, id);
	}

	public void remove(Integer conferenceId) {
		try {
			ut.begin();
			Conference persistedConf = em.find(Conference.class, conferenceId);
			em.remove(persistedConf);
			ut.commit();
		} catch (Exception e) {
			try {
				ut.rollback();
			} catch (Exception e1) {
				e1.printStackTrace();
			}
			
		}
	}

	public void update(Conference conf) {
		try {
			ut.begin();
			Conference persistedConf = em.find(Conference.class, conf.getId());
			persistedConf.setDateAbstract(conf.getDateAbstract());
			persistedConf.setDateArticle(conf.getDateArticle());
			persistedConf.setDateEnd(conf.getDateEnd());
			persistedConf.setDateEvaluation(conf.getDateEvaluation());
			persistedConf.setDateStart(conf.getDateStart());
			persistedConf.setOtherInformations(conf.getOtherInformations());
			persistedConf.setTitle(conf.getTitle());
			ut.commit();
		} catch (Exception e) {
			try {
				ut.rollback();
			} catch (Exception e1) {
				e1.printStackTrace();
			}
		}
	}

	@SuppressWarnings("unchecked")
	public List<Conference> getConferences() {
		 return em.createQuery("from Conference conf ORDER BY conf.id").getResultList();
	}
	
	public List<Conference> getConferences(User user){
		List<Conference> list = new ArrayList<Conference>();
		
		Query query = em.createQuery("SELECT DISTINCT role.conference FROM Role role WHERE role.user = ?");
		query.setParameter(1, user);
		
		if (query.getResultList().size() > 0 ) {
			for (Object conf : query.getResultList()) {
				list.add((Conference) conf);
			}
		}
		
		return list;
	}
	
			
	/* 
	 * 
	 * m�thodes relatives aux roles
	 * */
	
	public Role addRole(Role.RoleType roleType, String login, Integer confId) {
		try {
			ut.begin();
			User user = em.find(User.class, login);
			Conference conf = em.find(Conference.class, confId);
			Role role = new Role(roleType, user, conf);
			em.persist(role);
			ut.commit();
			return role;
		} catch (Exception e) {
			try {
				ut.rollback();
			} catch (Exception e1) {
				e1.printStackTrace();
			}
		}
		return null;
	}

	public void removeRole(Role role) {
		try {
			ut.begin();
			RolePK rolePk = new RolePK(
					role.getUser(),
					role.getType(),
					role.getConference());
			Role rolePersisted = em.find(Role.class, rolePk);
			em.remove(rolePersisted);
			ut.commit();
		} catch (Exception e) {
			try {
				ut.rollback();
			} catch (Exception e1) {
				e1.printStackTrace();
			}
		}
	}
	
	public void removePCMemberForConf(Integer confId) {
		try {
			ut.begin();
			Conference conf = em.find(Conference.class, confId);
			Query query = em.createQuery("delete from Role where conference=? and type=?");
			query.setParameter(1, conf);
			query.setParameter(2, RoleType.PCMEMBER);
			query.executeUpdate();
			ut.commit();
		} catch (Exception e) {
			try {
				ut.rollback();
			} catch (Exception e1) {
				e1.printStackTrace();
			}
		}
	}

	public void updateRole(Role role){
		try {
			ut.begin();
			RolePK rolePk = new RolePK(
					role.getUser(),
					role.getType(),
					role.getConference());
			Role rolePersisted = em.find(Role.class, rolePk);
			rolePersisted.setUser(role.getUser());
			rolePersisted.setType(role.getType());
			rolePersisted.setConference(role.getConference());
			ut.commit();
		} catch (Exception e) {
			try {
				ut.rollback();
			} catch (Exception e1) {
				e1.printStackTrace();
			}
		}
	}
	
	@SuppressWarnings("unchecked")
	public List<Role> getRoles(Conference conf){
		Query query = em.createQuery("from Role role where conference = ?");
		query.setParameter(1, conf);		 		
		return query.getResultList();
	}
	
	@SuppressWarnings("unchecked")	
	public List<Role> getRoles(User user) {
		Query query = em.createQuery("from Role role where user = ?");
		query.setParameter(1, user); 		
		return query.getResultList();
	} 

	@SuppressWarnings("unchecked")
	public List<Role> getRoles(String userLogin, Integer confId) {
		User user = em.find(User.class, userLogin);
		Conference conf = em.find(Conference.class, confId);
		Query query = em.createQuery("from Role role where conference = ? and user = ?");
		query.setParameter(1, conf);
		query.setParameter(2, user);	
		return query.getResultList();
	}

	public boolean exists(Integer conferenceId) {
		return (this.getConference(conferenceId)!=null);
	}

	/* ================================================ */
	/* = Invitation management methods implementation = */
	/* ================================================ */
	
	public InvitationToken addInvitationToken(String email, RoleType role,
			Integer conferenceId) throws ConferenceDoesntExistException {
		Conference conf = getConference(conferenceId);
		
		if(conf == null){
			throw new ConferenceDoesntExistException(conferenceId);
		}
		

		InvitationTokenPK tokenPK = new InvitationTokenPK();
		tokenPK.email = email;
		tokenPK.role = role;
		tokenPK.conference = conf;
		
		InvitationToken newToken = em.find(InvitationToken.class, tokenPK);
		
		if(newToken == null){
			try {
				ut.begin();
				newToken = new InvitationToken();
				newToken.setEmail(email);
				newToken.setRole(role);
				newToken.setConference(conf);
			
				em.persist(newToken);
				ut.commit();
			} catch (Exception e) {
				try {
					ut.rollback();
				} catch (Exception e1) {
					e1.printStackTrace();
				}
			}
		}
		
		return newToken;
	}

	public void removeInvitationToken(InvitationToken token) {
		try {
			ut.begin();
			token = em.find(InvitationToken.class, token.getId());
			em.remove(token);
			ut.commit();
		} catch (Exception e) {
			try {
				ut.rollback();
			} catch (Exception e1) {
				e1.printStackTrace();
			}
		}
	}

	public InvitationToken getInvitationToken(InvitationTokenPK tokenId) {
		return em.find(InvitationToken.class, tokenId);
	}

	@SuppressWarnings("unchecked")
	public List<InvitationToken> getInvitationTokensForConference(Integer conferenceId) {
		Conference conf = em.find(Conference.class, conferenceId);
		Query query = em.createQuery("from InvitationToken where conference=?");
		query.setParameter(1, conf);
		return query.getResultList();
	}

	public void addCriterionToConference(Integer conferenceId, String name,
			Integer minRating, Integer maxRating) {
		try {
			ut.begin();
			Conference conf = em.find(Conference.class, conferenceId);
			Criterion criterion = new Criterion(name,minRating,maxRating,conf);
			em.persist(criterion);
			ut.commit();
		} catch (Exception e) {
			try {
				ut.rollback();
			} catch (Exception e1) {
				e1.printStackTrace();
			}
		}
	}
	
	public void removeCriterionToConference(Integer conferenceId) {
		try {
			ut.begin();
			Conference conf = em.find(Conference.class, conferenceId);
			Query query = em.createQuery("delete from Criterion where conference=?");
			query.setParameter(1, conf);
			query.executeUpdate();
			ut.commit();
		} catch (Exception e) {
			try {
				ut.rollback();
			} catch (Exception e1) {
				e1.printStackTrace();
			}
		}
	}
	
	public void removeOneCriterionToConference(Criterion criterion) {
		try {
			ut.begin();
			em.remove(criterion);
			ut.commit();
		} catch (Exception e) {
			try {
				ut.rollback();
			} catch (Exception e1) {
				e1.printStackTrace();
			}
		}
	}
	

	public void addPCMemberToConference(Integer conferenceId, String userLogin) {
		try {
			ut.begin();
			Conference conf = em.find(Conference.class,conferenceId);
			User member = em.find(User.class, userLogin);
			
			// First checks if the role already exists, if not create it
			RolePK memberRolePK = new RolePK(member,RoleType.PCMEMBER,conf);
			if(em.find(Role.class, memberRolePK)==null){
				Role memberRole = new Role();
				memberRole.setType(RoleType.PCMEMBER);
				memberRole.setUser(member);
				memberRole.setConference(conf);
				em.persist(memberRole);
			}
			ut.commit();
		} catch (Exception e) {
			try {
				ut.rollback();
			} catch (Exception e1) {
				e1.printStackTrace();
			}
		}
	}


	public void addConferenceCreationToken(String userLogin) {
		try {
			ut.begin();
			User user = em.find(User.class,userLogin);
			if(user != null){
				user.setConferenceCreationTokens(
						user.getConferenceCreationTokens()+1);
			}
			ut.commit();
		} catch (Exception e) {
			try {
				ut.rollback();
			} catch (Exception e1) {
				e1.printStackTrace();
			}
		}
	}

	public boolean canCreateConference(String userLogin) {
		return conferenceCreationTokens(userLogin)>0;
	}

	public Integer conferenceCreationTokens(String userLogin) {
		User user = em.find(User.class,userLogin);
		if(user == null){
			return 0;
		}
		return user.getConferenceCreationTokens();
	}
	
	public void useConferenceCreationToken(String userLogin) throws NoConferenceCreationTokenLeftException {
		try {
			ut.begin();
			User user = em.find(User.class,userLogin);
			if(user != null){
				Integer tokensLeft = user.getConferenceCreationTokens();
				if(tokensLeft>0){
					user.setConferenceCreationTokens(tokensLeft-1);
				} else {
					throw new NoConferenceCreationTokenLeftException();
				}
			}
			ut.commit();
		} catch (NoConferenceCreationTokenLeftException e) {
			try {
				ut.rollback();
			} catch (Exception e1) {
				e1.printStackTrace();
			}
			throw e;
		} catch (Exception e) {
			try {
				ut.rollback();
			} catch (Exception e1) {
				e1.printStackTrace();
			}
		}
	}
}
