/* BdfServer_Email - Copyright (c) 2011-2021 Vincent Calame - Exemole
 * Logiciel libre donné sous triple licence :
 * 1) selon les termes de la CeCILL V2
 * 2) selon les termes de l’EUPL V.1.1
 * 3) selon les termes de la GNU GPLv3
 * Voir le fichier licences.txt
 */


package fr.exemole.bdfserver.email;

import fr.exemole.bdfserver.api.BdfServerConstants;
import fr.exemole.bdfserver.api.instruction.BdfParameters;
import fr.exemole.bdfserver.api.interaction.domains.MailingDomain;
import fr.exemole.bdfserver.api.users.BdfUser;
import fr.exemole.bdfserver.email.datasource.SimpleTemplateDataSource;
import fr.exemole.bdfserver.email.datasource.StreamDataSourceFactory;
import fr.exemole.bdfserver.email.datasource.TableExportDataSource;
import fr.exemole.bdfserver.email.datasource.VersionDataSource;
import java.util.Collection;
import java.util.Set;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Part;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import net.fichotheque.SubsetItem;
import net.fichotheque.addenda.Version;
import net.fichotheque.corpus.Corpus;
import net.fichotheque.corpus.FicheMeta;
import net.fichotheque.utils.CorpusUtils;
import net.mapeadores.util.email.EmailUtils;
import net.mapeadores.util.mimetype.MimeTypeUtils;
import net.mapeadores.util.text.ValidExtension;


/**
 *
 * @author Vincent Calame
 */
public class SendEngine {

    private final SmtpManager smtpManager;
    private final BdfParameters bdfParameters;

    public SendEngine(SmtpManager smtpManager, BdfParameters bdfParameters) {
        this.smtpManager = smtpManager;
        this.bdfParameters = bdfParameters;
    }

    public SendReport sendEmail(EmailBuffer emailBuffer) {
        Session session = EmailUtils.createSession(smtpManager.getSmtpParameters());
        MimeMessage message = new MimeMessage(session);
        SendReport okReport;
        try {
            ConversionEngine conversionEngine = new ConversionEngine(emailBuffer, message);
            okReport = conversionEngine.run();
        } catch (MessagingException e) {
            return SendReport.emailBuildErrorReport(e.getMessage());
        }
        try {
            Transport.send(message);
            return okReport;
        } catch (MessagingException e) {
            return SendReport.sendErrorReport(e.getMessage());
        }
    }

    public static SendEngine build(BdfParameters bdfParameters) {
        return new SendEngine((SmtpManager) bdfParameters.getBdfServer().getContextObject(BdfServerConstants.SMTPMANAGER_CONTEXTOBJECT), bdfParameters);
    }

    public static boolean canSend(BdfParameters bdfParameters) {
        SmtpManager smtpManager = (SmtpManager) bdfParameters.getBdfServer().getContextObject(BdfServerConstants.SMTPMANAGER_CONTEXTOBJECT);
        if (!smtpManager.isInit()) {
            return false;
        }
        if (bdfParameters.getBdfUser().getRedacteur().getEmailCore() == null) {
            return false;
        }
        return true;
    }


    private class ConversionEngine {

        private final MimeMessage mimeMessage;
        private final EmailBuffer emailBuffer;
        private final SendReport okReport;
        private final Multipart root;

        private ConversionEngine(EmailBuffer emailBuffer, MimeMessage mimeMessage) {
            this.mimeMessage = mimeMessage;
            this.emailBuffer = emailBuffer;
            this.okReport = SendReport.okReport();
            this.root = new MimeMultipart();
        }

        private SendReport run() throws MessagingException {
            convertHeaders();
            switch (emailBuffer.getSendType()) {
                case MailingDomain.FICHE_SENDTYPE:
                    convertFiche();
                    break;
                case MailingDomain.COMPILATION_SENDTYPE:
                    convertCompilation();
                    break;
                case MailingDomain.TABLEEXPORT_SENDTYPE:
                    convertTableExport();
                    break;
                case MailingDomain.SELECTION_SENDTYPE:
                    convertSelection();
                    break;
                default:
                    insertMessage();
            }
            mimeMessage.setContent(root);
            return okReport;
        }

