/*
 * Copyright 2006-2007 Queplix Corp.
 *
 * Licensed under the Queplix Public License, Version 1.1.1 (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.queplix.com/solutions/commercial-open-source/queplix-public-license/
 *
 * 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 com.queplix.core.modules.mail.ejb;

import com.queplix.core.integrator.security.LogonSession;
import com.queplix.core.modules.attachment.ejb.AttachmentManagerLocal;
import com.queplix.core.modules.attachment.ejb.AttachmentManagerLocalHome;
import com.queplix.core.modules.config.utils.SysPropertyManager;
import com.queplix.core.modules.inbox.InboxHelper;
import com.queplix.core.modules.jeo.gen.AttachmentTempObject;
import com.queplix.core.modules.mail.Attachment;
import com.queplix.core.modules.mail.MailAddress;
import com.queplix.core.modules.mail.MailMessage;
import com.queplix.core.modules.mail.utils.MailDataSource;
import com.queplix.core.utils.JNDINames;
import com.queplix.core.utils.StringHelper;
import com.queplix.core.utils.ejb.AbstractSessionEJB;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import java.io.IOException;
import java.util.Date;
import java.util.Enumeration;
import java.util.Properties;

/**
 * E-mail manager EJB.
 *
 * @author [ALB] Baranov Andrey
 * @version $Revision: 1.6 $ $Date: 2006/01/27 17:58:31 $
 */

