package org.yacos.web.chairman.controller;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.mail.MailSendException;
import org.springframework.validation.BindException;
import org.springframework.validation.Errors;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.ModelAndViewDefiningException;
import org.springframework.web.servlet.mvc.AbstractWizardFormController;
import org.springframework.web.servlet.view.RedirectView;
import org.yacos.core.conferences.Conference;
import org.yacos.core.conferences.IConferenceManager;
import org.yacos.core.conferences.Conference.Phase;
import org.yacos.core.evaluation.Criterion;
import org.yacos.core.evaluation.IEvaluationManager;
import org.yacos.core.exceptions.ConferenceDoesntExistException;
import org.yacos.core.exceptions.NoConferenceCreationTokenLeftException;
import org.yacos.core.users.IUserManager;
import org.yacos.core.users.User;
import org.yacos.core.users.Role.RoleType;
import org.yacos.web.chairman.form.FormConference;
import org.yacos.web.chairman.validation.ConferenceValidator;
import org.yacos.web.system.controller.MailSenderService;
import org.yacos.web.system.session.SessionService;
import org.yacos.web.system.session.YACOSUtils;

public class AddConferenceController extends AbstractWizardFormController {

	protected final Log logger = LogFactory.getLog(getClass());

	private IConferenceManager conferenceManager;

	private IUserManager userManager;

	private IEvaluationManager evaluationManager;

	public IEvaluationManager getEvaluationManager() {
		return evaluationManager;
	}
	public void setEvaluationManager(IEvaluationManager evaluationManager) {
		this.evaluationManager = evaluationManager;
	}
	public IUserManager getUserManager() {
		return userManager;
	}
	public void setUserManager(IUserManager userManager) {
		this.userManager = userManager;
	}
	public IConferenceManager getConferenceManager() {
		return conferenceManager;
	}
	public void setConferenceManager(IConferenceManager conferenceManager) {
		this.conferenceManager = conferenceManager;
	}

	// ###########################################################

	private static int nextPerson = 1;

	private List<PersonBean> listPersonBean;
	private List<PersonBean> listPersonFiltered;
	private List<PersonBean> listPersonAdded;
	private List<String>	 listInvitations;

	public void fillUser(String text) {
		if (text.equals(""))
			listPersonFiltered = listPersonBean;
		else {
			listPersonFiltered = new ArrayList<PersonBean>();
			for (PersonBean b : listPersonBean) {
				if (b.getFirstName().toLowerCase().startsWith(text.toLowerCase()) || b.getLastName().toLowerCase().startsWith(text.toLowerCase())){
					listPersonFiltered.add(b);
				}
			}
		}
	}

	public void setPerson(PersonBean b) {
		if (b.getId() == -1) {
			b.setId(getNextPerson());
		}

		PersonBean truePerson = getTruePerson(b.getLogin());

		listPersonAdded.remove(truePerson);		
		listPersonAdded.add(truePerson);
		listPersonBean.remove(truePerson);
		listPersonFiltered.remove(truePerson);
	}

	public void deletePerson(PersonBean b) {

		PersonBean truePerson = getTruePerson(b.getLogin());

		listPersonAdded.remove(truePerson);
		listPersonBean.add(truePerson);
	}

	public PersonBean getTruePerson(String login) {
		for (PersonBean b : listPersonBean) {
			if (b.getLogin().equals(login)){
				return b;
			}
		}

		for (PersonBean b : listPersonAdded) {
			if (b.getLogin().equals(login)){
				return b;
			}
		}

		return null;
	}


	private static synchronized int getNextPerson()
	{
		return nextPerson++;
	}

	public List<PersonBean> getUsersFiltered() {
		return listPersonFiltered;
	}

	public List<PersonBean> getUsers() {
		return listPersonBean;
	}

	public List<PersonBean> getUsersAdded() {
		return listPersonAdded;
	}

	public List<String> getInvitation(){
		return listInvitations;
	}

	public boolean addInvitation(String email){
		email = email.toLowerCase();
		Pattern emailPattern = Pattern.compile("^[a-z0-9._-]+@[a-z0-9._-]{2,}[.][a-z]{2,4}$");	// Regex
		Matcher emailMatcher = emailPattern.matcher(email);

		if(!emailMatcher.matches()){
			return false;
		}

		listInvitations.add(email);

		return true;
	}