        private void convertHeaders() throws MessagingException {
            mimeMessage.setFrom(EmailUtils.convert(emailBuffer.getFromEmail()));
            mimeMessage.setReplyTo(EmailUtils.convert(emailBuffer.getReplytoCollection()));
            mimeMessage.setRecipients(Message.RecipientType.TO, EmailUtils.convert(emailBuffer.getToCollection()));
            mimeMessage.setRecipients(Message.RecipientType.CC, EmailUtils.convert(emailBuffer.getCcCollection()));
            mimeMessage.setRecipients(Message.RecipientType.BCC, EmailUtils.convert(emailBuffer.getBccCollection()));
            mimeMessage.setSubject(emailBuffer.getSubject(), "utf-8");
        }

        private void convertFiche() throws MessagingException {
            FicheMeta ficheMeta = (FicheMeta) emailBuffer.getSendObject();
            FicheAttachmentParameters ficheAttachmentParameters = emailBuffer.getFicheAttachmentParameters();
            insertMessage();
            if (ficheAttachmentParameters.isWithHtml()) {
                SimpleTemplateDataSource simpleTemplateDataSource = SimpleTemplateDataSource.newFicheInstance(bdfParameters, ficheMeta, ficheAttachmentParameters.getHtmlTemplateName());
                insert(simpleTemplateDataSource);
                //attach(simpleTemplateDataSource); joindre le fichier HTML semble poser problème dans certains cas pour des raisons inconnues (javax.mail)
            }
            if (ficheAttachmentParameters.isWithOdt()) {
                attach(StreamDataSourceFactory.newFicheInstance(bdfParameters, ficheMeta, ValidExtension.ODT, ficheAttachmentParameters.getOdtTemplateName()));
            }
            for (Version version : ficheAttachmentParameters.getVersionCollection()) {
                attach(VersionDataSource.newInstance(bdfParameters.getBdfServer(), version));
            }
        }

        private void convertCompilation() throws MessagingException {
            BdfUser bdfUser = bdfParameters.getBdfUser();
            FicheAttachmentParameters ficheAttachmentParameters = emailBuffer.getFicheAttachmentParameters();
            insertMessage();
            if (ficheAttachmentParameters.isWithHtml()) {
                SimpleTemplateDataSource simpleTemplateDataSource = SimpleTemplateDataSource.newCompilationInstance(bdfParameters, bdfUser.getSelectedFiches(), ficheAttachmentParameters.getHtmlTemplateName());
                insert(simpleTemplateDataSource);
                attach(simpleTemplateDataSource);
            }
            if (ficheAttachmentParameters.isWithOdt()) {
                attach(StreamDataSourceFactory.newCompilationInstance(bdfParameters, bdfUser.getSelectedFiches(), ValidExtension.ODT, ficheAttachmentParameters.getOdtTemplateName()));
            }
        }

        private void convertSelection() throws MessagingException {
            FicheAttachmentParameters ficheAttachmentParameters = emailBuffer.getFicheAttachmentParameters();
            Set<FicheMeta> ficheMetaSet = (Set<FicheMeta>) emailBuffer.getSendObject();
            insertMessage();
            for (FicheMeta ficheMeta : ficheMetaSet) {
                if (ficheAttachmentParameters.isWithHtml()) {
                    SimpleTemplateDataSource simpleTemplateDataSource = SimpleTemplateDataSource.newFicheInstance(bdfParameters, ficheMeta, ficheAttachmentParameters.getHtmlTemplateName());
                    insert(simpleTemplateDataSource);
                    attach(simpleTemplateDataSource);
                }
                if (ficheAttachmentParameters.isWithOdt()) {
                    attach(StreamDataSourceFactory.newFicheInstance(bdfParameters, ficheMeta, ValidExtension.ODT, ficheAttachmentParameters.getOdtTemplateName()));
                }
            }

        }

