/*
 * @(#)Attachment.java
 *
 * Copyright (c) 2001-2002 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.mime;

import javax.activation.MimeType;
import javax.activation.MimeTypeParseException;
import javax.activation.MimetypesFileTypeMap;
import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;


/**
 * Implements methods for creating an attachment to be added to a multi-part
 * <code>MimeMessage</code>.
 * <p/>
 * Example Usage:
 * <p/>
 * <pre>
 * Attachment att = new Attachment(new File("c:/tmp/image.gif"));
 * </pre>
 */
public class Attachment
        extends MimeMessage {
    private String filename;
    private String characterEncoding;

    /**
     * Constructs a new <code>Attachment</code> instance.
     *
     * @param file              the file to be attached
     * @param characterEncoding the character encoding of the attachment
     *                          filename
     *
     * @throws IOException   if an I/O error occurs
     * @throws MimeException if a MIME related parsing error occurs
     */
    public Attachment(File file, String characterEncoding)
            throws IOException, MimeException {
        super();
        this.filename = file.getName();
        this.characterEncoding = characterEncoding;

        try (InputStream in = Files.newInputStream(file.toPath())) {
            DataInputStream din = new DataInputStream(in);
            byte[] data = new byte[(int) file.length()];
            din.readFully(data);
            din.close();
            buildMessage(data);
        }
    }

    /**
     * Constructs a new <code>Attachment</code> instance.
     *
     * @param file the file to be attached
     *
     * @throws IOException   if an I/O error occurs
     * @throws MimeException if a MIME related parsing error occurs
     */
    public Attachment(File file)
            throws IOException, MimeException {
        this(file, "us-ascii");
    }

    /**
     * Constructs a new <code>Attachment</code> instance.
     *
     * @param filename          the filename of the attachment
     * @param data              the byte[] data for attachment
     * @param characterEncoding the character encoding used in the filename
     *
     * @throws MimeException if a MIME related parsing error occurs
     */
    public Attachment(String filename, byte[] data, String characterEncoding)
            throws MimeException {
        super();
        this.filename = new String(filename);
        this.characterEncoding = characterEncoding;
        buildMessage(data);
    }

    /**
     * Constructs a new <code>Attachment</code> instance.
     *
     * @param filename the filename of the attachment
     * @param data     the byte[] data for attachment
     *
     * @throws MimeException if a MIME related parsing error occurs
     */
    public Attachment(String filename, byte[] data)
            throws MimeException {
        this(filename, data, "us-ascii");
    }

    /**
     * Constructs a new <code>Attachment</code> instance.
     *
     * @param data the body byte[] data for attachment
     *
     * @throws MimeException if a MIME related error occurs
     */
    public Attachment(byte[] data)
            throws MimeException {
        buildMessage(data);
    }

    /**
     * Constructs a new <code>Attachment</code> instance from an existing MIME message.
     *
     * @param message the MimeMessage
     *
     * @throws MimeException if a MIME related error occurs
     */
    public Attachment(MimeMessage message)
            throws MimeException {
        super(message.getMessage());
    }

    /**
     * Builds message from input data.
     *
     * @param data the input data
     *
     * @throws MimeException if a MIME related parsing error occurs
     */
    private void buildMessage(byte[] data)
            throws MimeException {
        MimetypesFileTypeMap map = new MimetypesFileTypeMap();
        String contentType = null;

        if (filename != null) {
            contentType = map.getContentType(filename);
        }

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

        setContentType(contentType);

        MimeHeader attach = new MimeHeader("Content-Disposition", "attachment");

        if (filename != null) {
            MimeHeaderAttr mha = new MimeHeaderAttr("filename", filename,
                                                    characterEncoding);
            attach.addAttribute(mha);
        }

        addReplaceHeader(attach);
        setBody(data);
    }

    /**
     * Gets content-type for this <code>Attachment</code>.
     *
     * @return content-type
     */
    public String getContentType() {
        return super.getContentType();
    }

    /**
     * Sets Content-Type header for this <code>Attachment</code>.
     *
     * @param contentType the content-type for this <code>Attachment</code>
     *
     * @throws MimeException if a MIME related error occurs
     */
    public void setContentType(String contentType)
            throws MimeException {
        MimeType type = null;
        MimeHeader header = null;

        try {
            type = new MimeType(contentType);

            String baseType = type.getBaseType();

            if ((baseType != null) && baseType.equalsIgnoreCase("text")) {
                header = new MimeHeader("Content-Transfer-Encoding", "7BIT");
            } else {
                header = new MimeHeader("Content-Transfer-Encoding", "BASE64");
            }
        } catch (MimeTypeParseException mte) {
            header = new MimeHeader("Content-Transfer-Encoding", "BASE64");
        }

        addReplaceHeader(header);
        super.setContentType(contentType);
    }

    /**
     * Gets Content-transfer-encoding header value for this
     * <code>Attachment</code>.
     *
     * @return content-transfer-encoding or <code>null</code> if not found
     */
    public String getContentEncoding() {
        MimeHeader h = getHeader("Content-Transfer-Encoding");

        if (h == null) {
            return null;
        } else {
            return h.getValue();
        }
    }

    /**
     * Sets Content-Transfer-Encoding header for this <code>Attachment</code>.
     *
     * @param ce the content-transfer-encoding
     *
     * @throws MimeException if a MIME related error occurs
     */
    public void setContentEncoding(String ce)
            throws MimeException {
        addReplaceHeader(new MimeHeader("Content-Transfer-Encoding", ce));
    }

    /**
     * Gets filename field contained in Content-Disposition header attribute
     * for this <code>Attachment</code>.
     *
     * @return filename
     */
    public String getFilename() {
        String fnz = null;

        try {
            MimeHeader cd = getHeader("Content-Disposition");
            MimeHeaderAttr fn = null;

            // check if Content-Disposition header found
            if (cd != null) {
                fn = cd.getAttribute("filename");
            }

            // check if filename attribute available
            if (fn != null) {
                fnz = fn.getValue();
            } else {
                // filename attribute not found look in Content-Type header
                MimeHeader ct = getHeader("Content-Type");

                if (ct != null) {
                    // check if name attribute available
                    MimeHeaderAttr ctn = ct.getAttribute("name");

                    if (ctn != null) {
                        fnz = ctn.getValue();
                    }
                }

                if (fnz == null && cd.value.equals("inline")) {
                    MimeHeader cl = getHeader("Content-Location");
                    if (cl != null) {
                        fnz = cl.getValue();
                    }
                }
            }
        } catch (Exception e) {
            // todo: ignore?
        }

        return fnz;
    }

    /**
     * Gets decoded content of this <code>Attachment</code>.
     *
     * @return the decoded content of this <code>Attachment</code>
     *
     * @throws Exception if message could not be decoded
     */
    public byte[] getFileData()
            throws Exception {
        return getBodyData();
    }
}