	public void removeInvitations(List<String> emailList){
		for(String email : emailList){
			listInvitations.remove(email);
		}
	}


	// ###########################################################


	private static int nextId = 1;

	private Set<CriterionBean> criteria;
	private Set<CriterionBean> criteriaAll;

	public Set<CriterionBean> getCriteriaAll() {
		return criteriaAll;
	}

	public Set<CriterionBean> getCriteriaAdded() {
		return criteria;
	}

	public CriterionBean getTrueCriteria(int id) {
		for (CriterionBean cb : criteriaAll) {
			if (cb.getId() == id){
				return cb;
			}
		}

		for (CriterionBean cb : criteria) {
			if (cb.getId() == id){
				return cb;
			}
		}

		return null;
	}

	public void setCriterionClick(CriterionBean c) {
		if (c.getId() == -1) {
			c.setId(getNextId());
		}

		criteria.remove(c);
		criteria.add(c);
	}

	public void setCriterion(CriterionBean c) {
		if (c.getId() == -1) {
			c.setId(getNextId());
		}

		CriterionBean trueCriteria = getTrueCriteria(c.getId());

		criteria.remove(trueCriteria);
		criteria.add(trueCriteria);
		criteriaAll.remove(trueCriteria);
	}

	public void deleteCriterion(CriterionBean c) {

		CriterionBean trueCriteria = getTrueCriteria(c.getId());



		criteria.remove(trueCriteria);
		criteriaAll.add(trueCriteria);
	}

	private static synchronized int getNextId()
	{
		return nextId++;
	}

	// ###########################################################


	public AddConferenceController() {
		setCommandName("formConference");
		setPages(new String[] {"addConference", "addConference2", "addConference3", "addConference4", "addConference5"});
	}