        private void convertTableExport() throws MessagingException {
            TableExportEmail tableExportEmail = (TableExportEmail) emailBuffer.getSendObject();
            Corpus corpus = tableExportEmail.getCorpus();
            if (!bdfParameters.getPermissionSummary().hasAccess(corpus)) {
                return;
            }
            Collection<SubsetItem> subsetItemList = CorpusUtils.getFicheMetaListByCorpus(bdfParameters.getBdfUser().getSelectedFiches(), corpus.getSubsetKey());
            DataSource tableExportDataSource = TableExportDataSource.build(bdfParameters, corpus, subsetItemList, tableExportEmail.getTableExportParameters(), tableExportEmail.getExtension(), tableExportEmail.getCharset());
            insertMessage();
            attach(tableExportDataSource);
        }

        private void insertMessage() throws MessagingException {
            ConversionEngine.this.insertMessage(root);
        }

        private void insertMessage(Multipart mp) throws MessagingException {
            MimeBodyPart part = new MimeBodyPart();
            part.setText(emailBuffer.getMessage(), "utf-8");
            mp.addBodyPart(part);
        }

        private void insert(SimpleTemplateDataSource simpleTemplateDataSource) throws MessagingException {
            if (simpleTemplateDataSource.getMimeType().equals(MimeTypeUtils.HTML)) {
                MimeBodyPart htmlPart = new MimeBodyPart();
                htmlPart.setText(simpleTemplateDataSource.getContent(), "utf-8", "html");
                root.addBodyPart(htmlPart);
            }
        }

        private void insertVariante(SimpleTemplateDataSource simpleTemplateDataSource) throws MessagingException {
            if (simpleTemplateDataSource.getMimeType().equals(MimeTypeUtils.HTML)) {
                Multipart alternative = new MimeMultipart("alternative");
                insertMessage(alternative);
                mergeMessageInHtml(alternative, simpleTemplateDataSource.getContent());
                addSubMultipart(alternative, root);
            } else {
                insertMessage();
            }
            attach(simpleTemplateDataSource);
        }

        private void mergeMessageInHtml(Multipart mp, String html) throws MessagingException {
            int idx = html.indexOf("<body");
            if (idx != -1) {
                idx = html.indexOf('>', idx + 1);
            }
            if (idx != -1) {
                html = html.substring(0, idx + 1) + getPre() + html.substring(idx + 1);
            } else {
                html = getPre() + html;
            }
            MimeBodyPart htmlPart = new MimeBodyPart();
            htmlPart.setText(html, "utf-8", "html");
            mp.addBodyPart(htmlPart);
        }

        private void addSubMultipart(Multipart sub, Multipart parent) throws MessagingException {
            MimeBodyPart part = new MimeBodyPart();
            part.setContent(sub);
            parent.addBodyPart(part);
        }

        private void attach(DataSource dataSource) throws MessagingException {
            okReport.addAttachment(dataSource.getName());
            MimeBodyPart part = new MimeBodyPart();
            part.setDisposition(Part.ATTACHMENT);
            part.setFileName(dataSource.getName());
            part.setDataHandler(new DataHandler(dataSource));
            root.addBodyPart(part);
        }

        private String getPre() {
            String text = emailBuffer.getMessage();
            StringBuilder buf = new StringBuilder();
            buf.append("<pre>");
            int length = text.length();
            for (int i = 0; i < length; i++) {
                escape(text.charAt(i), buf);
            }
            buf.append("</pre><br>");
            return buf.toString();
        }

    }

    private static void escape(char carac, StringBuilder buf) {
        switch (carac) {
            case '&':
                buf.append("&amp;");
                break;
            case '"':
                buf.append("&quot;");
                break;
            case '<':
                buf.append("&lt;");
                break;
            case '>':
                buf.append("&gt;");
                break;
            case '\'':
                buf.append("&#x27;");
                break;
            case '\\':
                buf.append("&#x5C;");
                break;
            case '`':
                buf.append("&#x60;");
                break;
            default:
                buf.append(carac);
        }
    }

}
