/**
 * Copyright (c) 2007-2012 EBM WebSourcing, 2012-2024 Linagora
 *
 * This program/library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 2.1 of the License, or (at your
 * option) any later version.
 *
 * This program/library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program/library; If not, see http://www.gnu.org/licenses/
 * for the GNU Lesser General Public License version 2.1.
 */
package org.ow2.petals.bc.mail;

import java.nio.charset.Charset;
import java.util.Date;
import java.util.Set;
import java.util.logging.Logger;

import javax.activation.DataHandler;
import javax.jbi.messaging.NormalizedMessage;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.internet.ContentType;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;

import org.ow2.petals.bc.mail.exception.MissingElementException;
import org.ow2.petals.bc.mail.service.provide.ProvideDescriptor;
import org.ow2.petals.component.framework.api.message.Exchange;

import com.sun.mail.smtp.SMTPMessage;

/**
 * This class is used to process (reading or creating) mail messages.
 * 
 * @author Adrien LOUIS - EBM WebSourcing
 * @author Olivier Fabre - EBM WebSourcing
 */
public class MimeMessageManager {

    private Logger log;

    /**
     *
     * @param log
     *            the component {@link Logger}
     */
    public MimeMessageManager(final Logger log) {
        this.log = log;
    }

    /**
     * Create a {@link SMTPMessage} from the given {@link NormalizedMessage}, including mail {@link Session}
     * information.
     *
     * @param session
     *            the mail {@link Session}
     * @param provideDescriptor
     *            the {@link ProvideDescriptor}
     * @param exchange
     *            the mapped {@link NormalizedMessage}
     * @param mailBody
     *            The mail body message
     * @return the created {@link SMTPMessage}
     * @throws javax.jbi.messaging.MessagingException
     *             if an error occurs during e-mail messafe creation
     * @throws MissingElementException
     */
    public SMTPMessage buildSMTPMessage(final Session session, final ProvideDescriptor provideDescriptor,
            final Exchange exchange, final String mailBody)
            throws javax.jbi.messaging.MessagingException, MissingElementException {

        this.log.fine("create email from JBI Exchange");

        // Set MimeMessage attributes
        final SMTPMessage smtpMessage = this.initSMTPMessage(session, provideDescriptor.getToAddress(),
                provideDescriptor.getCcAddress(), provideDescriptor.getBccAddress(),
                provideDescriptor.getFromAddress(), provideDescriptor.getReplyAddress(),
                provideDescriptor.getSubject());

        try {
            // Extract and encode the content

            final ContentType mailContentType = new ContentType(provideDescriptor.getContentType());
            mailContentType.setParameter("charset", Charset.defaultCharset().displayName());

            final String sendMode = provideDescriptor.getSendMode();
            if (exchange.getInMessageAttachmentNames().isEmpty()
                    || MailConstants.SEND_MODE_CONTENTONLY.equalsIgnoreCase(sendMode)) {
                // No attachment to set
                smtpMessage.setContent(mailBody, mailContentType.toString());
            } else {
                // There are attachments, we use multi-parts
                final Multipart multipart = new MimeMultipart();
                if (!sendMode.equalsIgnoreCase(MailConstants.SEND_MODE_ATTACHMENTSONLY)) {
                    try {
                        final MimeBodyPart messageBodyPart = new MimeBodyPart();
                        messageBodyPart.setContent(mailBody, mailContentType.toString());
                        multipart.addBodyPart(messageBodyPart);
                    } catch (final MessagingException e) {
                        throw new javax.jbi.messaging.MessagingException(e);
                    }
                }

                this.setMimeMessageAttachments(multipart, exchange);
                smtpMessage.setContent(multipart);
            }
        } catch (final javax.mail.MessagingException e) {
            final String msg = "Error setting Mime message content"
                    + "with all information collected from NormalizedMessage";
            throw new javax.jbi.messaging.MessagingException(msg, e);
        }

        return smtpMessage;
    }

    /**
     * Add {@link NormalizedMessage} attachments to {@link MimeMessage} attachments. If an error occurs during the
     * transmission of an attachment, it is skipped and the process continues with other attachments.
     *
     * @param multipart
     *            the {@link Multipart} used to handle attachments
     * @param exchange
     *            the exchange containing the {@link NormalizedMessage} 'IN' handling attachments that have to be transmitted
     */
    protected void setMimeMessageAttachments(Multipart multipart, Exchange exchange)
            throws javax.jbi.messaging.MessagingException {
        this.log.fine("set the email attachments");
        NormalizedMessage in = exchange.getInMessage();
        final Set<String> attachmentIds = in.getAttachmentNames();
        MimeBodyPart attachementBodyPart = null;
        DataHandler handler = null;
        if (attachmentIds != null) {
            for (final String id : attachmentIds) {
                handler = in.getAttachment(id);
                this.log.fine("add " + id + " as MimeMessage attachment.");
                attachementBodyPart = new MimeBodyPart();
                try {
                    attachementBodyPart.setDataHandler(handler);
                    attachementBodyPart.setFileName(id);
                    multipart.addBodyPart(attachementBodyPart);
                } catch (final javax.mail.MessagingException e) {
                    final String msg = "Error setting MimeMessage attachments " + id;
                    this.log.warning(msg);
                }
            }
        }
    }

    /**
     * Sets {@link MimeMessage} recipient, date, from and subject.
     *
     * @param session
     *            the mail {@link Session}
     * @param toAddresses
     *            the e-mail addresses of recipients "To" (primary)
     * @param ccAddresses
     *            the e-mail addresses of recipients "Cc" (carbon copy)
     * @param bccAddresses
     *            the e-mail addresses of recipients "Bcc" (blind carbon copy)
     * @param fromAddress
     *            the sender e-mail address
     * @param replyAddress
     *            the reply e-mail address
     * @param subject
     *            the mail subject
     * @return The SMTP message correctly initialized with provided information
     */
    private SMTPMessage initSMTPMessage(final Session session, final InternetAddress[] toAddresses,
            final InternetAddress[] ccAddresses, final InternetAddress[] bccAddresses, final String fromAddress,
            final String replyAddress, final String subject) throws javax.jbi.messaging.MessagingException {
        this.log.fine("Initialize the e-mail messafe.");
        final SMTPMessage smtpMessage = new SMTPMessage(session);

        try {
            // Build the sender address
            final InternetAddress fromInternetAddress = new InternetAddress(fromAddress);
            smtpMessage.setRecipients(Message.RecipientType.TO, toAddresses);
            smtpMessage.setRecipients(Message.RecipientType.CC, ccAddresses);
            smtpMessage.setRecipients(Message.RecipientType.BCC, bccAddresses);
            smtpMessage.setSentDate(new Date());
            smtpMessage.setFrom(fromInternetAddress);
            smtpMessage.setSubject(subject);
            if (replyAddress != null) {
                // Build the reply address
                final InternetAddress[] replyInternetAddresses = new InternetAddress[] {
                        new InternetAddress(replyAddress) };
                smtpMessage.setReplyTo(replyInternetAddresses);
            }

            return smtpMessage;

        } catch (final javax.mail.MessagingException e) {
            final String msg = "Error setting SMTPMessage attributes";
            throw new javax.jbi.messaging.MessagingException(msg, e);
        }
    }
}