	protected Object formBackingObject(HttpServletRequest request) throws ModelAndViewDefiningException {
		logger.info(this.getClass().toString() + " dans le formBackingObject");

		FormConference conference = new FormConference();

		String action = request.getParameter("action");
		if (action == null || action.equals(""))
		{			
			if (! conferenceManager.canCreateConference(SessionService.getInstance().getCurrentUserLogin()))
				throw new ModelAndViewDefiningException(new ModelAndView("denied"));

			criteria = new HashSet<CriterionBean>();
			criteriaAll = new HashSet<CriterionBean>();
			List<Criterion> listCriterion = evaluationManager.getCriterions();


			int maxNext = 0;
			for (Criterion crit : listCriterion) {
				if (maxNext <= crit.getId())
					maxNext = crit.getId();
			}

			nextId = maxNext + 1;

			for (Criterion crit : listCriterion) {
				CriterionBean cb = new CriterionBean();
				cb.setId(crit.getId());
				cb.setLabel(crit.getName());
				cb.setMax(crit.getMax_rating());
				cb.setMin(crit.getMin_rating());
				criteriaAll.add(cb);
				getNextId();
			}

			listPersonBean = new ArrayList<PersonBean>();
			listPersonAdded = new ArrayList<PersonBean>();
			listInvitations = new ArrayList<String>();
			listPersonFiltered = listPersonBean;
			List<User> listUsers = userManager.getUsers();
			for (User user : listUsers) {
				PersonBean pb = new PersonBean();
				pb.setId(getNextPerson());
				pb.setFirstName(user.getFirstName());
				pb.setLastName(user.getLastName());
				pb.setLogin(user.getLogin());
				listPersonBean.add(pb);	
			}

			return conference;
		}
		else if (action.equals("modify")) {
			Conference conf = SessionService.getInstance().getCurrentConference();

			criteria = new HashSet<CriterionBean>();
			criteriaAll = new HashSet<CriterionBean>();

			List<Criterion> listCriterionForConf = evaluationManager.getCriterions(conf.getId());
			List<Criterion> listCriterion = evaluationManager.getCriterions();

			int maxNext = 0;
			for (Criterion crit : listCriterion) {
				if (maxNext <= crit.getId())
					maxNext = crit.getId();
			}

			nextId = maxNext + 1;

			for (Criterion crit : listCriterionForConf) {
				CriterionBean cb = new CriterionBean();
				cb.setId(crit.getId());
				cb.setLabel(crit.getName());
				cb.setMax(crit.getMax_rating());
				cb.setMin(crit.getMin_rating());
				criteria.add(cb);
				getNextId();
			}

			for (Criterion crit : listCriterion) {
				CriterionBean cb = new CriterionBean();
				cb.setId(crit.getId());
				cb.setLabel(crit.getName());
				cb.setMax(crit.getMax_rating());
				cb.setMin(crit.getMin_rating());
				criteriaAll.add(cb);
				getNextId();
			}

			// remove of the criteria already added
			Set<CriterionBean> criteriaAllIter = new HashSet<CriterionBean>(criteriaAll);
			for (CriterionBean crit : criteriaAllIter)
				for (CriterionBean crit2 : criteria)
					if (crit.getId() == crit2.getId())
						criteriaAll.remove(crit);


			listPersonBean = new ArrayList<PersonBean>();
			listPersonAdded = new ArrayList<PersonBean>();
			// Should get the invitation tokens and allow them to be removed
			listInvitations = new ArrayList<String>();
			listPersonFiltered = listPersonBean;
			List<User> listUsers = userManager.getUsers();
			for (User user : listUsers) {
				PersonBean pb = new PersonBean();
				pb.setId(getNextPerson());
				pb.setFirstName(user.getFirstName());
				pb.setLastName(user.getLastName());
				pb.setLogin(user.getLogin());
				listPersonBean.add(pb);	
			}

			List<User> listUsersForConf = userManager.getUsers(conf.getId(), RoleType.PCMEMBER);
			for (User user : listUsersForConf) {
				PersonBean pb = new PersonBean();
				pb.setId(getNextPerson());
				pb.setFirstName(user.getFirstName());
				pb.setLastName(user.getLastName());
				pb.setLogin(user.getLogin());
				listPersonAdded.add(pb);	
			}

			// remove of the person already added
			List<PersonBean> listPersonIter = new ArrayList<PersonBean>(listPersonBean);
			for (PersonBean person : listPersonIter)
				for (PersonBean person2 : listPersonAdded)
					if (person.getLogin().equals(person2.getLogin()))
						listPersonBean.remove(person);

			DateFormat format = new SimpleDateFormat("yyyy-MM-dd");

			conference.setTitle(conf.getTitle());
			conference.setDateArticleParsed(conf.getDateArticle());
			conference.setDateArticle(format.format(conf.getDateArticle()));
			conference.setDateEndParsed(conf.getDateEnd());
			conference.setDateEnd(format.format(conf.getDateEnd()));
			conference.setDateEvaluationParsed(conf.getDateEvaluation());
			conference.setDateEvaluation(format.format(conf.getDateEvaluation()));
			conference.setDateStartParsed(conf.getDateStart());
			conference.setDateStart(format.format(conf.getDateStart()));
			conference.setDescription(conf.getDescription());
			conference.setOtherInformations(conf.getOtherInformations());
			conference.setPageNumberChecked(conf.getPageNumber());
			conference.setPageNumber(conf.getPageNumber().toString());
			conference.setTypePDF(conf.isTypePDF());
			conference.setTypeLatec(conf.isTypeLatec());
			conference.setTypeWord(conf.isTypeWord());
			conference.setTypeODT(conf.isTypeODT());
			conference.setSendInfo(conf.getSendInfo());

			return conference;

		}
		else {
			throw new ModelAndViewDefiningException(new ModelAndView("404error"));
		}

	}



	protected void validatePage(Object command, Errors errors, int page) {
		FormConference conference = (FormConference) command;
		ConferenceValidator conferenceValidator = (ConferenceValidator) getValidator();
		switch (page) {
		case 0:
			conferenceValidator.validateFirstPage(conference, errors);
			break;
		case 1:
			conferenceValidator.validateSecondPage(conference, errors);
			break;
		case 2:
			conferenceValidator.validateThirdPage(conference, errors);
			break;
		}
	}

