/**********************************************************************************
 * $URL$
 * $Id$
 ***********************************************************************************
 *
 * Copyright (c) 2004, 2005, 2006, 2007, 2008, 2009 The Sakai Foundation
 *
 * Licensed under the Educational Community License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.opensource.org/licenses/ECL-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 **********************************************************************************/

package org.sakaiproject.tool.assessment.ui.listener.author;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Iterator;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ActionEvent;
import javax.faces.event.ActionListener;
import javax.faces.model.SelectItem;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import org.sakaiproject.authz.api.AuthzGroup.RealmLockMode;
import org.sakaiproject.component.cover.ComponentManager;
import org.sakaiproject.component.cover.ServerConfigurationService;
import org.sakaiproject.email.cover.EmailService;
import org.sakaiproject.event.cover.EventTrackingService;
import org.sakaiproject.exception.IdUnusedException;
import org.sakaiproject.grading.api.InvalidCategoryException;
import org.sakaiproject.rubrics.api.RubricsConstants;
import org.sakaiproject.rubrics.api.RubricsService;
import org.sakaiproject.rubrics.api.model.ToolItemRubricAssociation;
import org.sakaiproject.samigo.api.SamigoAvailableNotificationService;
import org.sakaiproject.site.api.Group;
import org.sakaiproject.site.api.Site;
import org.sakaiproject.site.cover.SiteService;
import org.sakaiproject.spring.SpringBeanLocator;
import org.sakaiproject.tasks.api.Priorities;
import org.sakaiproject.tasks.api.Task;
import org.sakaiproject.tasks.api.TaskService;
import org.sakaiproject.samigo.util.SamigoConstants;
import org.sakaiproject.grading.api.AssignmentHasIllegalPointsException;
import org.sakaiproject.grading.api.InvalidGradeItemNameException;
import org.sakaiproject.tool.assessment.data.dao.assessment.PublishedItemData;
import org.sakaiproject.tool.assessment.data.dao.assessment.PublishedSectionData;
import org.sakaiproject.tool.assessment.facade.ExtendedTimeFacade;
import org.sakaiproject.tool.assessment.integration.helper.ifc.CalendarServiceHelper;
import org.sakaiproject.tool.assessment.data.dao.assessment.PublishedMetaData;
import org.sakaiproject.tool.assessment.data.ifc.assessment.AssessmentAccessControlIfc;
import org.sakaiproject.tool.assessment.data.ifc.assessment.AssessmentMetaDataIfc;
import org.sakaiproject.tool.assessment.data.ifc.assessment.EvaluationModelIfc;
import org.sakaiproject.tool.assessment.facade.AgentFacade;
import org.sakaiproject.tool.assessment.facade.AssessmentFacade;
import org.sakaiproject.tool.assessment.facade.PublishedAssessmentFacade;
import org.sakaiproject.tool.assessment.integration.context.IntegrationContextFactory;
import org.sakaiproject.tool.assessment.integration.helper.ifc.GradebookServiceHelper;
import org.sakaiproject.tool.assessment.services.GradingService;
import org.sakaiproject.tool.assessment.services.PersistenceService;
import org.sakaiproject.tool.assessment.services.assessment.AssessmentEntityProducer;
import org.sakaiproject.tool.assessment.services.assessment.AssessmentService;
import org.sakaiproject.tool.assessment.services.assessment.PublishedAssessmentService;
import org.sakaiproject.tool.assessment.ui.bean.author.AssessmentBean;
import org.sakaiproject.tool.assessment.ui.bean.author.AssessmentSettingsBean;
import org.sakaiproject.tool.assessment.ui.bean.author.AuthorBean;
import org.sakaiproject.tool.assessment.ui.bean.author.PublishRepublishNotificationBean;
import org.sakaiproject.tool.assessment.ui.bean.authz.AuthorizationBean;
import org.sakaiproject.tool.assessment.ui.bean.evaluation.TotalScoresBean;
import org.sakaiproject.tool.assessment.ui.listener.util.ContextUtil;
import org.sakaiproject.tool.assessment.util.TextFormat;
import org.sakaiproject.tool.cover.ToolManager;
import org.sakaiproject.util.ResourceLoader;
import org.springframework.web.client.HttpClientErrorException;

