/*
 * DownloadMailService.java
 *
 * Created on 2007.10.18., 22:35:48
 *
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package hu.nir.nmail.services;

import com.sun.mail.pop3.POP3SSLStore;
import com.sun.mail.pop3.POP3Store;
import hu.nir.nmail.dbm.DataManager;
import hu.nir.nmail.ejb.entity.MailFolder;
import hu.nir.nmail.ejb.entity.NmailUser;
import hu.nir.nmail.ejb.entity.UserSetting;
import hu.nir.nmail.spring.beans.MailsData;
import hu.nir.nmail.utils.NmailUtility;
import hu.nir.nmail.spring.beans.NmailErrors;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.mail.Address;
import javax.mail.BodyPart;
import javax.mail.Flags.Flag;
import javax.mail.Folder;
import javax.mail.FolderNotFoundException;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Part;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.URLName;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;

/**
 *
 * @author attila
 */
public class DownloadMailService implements NmailService {

    private DataManager dataManager;
    private Logger logger = Logger.getLogger(DownloadMailService.class.getName());

    public Map<String,Object> processCommand(Object... commands) {
        NmailErrors nmailErrors = new NmailErrors();
        NmailUser nmailUser = null;
        MailsData mailsData = null;
        
        for (Object command : commands) {
            if (command instanceof NmailUser) {
                nmailUser = (NmailUser) command;
                continue;
            }
            if (command instanceof MailsData) {
                mailsData = (MailsData) command;
            }
        }

        if (nmailUser == null) {
            return null;
        }
        
        UserSetting userSettings = nmailUser.getUserSetting();
        
        MailFolder mailFolder = nmailUser.getMailFolder();
        Session session = Session.getInstance(System.getProperties());
        URLName uRLName = new URLName(mailFolder.getPop3Host());
        Store store;

        if (mailFolder.getSsl()) {
            store = new POP3SSLStore(session, uRLName);
        } else {
            store = new POP3Store(session, uRLName);
        }

        Folder inbox = null;

        try {
            store.connect(mailFolder.getPop3Host(), mailFolder.getPort(), mailFolder.getPop3UserName(), mailFolder.getPop3Password());
            logger.info("Successfuly connected to the host: " + mailFolder.getPop3Host());

            inbox = store.getDefaultFolder();
            if (inbox == null) {
                inbox = store.getFolder("INBOX");
            } else {
                inbox = inbox.getFolder("INBOX");
            }

            inbox.open(Folder.READ_WRITE);
            if (inbox.isOpen()) {
                logger.log(Level.INFO, "POP3 inbox is opened.");
            }

            int messageCount = inbox.getMessageCount();
            logger.log(Level.INFO, messageCount+" email found");

            if (messageCount > 0) {
                List<Message> messages = getLastMessages(inbox);
                messages = storeMessages(messages, nmailUser);
                
                if (mailsData == null) {
                    Message firstMessage = messages.get(0);
                    mailsData = new MailsData(0,userSettings.getDefaultSenderName(),userSettings.getDefaultSenderAddress(),"",0,firstMessage.getSubject());
                }
                
                mailsData.setMessages(messages);
            }
            
            mailsData.setMessageCount(messageCount);
        } catch (IOException ex) {
            logger.log(Level.SEVERE, ex.getMessage(), ex);
            nmailErrors.addErrorMessage(ex.getMessage());
        } catch (FolderNotFoundException ex) {
            logger.log(Level.SEVERE, "INBOX was not found!", ex);
            nmailErrors.addErrorCode("pop3.foldernotfound");
        } catch (MessagingException ex) {
            logger.log(Level.SEVERE, "Connection failed to the host: " + mailFolder.getPop3Host() + "!", ex);
            nmailErrors.addErrorCode("pop3.connection");
        } finally {
            try {
                if (inbox != null && inbox.isOpen()) {
                    inbox.close(true); //close inbox and expunge the deleted messages
                    logger.log(Level.INFO, "POP3 inbox is closed.");
                }

                if (store.isConnected()) {
                    store.close();
                }
            } catch (MessagingException ex) {
                //
                logger.log(Level.SEVERE, ex.getMessage(), ex);
            }
        }

        return NmailUtility.toMap("mailsData", mailsData, "nmailErrors", nmailErrors);
    }
    
    private List<Message> getLastMessages(Folder inbox) throws MessagingException {
        Message[] allMessage = inbox.getMessages();
        List<Message> lastMessages = new ArrayList<Message>();
        int messageCount = allMessage.length -1;
        
        for (int i = messageCount; i >= 0; i--) {
            if (i > messageCount -5) {
                lastMessages.add(allMessage[i]);
            }
            else { //delete the old ones
                allMessage[i].setFlag(Flag.DELETED, true);
            }
        }
        
        return lastMessages;
    }

