package org.yacos.core.article;

import java.io.Serializable;
import java.util.ArrayList;
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.PersistenceContext;
import javax.persistence.Query;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.NotSupportedException;
import javax.transaction.RollbackException;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;

import org.yacos.core.article.Article.State;
import org.yacos.core.article.Preference.PreferenceType;
import org.yacos.core.conferences.Conference;
import org.yacos.core.exceptions.ConferenceDoesntExistException;
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 ArticleManagerBean implements IArticleManager, Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	@PersistenceContext
	private EntityManager em;
	
	@Resource
	private UserTransaction ut;
	
	public ArticleManagerBean() {
		
	}

	public Article addArticle(String title, String topic,String url_article, 
			String mainAuthorLogin, ArrayList<String> secondaryAuthors, State state,Integer conferenceId) {

		try {
			ut.begin();
			Article a = new Article();
			a.setTitle(title);
			a.setTopic(topic);
			a.setURL_article(url_article);
			User mainAuthor = em.find(User.class,mainAuthorLogin);
			a.setMainAuthor(mainAuthor);
			a.setSecondaryAuthors(secondaryAuthors);
			a.setState(state);
			Conference conference = em.find(Conference.class, conferenceId);
			a.setConference(conference);
			em.persist(a);
			ut.commit();
			
			addRoleForConference(mainAuthorLogin,RoleType.AUTHOR,conferenceId);
			
			return a;
		} catch (Exception e) {
			try {
				ut.rollback();
				e.printStackTrace();
			} catch (Exception e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
		}
		return null;
	}

	public void updateArticle(Article article) {
		try {
			ut.begin();
			Article persistentArticle = em.find(Article.class, article.getId());
			persistentArticle.setTitle(article.getTitle());
			persistentArticle.setTopic(article.getTopic());
			persistentArticle.setMainAuthor(article.getMainAuthor());
			persistentArticle.setURL_article(article.getURL_article());
			persistentArticle.setState(article.getState());
			persistentArticle.setSecondaryAuthors(article.getSecondaryAuthors());
			ut.commit();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				ut.rollback();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	public void removeArticle(Integer articleId) {
		try {
			ut.begin();
			Article article = this.getArticle(articleId);
			em.remove(article);
			ut.commit();
		} catch (Exception e) {
			try {
				ut.rollback();
			} catch (Exception e1) {
				e1.printStackTrace();
			}
			e.printStackTrace();
		}
	}

	public Article getArticle(Integer articleId) {
		return em.find(Article.class, articleId);
	}
	
	@SuppressWarnings("unchecked")
	public List<Article> getArticles(Integer confId) throws ConferenceDoesntExistException {
		if(confId == null){
			throw new ConferenceDoesntExistException(confId);
		}
		Conference conf = em.find(Conference.class, confId);
		if(conf == null){
			throw new ConferenceDoesntExistException(confId);
		}
	
		Query query = em.createQuery("from Article a WHERE a.conference = ? ORDER BY a.title");
		query.setParameter(1, conf);
		return query.getResultList();
	}

	@SuppressWarnings("unchecked")
	public List<Article> getArticleOfAuthor(Integer confId, String authorId) {
		Conference conf = em.find(Conference.class, confId);
		User author = em.find(User.class, authorId);
		Query query = em.createQuery("from Article a WHERE conference = ? and mainAuthor = ? ORDER BY a.title");
		query.setParameter(1, conf);
		query.setParameter(2, author);
		return query.getResultList();
	}
	     
	public List<Article> getArticlesOfMember(Integer confId, String memberId){
		
		Conference conf = em.find(Conference.class, confId);
		User member = em.find(User.class, memberId);
		Query query = em.createQuery("from Article a join a.pcMembers m WHERE a.conference = ? and m = ? ORDER BY a.title");
		query.setParameter(1, conf);
		query.setParameter(2, member);
		return query.getResultList();
	}
	
	public void assignArticleToPCMember(Integer articleId,String memberId){
		try{
			ut.begin();
			Article article = this.getArticle(articleId);
			User PCMember = em.find(User.class, memberId);
			article.getPcMembers().add(PCMember);
			ut.commit();
			
			this.updateArticle(article);
		} catch (Exception e) {
			try{
				ut.rollback();
			} catch (Exception e1) {
				e1.printStackTrace();
			}
		}
		//article.addPCMember(PCMember);
	}
	
	
	public void unassignArticleToPCMember(Integer articleId,String memberId){
		try{
			ut.begin();
			Article article = this.getArticle(articleId);
			User PCMember = em.find(User.class, memberId);
			article.getPcMembers().remove(PCMember);
			ut.commit();	
			this.updateArticle(article);
		} catch (Exception e) {
			try{
				ut.rollback();
			} catch (Exception e1) {
				e1.printStackTrace();
			}
		}
	}
	
	
	public List<User> getArticlePCMembers(Integer articleId) {
			Article article = this.getArticle(articleId);
			List<User> users = (List<User>)article.getPcMembers();
			return users;	
	}
	
	public void delegateArticleToReferee(Integer articleId, String refereeId, String memberId){
		Article article = this.getArticle(articleId);
		User PCMember = em.find(User.class, memberId);
		User referee = em.find(User.class, refereeId);
		article.delegateTo(referee,PCMember);
	}

	public void undelegateArticleToReferee(Integer articleId, String refereeId, String memberId){
		//TODO
	}
	
	

	
	/*
	 * gestion des preferences
	 */

	
	public Preference addPreference(Integer article_id, String userLogin,PreferenceType preferenceType){
		try{
			ut.begin();
			Preference preference = new Preference();
			Article article = getArticle(article_id);
			preference.setArticle(article);
			User pcMember = em.find(User.class, userLogin);
			preference.setPcMember(pcMember);
			preference.setPreferenceType(preferenceType);
			em.persist(preference);
			ut.commit();
			return preference;
		} catch(Exception e){
			try {
				ut.rollback();
			} catch (Exception e1) {
				e1.printStackTrace();
			}
		}
		return null;
	}
	
	
	public void removePreference(Integer article_id, String userLogin){
		try{
			ut.begin();
			Preference pref = this.getPreferenceForUserAndArticle(article_id, userLogin);
			em.remove(pref);
			ut.commit();
		} catch (Exception e) {
			try{
				ut.rollback();
			} catch (Exception e1) {
				e1.printStackTrace();
			}
			e.printStackTrace();
		}
	}
		
	public void updatePreference(Preference preference){
		try{
			ut.begin();
			int articleId = preference.getArticle().getId();
			String login = preference.getPcMember().getLogin();
			PreferencePK preferenceid = new PreferencePK();
			preferenceid.article = em.find(Article.class, articleId);
			preferenceid.pcMember = em.find(User.class, login);
			
			Preference preference2 = em.find(Preference.class, preferenceid);
			
			em.remove(preference2);
			em.persist(preference);
			ut.commit();			
			
		} catch (Exception e) {
			try{
				e.printStackTrace();
				ut.rollback();
			} catch (Exception e1) {
				e1.printStackTrace();
			}
			e.printStackTrace();
		}
	}

	public Preference getPreferenceForUserAndArticle(Integer article_id, String pcMemberLogin){
		PreferencePK preferenceid = new PreferencePK();
		preferenceid.article = em.find(Article.class, article_id);
		preferenceid.pcMember = em.find(User.class, pcMemberLogin);
		try {
			return (Preference) em.find(Preference.class, preferenceid);
		} catch (NoResultException e) {
			return null;
		}
	}
	
	@SuppressWarnings("unchecked")
	public List<Preference> getPreferencesForArticle(Integer article_id){
		Query query = em.createQuery("from Preference p WHERE article_id = ?");
		query.setParameter(1, article_id);
		return query.getResultList();
	}
	
	
	public boolean existsArticle(Integer articleId) {
		return em.find(Article.class, articleId)!=null;
	}

	public boolean existsPreference(Preference preference) {
		return em.find(Preference.class, preference.getId())!=null;
	}
	
	public boolean hasUserRoleForConference(String userLogin, RoleType roleType, Integer conferenceId){
		Conference conference = em.find(Conference.class, conferenceId);
		User user = em.find(User.class, userLogin);
		
		RolePK rolePK = new RolePK(user,roleType,conference);
		return em.find(Role.class, rolePK)!=null;
	}

	private void addRoleForConference(String userLogin, RoleType roleType, Integer conferenceId) {
		Conference conference = em.find(Conference.class, conferenceId);
		User user = em.find(User.class, userLogin);

		if(! hasUserRoleForConference(userLogin, RoleType.AUTHOR, conferenceId)){
			Role role = new Role();
			role.setUser(user);
			role.setType(roleType);
			role.setConference(conference);

			try{
				ut.begin();
				em.persist(role);
				ut.commit();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
	public EntityManager getEntityManager() {
		return em;
	}


}