	protected ModelAndView processFinish(
			HttpServletRequest request, HttpServletResponse response, Object command, BindException errors) {
		FormConference conference = (FormConference) command;

		String action = request.getParameter("action");
		if (action == null || action.equals("")) {

			Conference conf = null;

			try {
				conf = conferenceManager.addConference(
						conference.getTitle(),
						SessionService.getInstance().getCurrentUserLogin(),
						conference.getDescription(), 
						conference.getOtherInformations(), 
						conference.getDateArticleParsed(), 
						conference.getDateArticleParsed(), 
						conference.getDateEvaluationParsed(), 
						conference.getDateStartParsed(), 
						conference.getDateEndParsed(),
						conference.getPageNumberChecked(),
						conference.isTypePDF(),
						conference.isTypeLatec(),
						conference.isTypeWord(),
						conference.isTypeODT(),
						conference.getSendInfo());
			} catch (NoConferenceCreationTokenLeftException e1) {
				return new ModelAndView("denied","message",getMessageSourceAccessor().getMessage("conference.error.noTokenLeft"));
			}

			conference.setConferenceId(conf.getId());
			SessionService.getInstance().setCurrentConference(conf);

			for(CriterionBean criterion : criteria){
				conferenceManager.addCriterionToConference(
						conf.getId(),
						criterion.getLabel(),
						criterion.getMin(),
						criterion.getMax());
			}

			for(PersonBean personBean : listPersonAdded){
				conferenceManager.addPCMemberToConference(
						conf.getId(),
						personBean.getLogin());
				// Send an email to the pc member
				String mailSubject = "YACOS : you have been added to the program committee of a new conference";
				String mailBody = "The chairman of the new conference "+conference.getTitle();
				mailBody += " has added you to the Program Committee\n";
				mailBody += "You will be able to view, evaluate or delegate the evaluation of the articles that will be submitted\n";
				mailBody += YACOSUtils.fullURL("userBoard.htm");
				User user = userManager.getUser(personBean.getLogin());
				MailSenderService.getInstance().sendEMail(user.getEmail(), mailSubject, mailBody);
			}

			String mailSubject = "YACOS "+conf.getTitle()+" invitation";
			String mailBody = "Greetings,\n";
			mailBody += "You've been invited to join the program comity of the conference :\n";
			mailBody += conf.getTitle()+"/n";
			mailBody += "You can register on this page : \n";
			mailBody += YACOSUtils.fullURL("registerUser.htm")+" \n";
			mailBody += "Please note that you MUST use this eMail address in the registration form in order to get the appropriate credentials.";
			// TODO : use a template
			for(String invitationEmail : listInvitations){
				try {
					conferenceManager.addInvitationToken(invitationEmail, RoleType.PCMEMBER, conf.getId());
					MailSenderService.getInstance().sendEMail(invitationEmail, mailSubject , mailBody);
				} catch (ConferenceDoesntExistException e) {
					// Should never happen
					logger.warn("Could not send invitation email.");
				} catch (MailSendException e) {
					logger.warn("Could not send invitation email.");
				}
			}

			conferenceManager.addRole(RoleType.CHAIRMAN, SessionService.getInstance().getCurrentUserLogin(), conf.getId());
			conferenceManager.addRole(RoleType.PCMEMBER, SessionService.getInstance().getCurrentUserLogin(), conf.getId());
			// Refresh the roles to take into account the chairman credentials
			SessionService.getInstance().refreshAuthentication();
		} else if (action.equals("modify")) {
			Conference conf = SessionService.getInstance().getCurrentConference();
			conf.setTitle(conference.getTitle());
			conf.setDescription(conference.getDescription());
			conf.setOtherInformations(conference.getOtherInformations());
			conf.setDateArticle(conference.getDateArticleParsed());
			conf.setDateAbstract(conference.getDateArticleParsed());
			conf.setDateEnd(conference.getDateEndParsed());
			conf.setDateStart(conference.getDateStartParsed());
			conf.setDateEvaluation(conference.getDateEvaluationParsed());
			conf.setPageNumber(conference.getPageNumberChecked());
			conf.setTypePDF(conference.isTypePDF());
			conf.setTypeLatec(conference.isTypeLatec());
			conf.setTypeWord(conference.isTypeWord());
			conf.setTypeODT(conference.isTypeODT());
			conf.setSendInfo(conference.getSendInfo());

			conferenceManager.update(conf);

			if (conf.getCurrentPhase() == Phase.CALL_FOR_PAPER) {

				conferenceManager.removeCriterionToConference(conf.getId());

				for(CriterionBean criterion : criteria){
					conferenceManager.addCriterionToConference(
							conf.getId(),
							criterion.getLabel(),
							criterion.getMin(),
							criterion.getMax());
				}

				conferenceManager.removePCMemberForConf(conf.getId());

				for(PersonBean personBean : listPersonAdded){
					conferenceManager.addPCMemberToConference(
							conf.getId(),
							personBean.getLogin());
				}
			}
		}

		return new ModelAndView(new RedirectView("reportConfCreate.htm"));
	}

}