    private List<Message> storeMessages(List<Message> messages, NmailUser nmailUser) throws MessagingException, IOException {
        Session session = Session.getInstance(System.getProperties());
        //We store the messages in th memory in a message list
        List<Message> messageList = new ArrayList<Message>();
        
        for (Message oldMessage : messages) {
            Message newMessage = new MimeMessage(session);
            
            storeHeaders(oldMessage, newMessage);
            storeContent(oldMessage, newMessage, nmailUser);
            
            messageList.add(newMessage);
        }
        
        return messageList;
    }
    
    private void storeFrom(Message oldMessage, Message newMessage) throws MessagingException {
        Address[] from = oldMessage.getFrom();
        List<Address> addresses = new ArrayList<Address>();
        
        for (int i=0, n=from.length; i<n; i++) {
            
            if (i == 0) {
                newMessage.setFrom(from[i]);
            }
            else {
                addresses.add(from[i]);
            }
        }
        
        if (addresses.size() > 0) {
            Address[] from2 = addresses.toArray(new Address[addresses.size()]);
            newMessage.addFrom(from2);
        }
    }
    
    
    private void storeHeaders(Message oldMessage, Message newMessage) throws MessagingException {
        
        String subject = oldMessage.getSubject();
        Date sentDate = oldMessage.getSentDate();
        
        newMessage.setSubject(subject != null ? subject : "");
        newMessage.setSentDate(sentDate != null ? sentDate : new Date());

        storeFrom(oldMessage, newMessage);
        
        String[] contentTypes = oldMessage.getHeader("Content-Type");
        
        if (contentTypes != null) {
            for (String contentType : contentTypes) {
                newMessage.setHeader("Content-Type", contentType);
            }
        }
        
        String[] encodings = oldMessage.getHeader("Content-Transfer-Encoding");
        
        if (encodings != null) {
            for (String encoding : encodings) {
                newMessage.setHeader("Content-Transfer-Encoding", encoding);
            }
        }
        
        String[] mimeVersions = oldMessage.getHeader("MIME-Version");
        
        if (mimeVersions != null) {
            for (String mimeVersion : mimeVersions) {
                newMessage.setHeader("MIME-Version", mimeVersion);
            }
        }
    }

    private void storeContent(Message oldMessage, Message newMessage, NmailUser nmailUser) {
        try {
            Object content = oldMessage.getContent();
            
            if (content instanceof Multipart) {
                Multipart multipart = (Multipart) content;
                Multipart newMultipart;
                String contentType = multipart.getContentType();
                if (contentType.contains("multipart/alternative")) {
                        newMultipart = new MimeMultipart("alternative");
                }
                else {
                        newMultipart = new MimeMultipart();
                }
                
                for (int i = 0, n = multipart.getCount(); i < n; i++) {
                    BodyPart part = multipart.getBodyPart(i);
                    String disposition = part.getDisposition();

                    if (disposition != null && (disposition.equals(Part.ATTACHMENT) || disposition.equals(Part.INLINE)) && part.getFileName() != null) {

                        Properties props = System.getProperties();
                        File file = new File(props.getProperty("java.io.tmpdir") + File.separator + nmailUser.getNmailUserLogin().getUserName() + "@" + part.getFileName());
                        FileOutputStream fos = new FileOutputStream(file);
                        InputStream is = part.getInputStream();
                        byte[] buffer = new byte[512*1024];
                        while (is.read(buffer) > 0) {
                            fos.write(buffer);
                        }

                        is.close();
                        fos.close();
                        BodyPart mimeMultipart = new MimeBodyPart();
                        FileDataSource source = new FileDataSource(file);
                        mimeMultipart.setDataHandler(new DataHandler(source));
                        mimeMultipart.setFileName(part.getFileName());
                        mimeMultipart.setDisposition(disposition);
                        newMultipart.addBodyPart(mimeMultipart);
                    }
                    else {
                        newMultipart.addBodyPart(part);
                    }
                }
                
                newMessage.setContent(newMultipart);
            }
            else {
                newMessage.setContent(content, oldMessage.getContentType());
            }
        } catch (IOException ex) {
            logger.log(Level.SEVERE, null, ex);
        } catch (MessagingException ex) {
            logger.log(Level.SEVERE, null, ex);
        }
    }

    public DataManager getDataManager() {
        return dataManager;
    }

    public void setDataManager(DataManager dataManager) {
        this.dataManager = dataManager;
    }
}