public class MailManagerEJB
        extends AbstractSessionEJB {

    // =============================================================== Constants

    /**
     * SMTP server property.
     */
    public static final String SERVER_PROPERTY = "SMTP_SERVER";

    /**
     * Mail header to name the mailer software.
     */
    public static final String HEADER_X_MAILER = InboxHelper.HEADER_X_MAILER;

    /**
     * Content type for plain text.
     */
    public static final String CONTENT_TYPE_TEXT
            = "text/plain; charset=\"UTF-8\"";

    /**
     * Content type for HTML.
     */
    public static final String CONTENT_TYPE_HTML
            = "text/html; charset=\"UTF-8\"";

    /**
     * Content type for attachments.
     */
    public static final String CONTENT_TYPE_BINARY = "application/octet-stream";

    // Debug flag.
    private static final boolean MAIL_DEBUG = true;

    // ========================================================= EJB API methods

    /**
     * Initializes bean.
     */
    public void ejbCreate() {
        INFO("MailManager EJB created - " + hashCode());
    }

    // ========================================================== Public methods

    /**
     * Sends the mail.
     *
     * @param ls      user logon session
     * @param message MailMessage object
     * @throws MessagingException
     */
    public void sendMessage(LogonSession ls, MailMessage message)
            throws MessagingException {
        sendMessage(ls, message, null);
    }

    /**
     * Sends the mail.
     *
     * @param ls          user logon session
     * @param message     MailMessage object
     * @param mailHeaders mail headers
     * @throws MessagingException
     */
    public void sendMessage(LogonSession ls,
                            MailMessage message,
                            Properties mailHeaders)
            throws MessagingException {
        sendMessage(ls, message, mailHeaders, null);
    }

    /**
     * Sends the mail.
     *
     * @param ls          user logon session
     * @param message     MailMessage object
     * @param mailHeaders mail headers
     * @param attachments attachments VO array
     * @throws MessagingException
     */
    public void sendMessage(LogonSession ls,
                            MailMessage message,
                            Properties mailHeaders,
                            Attachment[] attachments)
            throws MessagingException {

        // Get attachments from the database.
        Long processId = message.getProcessId();
        AttachmentTempObject[] dbAttachments = null;
        if(processId != null) {
            dbAttachments = getAttachments(processId.longValue(), ls);
        }

        sendMessage(ls, message, mailHeaders, attachments, dbAttachments);
    }

    /**
     * Sends the mail.
     *
     * @param ls            user logon session
     * @param message       MailMessage object
     * @param mailHeaders   mail headers
     * @param attachments   attachments VO array
     * @param dbAttachments DB attachments VO array
     * @throws MessagingException
     */
    public void sendMessage(LogonSession ls,
                            MailMessage message,
                            Properties mailHeaders,
                            Attachment[] attachments,
                            AttachmentTempObject[] dbAttachments)
            throws MessagingException {

        // Check message.
        if(message == null) {
            throw new NullPointerException("Message must not be null.");
        }

        // Prepare array of attachments.
        // Add them to <code>attachments</code>.
        int size = (dbAttachments == null) ? 0:dbAttachments.length;
        int tmpSize = (dbAttachments == null) ? 0:dbAttachments.length;
        if(size > 0) {
            int i;
            if(attachments == null) {
                // Create new array.
                attachments = new Attachment[size];
                i = 0;
            } else {
                // Enlarge old array.
                size += attachments.length;
                i = attachments.length;
                Attachment[] _attachments = new Attachment[size];
                System.arraycopy(attachments, 0, _attachments, 0,
                        attachments.length);
                attachments = _attachments;
            }
            for(int t = 0; t < tmpSize; t++) {
                AttachmentTempObject obj = dbAttachments[t];
                attachments[i] = new Attachment(obj);
                i++;
            }
        }

        // Init count of attachments.
        size = (attachments == null) ? 0:attachments.length;

        try {
            // Get a mail session.
            Session session = getMailSession();

            // Create a message with 'multipart/mixed' Multipart.
            Message msg = new MimeMessage(session);

            Multipart mixedMultipart = new MimeMultipart();
            msg.setContent(mixedMultipart);

            MimeBodyPart mixedBodyPart = new MimeBodyPart();
            mixedMultipart.addBodyPart(mixedBodyPart);

            // Prepare Multipart for content attachments and
            Multipart attachMultipart;
            // .. MimeBodyPart for message body
            MimeBodyPart messageBodyPart;
            if(size > 0) {
                // Have attachments -
                // create new 'multipart/related' MimeMultipart.
                attachMultipart = new MimeMultipart("related");
                mixedBodyPart.setContent(attachMultipart);

                MimeBodyPart attachBodyPart = new MimeBodyPart();
                attachMultipart.addBodyPart(attachBodyPart);

                // Create new 'multipart/alternative' MimeMultipart.
                MimeMultipart alterMultipart = new MimeMultipart("alternative");
                attachBodyPart.setContent(alterMultipart);

                messageBodyPart = new MimeBodyPart();
                alterMultipart.addBodyPart(messageBodyPart);

            } else {
                attachMultipart = mixedMultipart;
                messageBodyPart = mixedBodyPart;
            }

            // Set current JVM date.
            msg.setSentDate(new Date());

            // Set headers, if any.
            if(mailHeaders != null) {
                Enumeration en = mailHeaders.keys();
                while(en.hasMoreElements()) {
                    String headerName = (String) en.nextElement();
                    String headerValue = mailHeaders.getProperty(headerName);
                    msg.setHeader(headerName, headerValue);
                }
            }

            // Set X-Mailer header.
            msg.setHeader(HEADER_X_MAILER, InboxHelper.getMailer());

            if(getLogger().isDebugEnabled()) {
                DEBUG("Sending mail from '" + message.getFrom() + "'");
            }

            // 1) Get 'from' attribute.
            if(message.getFrom() != null) {
                msg.setFrom(message.getFrom().toInternetAddress());
            } else {
                String defaultSender = InboxHelper.getDefSender(ls);
                msg.setFrom(new MailAddress(defaultSender).toInternetAddress());
            }

            // 2) Get 'to' attribute.
            if(message.getTo() != null) {
                for(int i = 0; i < message.getTo().length; i++) {
                    msg.addRecipient(Message.RecipientType.TO,
                            message.getTo()[i].toInternetAddress());
                }
            } else {
                throw new NullPointerException(
                        "Please specify receiver e-mail");
            }

            // 3) Get 'cc' attribute.
            if(message.getCc() != null) {
                for(int i = 0; i < message.getCc().length; i++) {
                    msg.addRecipient(Message.RecipientType.CC,
                            message.getCc()[i].toInternetAddress());
                }
            }

            // 4) Get 'bcc' attribute.
            if(message.getBcc() != null) {
                for(int i = 0; i < message.getBcc().length; i++) {
                    msg.addRecipient(Message.RecipientType.BCC,
                            message.getBcc()[i].toInternetAddress());
                }
            }

            // 5) Get 'subject' attribute.
            if(message.getSubject() != null) {
                msg.setSubject(message.getSubject());
            } else {
                msg.setSubject("");
            }

            // 6) Get 'body' attribute.
            String mimeType =
                    (message.getBodyType() == MailMessage.BODYTYPE_HTML)
                            ? CONTENT_TYPE_HTML:CONTENT_TYPE_TEXT;

            if(message.getBody() != null) {
                messageBodyPart.setContent(message.getBody(), mimeType);
            } else {
                messageBodyPart.setContent("", mimeType);
            }

            // 7) Check for attachments.
            for(int i = 0; i < size; i++) {
                Attachment obj = attachments[i];
                String fileName = obj.getFilename();

                DataSource ds;
                if(obj.isLoaded()) {
                    // Take file data directly.
                    if(!StringHelper.isEmpty(obj.getFiletype())) {
                        ds = new MailDataSource(obj.getData(),
                                obj.getFiletype());
                    } else {
                        ds = new MailDataSource(obj.getData(),
                                CONTENT_TYPE_BINARY);
                    }
                } else {
                    // Use File Data Source to prevent OutOfMemory exception
                    ds = new FileDataSource(obj.getFile());
                }

                // Create new MimeBodyPart and set DataSource.
                MimeBodyPart attachmentBodyPart = new MimeBodyPart();
                attachmentBodyPart.setDataHandler(new DataHandler(ds));

                boolean isContentAttachment = false;
                if(fileName != null) {
                    // Set file name.
                    String normFileName = InboxHelper.normalizeAttachmentName(
                            fileName, i);
                    attachmentBodyPart.setFileName(normFileName);
                    attachmentBodyPart.setDescription(normFileName);

                    isContentAttachment = InboxHelper.isContentAttachment(
                            fileName);
                    if(isContentAttachment) {
                        /*
                         * Content Attachment!
                         * Valid header set example:
                         *
                         * Content-Type: image/gif; name="image003.gif"
                         * Content-Transfer-Encoding: base64
                         * Content-ID: <image003.gif@01C6235A.3A394B60>
                         * Content-Description: image003.gif
                         * Content-Location: image003.gif
                         */
                        String contentId = InboxHelper.getContentId(fileName);
                        if(!StringHelper.isEmpty(contentId)) {
                            // Set "Content-ID".
                            attachmentBodyPart.setHeader("Content-ID",
                                    contentId);
                        }
                    }

                } else {
                    // Generate new file name.
                    String normFileName = InboxHelper.generateUniqueAttachName(
                            i);
                    attachmentBodyPart.setFileName(normFileName);
                }

                // Add attachment to a message.
                if(isContentAttachment) {
                    // .. to special attachment Multipart
                    attachMultipart.addBodyPart(attachmentBodyPart);
                } else {
                    // .. directly to the message
                    mixedMultipart.addBodyPart(attachmentBodyPart);
                }

                if(getLogger().isDebugEnabled()) {
                    DEBUG("The attachment # " + i + " received, file = "
                            + fileName);
                }
            }

            // Send a message.
            Transport.send(msg);

        } catch (MessagingException ex) {
            ERROR(ex);
            throw ex;

        } catch (IOException ex) {
            throwException(
                    "Can't get content. Encoding exception: " + ex.getMessage(),
                    ex);
        }
    }

    // ========================================================= Private methods

    // Gets a mail session.

    private Session getMailSession() {

        // Initialize the mail session.
        String smtpHost = SysPropertyManager.getProperty(SERVER_PROPERTY);
        if(smtpHost == null) {
            throwException("No SMTP server specified in property file.");
        }

        // Store the properties.
        Properties props = new Properties();
        props.put("mail.smtp.host", smtpHost);
        props.put("mail.debug", "" + MAIL_DEBUG);

        Session session = Session.getInstance(props);
        session.setDebug(MAIL_DEBUG);

        INFO("Smtp host now: " + smtpHost);
        return session;

    }

    // Gets an array of temporary attachment value objects.
    private AttachmentTempObject[] getAttachments(long process_id,
                                                  LogonSession ls) {
        return getAttachmentManager().getTempAttachments(ls, process_id);
    }

    // Gets Attachment Manager EJB reference.
    private AttachmentManagerLocal getAttachmentManager() {
        return (AttachmentManagerLocal) getLocalObject(
                JNDINames.AttachmentManager, AttachmentManagerLocalHome.class);
    }

}