/**
 * <p>Title: Samigo</p>2
 * <p>Description: Sakai Assessment Manager</p>
 * @author Ed Smiley
 * @version $Id$
 */
@Slf4j
public class PublishAssessmentListener
    implements ActionListener {

  private static final GradebookServiceHelper gbsHelper =
      IntegrationContextFactory.getInstance().getGradebookServiceHelper();
  private static final boolean integrated =
      IntegrationContextFactory.getInstance().isIntegrated();
  private static final Lock repeatedPublishLock = new ReentrantLock();
  private static boolean repeatedPublish = false;

  private CalendarServiceHelper calendarService = IntegrationContextFactory.getInstance().getCalendarServiceHelper();
  private static final ResourceLoader rl = new ResourceLoader("org.sakaiproject.tool.assessment.bundle.AssessmentSettingsMessages");

  private RubricsService rubricsService;
  private TaskService taskService;
  private SamigoAvailableNotificationService samigoAvailableNotificationService;

  public PublishAssessmentListener() {
    rubricsService = ComponentManager.get(RubricsService.class);
    taskService = ComponentManager.get(TaskService.class);
	samigoAvailableNotificationService = ComponentManager.get(SamigoAvailableNotificationService.class);
  }

  public void processAction(ActionEvent ae) throws AbortProcessingException {
	  repeatedPublishLock.lock();
	  try {

  		//FacesContext context = FacesContext.getCurrentInstance();
  		if (ae == null) {
  			repeatedPublish = false;
  			return;
  		}
  		else {
  			UIComponent eventSource = (UIComponent) ae.getSource();
  			ValueBinding vb = eventSource.getValueBinding("value");
  			if (vb == null) {
  				repeatedPublish = false;
  				return;
  			}
  			else {
  				String buttonValue = (String) vb.getExpressionString(); 
  				if(buttonValue.endsWith(".button_unique_save_and_publish}"))
  				{
  					repeatedPublish = false;
  					return;
  				}
  			}
  		}
  		if(!repeatedPublish)
  		{
  			//Map reqMap = context.getExternalContext().getRequestMap();
  			//Map requestParams = context.getExternalContext().getRequestParameterMap();
  			AuthorBean author = (AuthorBean) ContextUtil.lookupBean(
  			"author");
  			AuthorizationBean authorization = (AuthorizationBean) ContextUtil.lookupBean("authorization");

  			AssessmentSettingsBean assessmentSettings = (AssessmentSettingsBean) ContextUtil.lookupBean("assessmentSettings");

  			AssessmentService assessmentService = new AssessmentService();

  			AssessmentFacade assessment = assessmentService.getAssessment(
  					assessmentSettings.getAssessmentId().toString());

  			// 0. sorry need double checking assesmentTitle and everything
  			boolean error = checkTitle(assessment);
  			if (error){
  				return;
  			}

  			// Tell AuthorBean that we just published an assessment
  			// This will allow us to jump directly to published assessments tab
  			author.setJustPublishedAnAssessment(true);

  			//update any random draw questions from pool since they could have changed
  			int success = assessmentService.updateAllRandomPoolQuestions(assessment, true);
  			if(success == assessmentService.UPDATE_SUCCESS){

  				//grab new updated assessment
  				assessment = assessmentService.getAssessment(assessment.getAssessmentId().toString());	

  				publish(assessment, assessmentSettings);

  				GradingService gradingService = new GradingService();
  				PublishedAssessmentService publishedAssessmentService = new PublishedAssessmentService();
  				AuthorActionListener authorActionListener = new AuthorActionListener();
  				authorActionListener.prepareAssessmentsList(author, authorization, assessmentService, gradingService, publishedAssessmentService);

  				repeatedPublish = true;
  			}else{
  				repeatedPublish = false;

  				FacesContext context = FacesContext.getCurrentInstance();
  				if(success == AssessmentService.UPDATE_ERROR_DRAW_SIZE_TOO_LARGE){  		    		
  					String err=ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.AuthorMessages","update_pool_error_size_too_large");
  					context.addMessage(null,new FacesMessage(err));
  				}else{
  					String err=ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.AuthorMessages","update_pool_error_unknown");
  					context.addMessage(null,new FacesMessage(err));
  				}

  				return;
  			}
  		}
	  } finally{
		  repeatedPublishLock.unlock();

	  }
  }

  private void publish(AssessmentFacade assessment,
                       AssessmentSettingsBean assessmentSettings) {
	PublishedAssessmentService publishedAssessmentService = new PublishedAssessmentService();
    PublishedAssessmentFacade pub = null;

    try {
      assessment.addAssessmentMetaData("ALIAS", assessmentSettings.getAlias());
      pub = publishedAssessmentService.publishAssessment(assessment);

      //Lock the groups for deletion if the assessment is released to groups, students can lose submissions if the group is deleted.
      boolean groupRelease = AssessmentAccessControlIfc.RELEASE_TO_SELECTED_GROUPS.equals(assessmentSettings.getReleaseTo());

      if (groupRelease) {
        try{
            String publishedAssessmentId = String.valueOf(pub.getPublishedAssessmentId());
            PublishedAssessmentFacade publishedAssessment = publishedAssessmentService.getPublishedAssessment(publishedAssessmentId, true);
            Map<String, String> selectedGroups = publishedAssessment.getReleaseToGroups();

            log.debug("Locking groups for deletion by the published assessment with id {}.", publishedAssessmentId);
            log.debug("Locking for deletion the following groups {}.", selectedGroups);

            Site site = SiteService.getSite(ToolManager.getCurrentPlacement().getContext());
            Collection<Group> groups = site.getGroups();

            for(Group group : groups){
                if(selectedGroups.keySet().contains(group.getId())){
                    log.debug("Locking the group {} for deletion by the the published assessment with id {}.", group.getTitle(), publishedAssessmentId);
                    group.setLockForReference(publishedAssessmentId, RealmLockMode.DELETE);
                }
            }

            log.debug("Saving the site after locking the groups for deletion.");
            SiteService.save(site);
        }catch(Exception e){
            log.error("Fatal error locking the groups for deletion {}.", e);
        }
      }

      PublishRepublishNotificationBean publishRepublishNotification = (PublishRepublishNotificationBean) ContextUtil.lookupBean("publishRepublishNotification");
      boolean sendNotification = publishRepublishNotification.getSendNotification();
      String subject = publishRepublishNotification.getNotificationSubject();
      String notificationMessage = getNotificationMessage(publishRepublishNotification, assessmentSettings.getTitle(), assessmentSettings.getReleaseTo(),
                                                            assessmentSettings.getStartDateInClientTimezoneString(), assessmentSettings.getPublishedUrl(),
                                                            assessmentSettings.getDueDateInClientTimezoneString(), assessmentSettings.getTimedHours(), assessmentSettings.getTimedMinutes(),
                                                            assessmentSettings.getUnlimitedSubmissions(), assessmentSettings.getSubmissionsAllowed(), assessmentSettings.getScoringType(),
                                                            assessmentSettings.getFeedbackDelivery(), assessmentSettings.getFeedbackDateInClientTimezoneString(),
                                                            assessmentSettings.getFeedbackEndDateInClientTimezoneString(), assessmentSettings.getFeedbackScoreThreshold(),
                                                            assessmentSettings.getAutoSubmit(), assessmentSettings.getLateHandling(), assessmentSettings.getRetractDateString());
       
      if (sendNotification) {
        sendNotification(pub, publishedAssessmentService, subject, notificationMessage, 
          assessmentSettings.getReleaseTo());
      }

      ExtendedTimeFacade extendedTimeFacade = PersistenceService.getInstance().getExtendedTimeFacade();
      extendedTimeFacade.copyEntriesToPub(pub.getData(), assessmentSettings.getExtendedTimes());

      EventTrackingService.post(EventTrackingService.newEvent(SamigoConstants.EVENT_ASSESSMENT_PUBLISH, "siteId=" + AgentFacade.getCurrentSiteId() + ", assessmentId=" + assessment.getAssessmentId() + ", publishedAssessmentId=" + pub.getPublishedAssessmentId(), true));

      for (Object sectionObj : pub.getSectionSet()){
        PublishedSectionData sectionData = (PublishedSectionData) sectionObj;
        for (Object itemObj : sectionData.getItemSet()){
          PublishedItemData itemData = (PublishedItemData) itemObj;
          EventTrackingService.post(EventTrackingService.newEvent(SamigoConstants.EVENT_PUBLISHED_ASSESSMENT_SAVEITEM, "/sam/" + AgentFacade.getCurrentSiteId() + "/publish, publishedItemId=" + itemData.getItemIdString(), true));

          try {
            Optional<ToolItemRubricAssociation> rubricAssociation = rubricsService.getRubricAssociation(RubricsConstants.RBCS_TOOL_SAMIGO, assessmentSettings.getAssessmentId().toString() + "." + itemData.getOriginalItemId().toString());
            if (rubricAssociation.isPresent()) {
              rubricsService.saveRubricAssociation(RubricsConstants.RBCS_TOOL_SAMIGO, RubricsConstants.RBCS_PUBLISHED_ASSESSMENT_ENTITY_PREFIX + pub.getPublishedAssessmentId().toString() + "." + itemData.getItemIdString(), rubricAssociation.get().getFormattedAssociation());
            }
          } catch(HttpClientErrorException hcee) {
            log.debug("Current user doesn't have permission to get a rubric: {}", hcee.getMessage());
          }
        }
      }

		  //update Calendar Events
      boolean addDueDateToCalendar = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("publishAssessmentForm:calendarDueDate") != null;
      calendarService.updateAllCalendarEvents(pub, assessmentSettings.getReleaseTo(), assessmentSettings.getGroupsAuthorized(), rl.getString("calendarDueDatePrefix") + " ", addDueDateToCalendar, notificationMessage);

      // Create task
      AssessmentBean assessmentBean = (AssessmentBean) ContextUtil.lookupBean("assessmentBean");
      String reference = AssessmentEntityProducer.REFERENCE_ROOT + "/" + AgentFacade.getCurrentSiteId() + "/" + pub.getPublishedAssessmentId();
      Task task = new Task();
      task.setSiteId(AgentFacade.getCurrentSiteId());
      task.setReference(reference);
      task.setSystem(true);
      task.setDescription(pub.getTitle());
      task.setDue((pub.getDueDate() == null ? null : pub.getDueDate().toInstant()));
      SelectItem[] usersMap = assessmentSettings.getUsersInSite();
      Set<String> users = new HashSet<>();
      for(SelectItem item : usersMap) {
        String userId = (String)item.getValue(); 
        if (StringUtils.isNotBlank(userId)) {
          users.add(userId);
        }
      }
      taskService.createTask(task, users, Priorities.HIGH);
        
    } catch (AssignmentHasIllegalPointsException gbe) {
       // Right now gradebook can only accept assessements with totalPoints > 0 
       // this  might change later
        log.warn(gbe.getMessage(), gbe);
        // Add a global message (not bound to any component) to the faces context indicating the failure
        String err=(String)ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.AuthorMessages",
                                                 "gradebook_exception_min_points");
        FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(err));
        throw new AbortProcessingException(gbe);
    } catch (InvalidGradeItemNameException gbe) {
        log.warn(gbe.getMessage(), gbe);
        String err=(String)ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.AuthorMessages",
                                                 "gradebook_exception_title_invalid");
        FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(err));
        throw new AbortProcessingException(gbe);
    } catch (InvalidCategoryException gbe) {
		log.warn("Incorrect gradebook category settings detected when attempting publish assessment: {}", gbe.toString());
		String err = (String)ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.AuthorMessages",
				"gradebook_exception_category_invalid");
		FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(err));
		throw new AbortProcessingException(gbe);
    } catch (Exception e) {
        log.warn(e.getMessage(), e);
        // Add a global message (not bound to any component) to the faces context indicating the failure
        String err=(String)ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.AuthorMessages",
                                                 "gradebook_exception_error");
        FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(err));
        throw new AbortProcessingException(e);
    }

    // Add ALIAS if it doesn't exist
    if ("".equals(assessment.getAssessmentMetaDataByLabel("ALIAS"))) {
      // generate an alias to the pub assessment
      String alias = assessmentSettings.getAlias();
      PublishedMetaData meta = new PublishedMetaData(pub.getData(),
          AssessmentMetaDataIfc.ALIAS, alias);
      publishedAssessmentService.saveOrUpdateMetaData(meta);
    }  

    // Now that everything is updated schedule an open notification email
    samigoAvailableNotificationService.scheduleAssessmentAvailableNotification(String.valueOf(pub.getPublishedAssessmentId()));
  }

  private boolean checkTitle(AssessmentFacade assessment){
    boolean error=false;
    String assessmentName = assessment.getTitle();
    AssessmentService assessmentService = new AssessmentService();
    String assessmentId = assessment.getAssessmentBaseId().toString();

    //#a - look for error: check if core assessment title is unique
    if (assessmentName!=null &&(assessmentName.trim()).equals("")){
      String publish_error=ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.AssessmentSettingsMessages","publish_error_message");
      FacesContext.getCurrentInstance().addMessage(null,new FacesMessage(publish_error));
      error=true;
    }
    
    if (!assessmentService.assessmentTitleIsUnique(assessmentId, TextFormat.convertPlaintextToFormattedTextNoHighUnicode(assessmentName), false)){
      error=true;
      String nameUnique_err=ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.AssessmentSettingsMessages","assessmentName_error");
      FacesContext.getCurrentInstance().addMessage(null,new FacesMessage(nameUnique_err));
    }

    //#b - check if gradebook exist, if so, if assessment title already exists in GB
    org.sakaiproject.grading.api.GradingService g = null;
    if (integrated){
      g = (org.sakaiproject.grading.api.GradingService) SpringBeanLocator.getInstance().
           getBean("org.sakaiproject.grading.api.GradingService");
    }
    String toGradebook = assessment.getEvaluationModel().getToGradeBook();
    try{
      if (toGradebook!=null && toGradebook.equals(EvaluationModelIfc.TO_DEFAULT_GRADEBOOK.toString()) &&
          gbsHelper.isAssignmentDefined(assessmentName, g)){
        error=true;
        String gbConflict_error=ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.AssessmentSettingsMessages","gbConflict_error");
        FacesContext.getCurrentInstance().addMessage(null,new FacesMessage(gbConflict_error));
      }
    }
    catch(Exception e){
      log.warn("external assessment in GB has the same title:"+e.getMessage());
    }
    return error;
  }
  
  public void sendNotification(PublishedAssessmentFacade pub, PublishedAssessmentService service, String subject, String message,
		  String releaseTo) {
	  TotalScoresBean totalScoresBean = (TotalScoresBean) ContextUtil.lookupBean("totalScores");
	  
	  boolean groupRelease = AssessmentAccessControlIfc.RELEASE_TO_SELECTED_GROUPS.equals(releaseTo);
	  if (groupRelease) {
		  totalScoresBean.setSelectedSectionFilterValue(TotalScoresBean.RELEASED_SECTIONS_GROUPS_SELECT_VALUE);
	  }
	  else {
		  totalScoresBean.setSelectedSectionFilterValue(TotalScoresBean.ALL_SECTIONS_SELECT_VALUE);
	  }

	  totalScoresBean.setPublishedId(pub.getPublishedAssessmentId().toString());
	  Map useridMap= totalScoresBean.getUserIdMap(TotalScoresBean.CALLED_FROM_NOTIFICATION_LISTENER, AgentFacade.getCurrentSiteId()); 
	  AgentFacade agent = null;

	  AgentFacade instructor = new AgentFacade();
	  ArrayList<InternetAddress> toIAList = new ArrayList<>();
	  try {
		  toIAList.add(new InternetAddress(instructor.getEmail())); // send one copy to instructor
	  } catch (AddressException e) {
		  log.warn("AddressException encountered when constructing instructor's email.");
	  }
	  Iterator iter = useridMap.keySet().iterator();

	  while (iter.hasNext()) {
		  String userUid = (String) iter.next();
		  agent = new AgentFacade(userUid);
		  InternetAddress ia = null;
		  try {
			  ia = new InternetAddress(agent.getEmail()); 
		  } catch (AddressException e) {
			  log.warn("AddressException encountered when constructing toIAList email. userUid = " + userUid);
		  }
		  if (ia != null) {
			  toIAList.add(ia);
		  }
	  }
	  
	  InternetAddress[] toIA = new InternetAddress[toIAList.size()];
	  int count = 0;
	  Iterator iter2 = toIAList.iterator();
	  while (iter2.hasNext()) {
		  toIA[count++] = (InternetAddress) iter2.next();
	  }

	  String noReplyEmaillAddress =  ServerConfigurationService.getString("setup.request","no-reply@" + ServerConfigurationService.getServerName());
      InternetAddress[] noReply = new InternetAddress[1];
      InternetAddress from = null;
      try {
          from = new InternetAddress(noReplyEmaillAddress);
          noReply[0] = from;
      } catch (AddressException e) {
          log.warn("AddressException encountered when constructing no_reply@serverName email.");
      }
	  
	  List<String> headers = new  ArrayList<String>();
	  headers.add("Content-Type: text/html");
	  EmailService.sendMail(from, toIA, subject, message, noReply, noReply, headers);
  }
  
  public String getNotificationMessage(PublishRepublishNotificationBean publishRepublishNotification, String title, String releaseTo, String startDateString, String publishedURL, String dueDateString,
										Integer timedHours, Integer timedMinutes, String unlimitedSubmissions, String submissionsAllowed, String scoringType, String feedbackDelivery,
										String feedbackDateString, String feedbackEndDateString, String feedbackScoreThreshold, boolean autoSubmitEnabled, String lateHandling,
										String retractDateString) {
	  String siteTitle = publishRepublishNotification.getSiteTitle();
	  if(siteTitle == null || "".equals(siteTitle)){
		  try {
			  Site site = SiteService.getSite(ToolManager.getCurrentPlacement().getContext());
			  siteTitle = site.getTitle();
			  publishRepublishNotification.setSiteTitle(siteTitle);
		  } catch (IdUnusedException iue) {
			  log.warn(iue.getMessage());
		  }
	  }
	  String newline = "<br />\n";
	  String bold_open = "<b>";
	  String bold_close = "</b>";
	  StringBuilder message = new StringBuilder();

	  String prePopulateText = publishRepublishNotification.getPrePopulateText();
	  if (prePopulateText != null && !prePopulateText.trim().equals("") && 
		  (!prePopulateText.trim().equals(rl.getString("pre_populate_text_publish")) && 
		   !prePopulateText.trim().equals(rl.getString("pre_populate_text_republish")) && 
		   !prePopulateText.trim().equals(rl.getString("pre_populate_text_regrade_republish")))) {
		  message.append(TextFormat.convertPlaintextToFormattedTextNoHighUnicode(prePopulateText));
		  message.append(newline);
		  message.append(newline);
	  }

	  message.append("\"");
	  message.append(bold_open);
	  message.append(TextFormat.convertPlaintextToFormattedTextNoHighUnicode(title));
	  message.append(bold_close);
	  message.append("\"");
	  message.append(" ");
	  
	  publishedURL = "<a target=\"_blank\" href=\"" + publishedURL + "\">" + publishedURL + "</a>";
	  if ("Anonymous Users".equals(releaseTo)) {
		  message.append(MessageFormat.format(rl.getString("available_anonymously_at"), startDateString, publishedURL));
	  }
	  else if (AssessmentAccessControlIfc.RELEASE_TO_SELECTED_GROUPS.equals(releaseTo)) {
		  message.append(MessageFormat.format(rl.getString("available_group_at_email"), startDateString, publishedURL));
	  }
	  else {
		  message.append(MessageFormat.format(rl.getString("available_class_at"), startDateString, publishedURL));
	  }
	  
	  if (dueDateString != null && !dueDateString.trim().equals("")) {
		  message.append(newline);
		  message.append(newline);
		  message.append(MessageFormat.format(rl.getString("it_is_due"), dueDateString));
	  }
	  
	  message.append(newline);
	  message.append(newline);

	  // Time limited
	  if (timedHours > 0 || timedMinutes > 0) {
		  message.append(rl.getString("the_time_limit_is"));
		  message.append(" ");
		  message.append(timedHours);
		  message.append(" ");
		  message.append(rl.getString("hours"));
		  if (timedMinutes > 0) {
			  message.append(", ");
			  message.append(timedMinutes);
			  message.append(" ");
			  message.append(rl.getString("minutes"));
		  }
		  message.append(". ");
		  message.append(rl.getString("submit_when_time_is_up"));
	  }
	  else {
		  message.append(rl.getString("there_is_no_time_limit"));
	  }

	  message.append(" ");
	  
	  // Number of submissions
	  if ("1".equals(unlimitedSubmissions)) {
		  message.append(rl.getString("student_submit_unlimited_times"));
	  }
	  else {
		  message.append(MessageFormat.format(rl.getString("student_submit_certain_time"), submissionsAllowed));
	  }

	  // Scoring type
	  message.append(" ");
	  if ("1".equals(scoringType)) {
		  message.append(rl.getString("record_highest"));
	  }
	  else if ("4".equals(scoringType)) {
		message.append(rl.getString("record_average"));
	  }
	  else {
		  message.append(rl.getString("record_last"));
	  }
	  
	  message.append(newline);
	  message.append(newline);

	  // Feedback
	  if ("1".equals(feedbackDelivery)) {
		  message.append(rl.getString("receive_immediate"));
	  }
	  else if ("4".equals(feedbackDelivery)) {
		  message.append(rl.getString("receive_feedback_on_submission"));
	  }
	  else if ("3".equals(feedbackDelivery)) {
		  message.append(rl.getString("receive_no_feedback"));
	  }
	  else {
		if(StringUtils.isNotBlank(feedbackScoreThreshold)){
			//Score threshold is set
			if(StringUtils.isNotBlank(feedbackEndDateString)){
				//Ranged availability
				message.append(MessageFormat.format(rl.getString("feedback_available_ranges_threshold"), feedbackScoreThreshold, feedbackDateString, feedbackEndDateString));
			} else{
				//Not ranged availability
				message.append(MessageFormat.format(rl.getString("feedback_available_on_threshold"), feedbackScoreThreshold, feedbackDateString));
			}
		} else{
			//Score threshold is not set
			if(StringUtils.isNotBlank(feedbackEndDateString)){
				//Ranged availability
				message.append(MessageFormat.format(rl.getString("feedback_available_ranges"), feedbackDateString, feedbackEndDateString));
			} else{
				//Not ranged availability
				message.append(MessageFormat.format(rl.getString("feedback_available_on"), feedbackDateString));
			}
		}
	  }
	  message.append(newline);
	  message.append(newline);

	  // Autosubmit
	  if (autoSubmitEnabled) {
		  String label, date;
		  if ("1".equals(lateHandling)) {
			  label = rl.getString("header_extendedTime_retract_date");
			  date = retractDateString;
		  } else {
			  label = rl.getString("header_extendedTime_due_date");
			  date = dueDateString;
		  }

		  message.append(MessageFormat.format(rl.getString("autosubmit_info"), label, date));
		  message.append(" ").append( MessageFormat.format(rl.getString("autosubmit_info_extended_time"), label));

		  message.append(newline);
		  message.append(newline);
	  }

	  StringBuffer siteTitleSb = new StringBuffer();
	  siteTitleSb.append(" \"");
	  siteTitleSb.append(siteTitle);
	  siteTitleSb.append("\" ");
	  StringBuffer portalUrlSb = new StringBuffer();
	  portalUrlSb.append(" <a href=\"");
	  portalUrlSb.append(ServerConfigurationService.getPortalUrl());
	  portalUrlSb.append("\" target=\"_blank\">");
	  portalUrlSb.append(ServerConfigurationService.getPortalUrl());
	  portalUrlSb.append("</a>");
	  message.append(MessageFormat.format(rl.getString("notification_content"), siteTitleSb.toString(), portalUrlSb.toString()));
	  
	  message.append(newline);
	  message.append(newline);
	  
	  return message.toString();
  }
}
