/*
 * @(#)HtmlEmailMessage.java
 *
 * Copyright (c) 2003 JSCAPE
 * 1147 S. 53rd Pl., Mesa, Arizona, 85206, U.S.A.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * JSCAPE. ("Confidential Information").  You shall not disclose such
 * Confidential Information and shall use it only in accordance with
 * the terms of the license agreement you entered into with JSCAPE.
 */
package com.jscape.inet.email;

import com.jscape.inet.mime.Attachment;
import com.jscape.inet.mime.MimeException;
import com.jscape.inet.mime.MimeHeader;
import com.jscape.inet.mime.MimeMessage;
import com.jscape.util.IOTools;

import javax.activation.MimetypesFileTypeMap;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
import java.util.Hashtable;


/**
 * Represents an email message that contains both HTML and plain text parts.
 * <p/>
 * Example Usage:<P>
 * <pre>
 * HtmlEmailMessage message = new HtmlEmailMessage();
 * message.setTo("jsmith@myserver.com");
 * message.setFrom("rjones@myserver.com");
 * message.setSubject("Meeting at 8");
 * message.setTextBody("this is the text body");
 * message.setHtmlBody("&lt;html&gt;&lt;body&gt;&lt;img src=\"cid:12345\"&gt;&lt;br&gt;this is the html body&lt;/body&gt;&lt;/html&gt;");
 * message.embed(new URL("http://www.domain.com/image.gif","12345");
 * </pre>
 */
public class HtmlEmailMessage
        extends EmailMessage {
    private Hashtable images = new Hashtable();
    private String textBody = "";
    private String htmlBody = "";
    private boolean build = true;

    /**
     * Creates a new HtmlEmailMessage instance.
     *
     * @throws MimeException if a MIME related error occurs
     */
    public HtmlEmailMessage()
            throws MimeException {
        super();
    }

    /**
     * Adds attachment to email message.
     * Invokes the parent EmailMessage#addAttachment(Attachment,boolean) method providing the <code>attachment</code>
     * and <code>false</code> as method argument to ensure that existing body is not added as a separate MIME part.
     *
     * @param att the attachment to add
     *
     * @throws MimeException if a MIME related error occurs
     */
    public void addAttachment(Attachment att)
            throws MimeException {
        super.addAttachment(att, false);
    }

    /**
     * Sets the plain text portion of this email message.
     * Text portion will be displayed by mail clients that are not capable of displaying HTML or have
     * HTML display disabled.
     *
     * @param textBody the plain text portion of this message
     */
    public void setTextBody(String textBody) {
        this.textBody = textBody;
    }

    /**
     * Sets the HTML portion of this email message.
     * HTML portion will be displayed by mail clients that are capable of displaying HTML and have
     * HTML display enabled.
     *
     * @param htmlBody the HTML portion of this message
     */
    public void setHtmlBody(String htmlBody) {
        this.htmlBody = htmlBody;
    }

    /**
     * Sets the HTML portion of this email message from contents of a file.
     * HTML portion will be displayed by mail clients that are capable of displaying HTML and have
     * HTML display enabled.
     *
     * @param htmlBody file containing HTML body
     *
     * @throws IOException if an I/O error occurs
     */
    public void setHtmlBody(File htmlBody)
            throws IOException {
        byte[] data = getBytesFromFile(htmlBody);
        this.htmlBody = new String(data, StandardCharsets.UTF_8);
    }

    /**
     * Sets the plain text portion of this email message from contents of a file.
     * Text portion will be displayed by mail clients that are not capable of displaying HTML or have
     * HTML display disabled.
     *
     * @param textBody file containing text body
     *
     * @throws IOException if an I/O error occurs
     */
    public void setTextBody(File textBody)
            throws IOException {
        byte[] data = getBytesFromFile(textBody);
        this.textBody = new String(data, StandardCharsets.UTF_8);
    }

    /**
     * Embeds a resource in the email message.
     *
     * @param url the URL of the resource
     * @param id  the unique Content-ID header value of the resource referenced in the HTML code
     */
    @SuppressWarnings("unchecked")
    public void embed(URL url, String id) {
        images.put(id, url);
    }

    /**
     * Embeds a resource in the email message.
     *
     * @param file the resource file
     * @param id   the unique Content-ID header value of the resource referenced in the HTML code
     */
    @SuppressWarnings("unchecked")
    public void embed(File file, String id) {
        images.put(id, file);
    }

    /**
     * Gets contents of this <code>HtmlEmailMessage</code>. Contents include all
     * MIME message headers, body and any parts.
     *
     * @return message contents
     *
     * @throws MimeException if a MIME related parsing error occurs
     */
    public byte[] getMessage()
            throws MimeException {
        if (build) {
            MimeMessage contents = new MimeMessage();
            contents.setContentType("multipart/alternative");

            try {
                MimeMessage plainMessage = new MimeMessage();
                plainMessage.setContentType("text/plain");

                MimeHeader cte = new MimeHeader("Content-Transfer-Encoding", "BASE64");
                plainMessage.addHeader(cte);
                plainMessage.setBody(textBody, StandardCharsets.UTF_8.name());
                contents.addPart(plainMessage);
            } catch (Exception e) {
                System.out.println(e);
            }

            try {
                MimeMessage htmlMessage = new MimeMessage();
                htmlMessage.setContentType("text/html");
                MimeHeader cte = new MimeHeader("Content-Transfer-Encoding", "BASE64");
                htmlMessage.addHeader(cte);
                htmlMessage.setBody(htmlBody, StandardCharsets.UTF_8.name());
                contents.addPart(htmlMessage);
            } catch (Exception e) {
                System.out.println(e);
            }

            try {
                Enumeration ids = images.keys();

                while (ids.hasMoreElements()) {
                    String id = (String) ids.nextElement();
                    String ct = "";

                    ByteArrayOutputStream bout = new ByteArrayOutputStream();
                    InputStream in = null;
                    Object obj = images.get(id);

                    if (obj instanceof URL) {
                        URL url = (URL) images.get(id);
                        URLConnection conn = url.openConnection();
                        ct = conn.getContentType();
                        in = conn.getInputStream();
                    } else {
                        File file = (File) images.get(id);
                        in = new FileInputStream(file);

                        MimetypesFileTypeMap map = new MimetypesFileTypeMap();
                        ct = map.getContentType(file.getName());
                    }

                    try {
                        int i;
                        while ((i = in.read()) != -1) {
                            bout.write(i);
                        }
                    } finally {
                        IOTools.close(in);
                    }

                    MimeMessage image = new MimeMessage();

                    if (ct == null) {
                        ct = "application/x-unknown";
                    }

                    image.setContentType(ct);
                    image.addHeader("Content-ID", "<" + id + ">");

                    MimeHeader cte = new MimeHeader("Content-Transfer-Encoding",
                                                    "BASE64");
                    image.addHeader(cte);
                    image.setBody(bout.toByteArray());
                    super.addPart(image);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

            super.addPart(contents);
            build = false;
        }

        return super.getMessage();
    }

    /**
     * Gets bytes from specified file.
     *
     * @param file the file to read from
     *
     * @return the file data as byte[]
     *
     * @throws IOException if an I/O related error occurs
     */
    private byte[] getBytesFromFile(File file)
            throws IOException {
        FileInputStream fin = null;

        try {
            fin = new FileInputStream(file);
            ByteArrayOutputStream bout = new ByteArrayOutputStream();

            int i;
            while ((i = fin.read()) != -1) {
                bout.write(i);
            }

            return bout.toByteArray();
        } finally {
            IOTools.close(fin);
        }
    }
}
