/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package hu.nir.nmail.threads;

import com.sun.mail.smtp.SMTPTransport;
import hu.nir.nmail.dbm.DataManager;
import hu.nir.nmail.ejb.entity.EmailSendingProces;
import hu.nir.nmail.ejb.entity.NmailSetting;
import hu.nir.nmail.ejb.entity.NmailUser;
import hu.nir.nmail.ejb.entity.SmtpSettings;
import hu.nir.nmail.ejb.stateless.NmailSettingManager;
import hu.nir.nmail.ejb.stateless.SmtpSettingsManager;
import hu.nir.nmail.ejb.stateless.UserProcesManager;
import hu.nir.nmail.utils.NmailUtility;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import javax.mail.Address;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.NoSuchProviderException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.URLName;

/**
 *
 * @author attila
 */
public class SenderThread implements Runnable {
    private Thread thread;
    private Logger logger = Logger.getLogger(SenderThread.class.getName());
    private NmailUser nmailUser;
    private Message message;
    private List<Address> addressList;
    private DataManager dataManager;
    private EmailSendingProces emailSendingProces;
    private UserProcesManager userProcesManager;
    
    public SenderThread(NmailUser nmailUser, Message message, List<Address> addressList, DataManager dataManager, EmailSendingProces emailSendingProces) {
        thread = new Thread(this);
        this.nmailUser = nmailUser;
        this.message = message;
        this.addressList = addressList;
        this.dataManager = dataManager;
        this.emailSendingProces = emailSendingProces;
        
        if (emailSendingProces != null) {
            this.userProcesManager = dataManager.getUserProcesManager();
        }
    }
    
    public void start() {
        thread.start();
    }
    
    public void stop() {
        thread = null;
    }
    
    public void run() {
        Date startDate = new Date();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy.MM.dd. HH:mm");
        logger.log(Level.INFO, "Sender process started "+dateFormat.format(startDate));
        
        try {
            SMTPTransport transport = smtpConnection();
            
            if (transport == null) {
                logger.log(Level.INFO, "Sender process is ended with error.");
                return;
            }
            
            sendMessages(transport);
            transport.close();
        } catch (NoSuchProviderException ex) {
            logger.log(Level.SEVERE, null, ex);
        } catch (MessagingException ex) {
            logger.log(Level.SEVERE, "Couldn't connect to the smtp host",ex);
        }
        
        logger.log(Level.INFO, "Sender process is ended. "+dateFormat.format(new Date()));
    }
    
    private SMTPTransport smtpConnection() throws NoSuchProviderException, MessagingException {
        SmtpSettingsManager smtpSettingsManager = dataManager.getSmtpSettingsManager();
        SmtpSettings smtpSettings = smtpSettingsManager.getSmtpSettings();
        Properties smtpProps = NmailUtility.getSMTPProperties(smtpSettings);
        Session session = Session.getDefaultInstance(smtpProps);
        String smtpHost = smtpProps.getProperty("mail.smtp.host");
        String smtpUser = smtpProps.getProperty("mail.user");
        String smtpPassword = smtpProps.getProperty("mail.password");
        int smtpPort = (int) smtpSettings.getPort();
        String smtpProtocol = smtpProps.getProperty("mail.transport.protocol");
        URLName urlName = new URLName(smtpProtocol,smtpHost,smtpPort,null,smtpUser,smtpPassword);
        PasswordAuthentication passwordAuthentication = new PasswordAuthentication(smtpUser, smtpPassword);
        session.setPasswordAuthentication(urlName, passwordAuthentication);
        SMTPTransport transport = new SMTPTransport(session, urlName);
        transport.connect(smtpHost, smtpUser, smtpPassword);

        if (transport.isConnected()) {
            logger.log(Level.INFO, "Connected to the smtp server: "+smtpSettings.getSmtpSettingsPK().getHost());
        }
        else {
            logger.log(Level.SEVERE, "Connection to the smtp server: "+smtpSettings.getSmtpSettingsPK().getHost()+" failed!");
            return null;
        }
        
        return transport;
    }
    
    private void sendMessages(SMTPTransport transport) {
        NmailSettingManager nmailSettingManager = dataManager.getNmailSettingManager();
        NmailSetting nmailSetting = nmailSettingManager.getAvailableNmailSetting();
        int emailBlockSize = 10;
        long blockWaitingTime = 2000;
        
        if (nmailSetting != null) {
            emailBlockSize = nmailSetting.getMailBlockSize();
            blockWaitingTime = nmailSetting.getBlockWaitingTime();
        }
        
        long sentEmail = emailSendingProces != null ? emailSendingProces.getSentEmail() : 0;
        long failedEmail = emailSendingProces != null ? emailSendingProces.getFailedEmail() : 0;
        int block = 0;
        int emailInActualBlock = 0;
        
        logger.log(Level.INFO, "Block size: "+emailBlockSize);
        logger.log(Level.INFO, "Block waiting time: "+blockWaitingTime/1000+" sec.");
        logger.log(Level.INFO, (block+1)+". started; success: "+sentEmail+" failed: "+failedEmail);
        
        for (Address address : addressList) {
            try {
                logger.log(Level.INFO,address.toString()+" address");
                message.setRecipient(Message.RecipientType.TO, address);
                transport.sendMessage(message,message.getRecipients(Message.RecipientType.TO));
                sentEmail++;
            } catch (Exception ex) {
                logger.log(Level.WARNING, "Couldn't send message to this address: "+address.toString(),ex);
                failedEmail++;
            } finally {
                if (emailInActualBlock < emailBlockSize-1) {
                    emailInActualBlock++;
                }
                else {
                    emailInActualBlock = 0;
                    block++;
                    
                    if (emailSendingProces != null)
                        storeThreadState(sentEmail, failedEmail, false);
                    
                    waiting(blockWaitingTime);
                    logger.log(Level.INFO, (block+1)+". started; success: "+sentEmail+" failed: "+failedEmail);
                }
            }
        }
        
        logger.log(Level.INFO, (block+1)+". started; success: "+sentEmail+" failed: "+failedEmail);
        
        if (emailSendingProces != null)
            storeThreadState(sentEmail, failedEmail, true);
    }
    
    private void storeThreadState(long sentEmail, long failedEmail, boolean finish) {
        emailSendingProces.setSentEmail(sentEmail);
        emailSendingProces.setFailedEmail(failedEmail);
        if (finish) {
            emailSendingProces.setEndProces(new Date());
        }
        emailSendingProces = (EmailSendingProces) userProcesManager.merge(emailSendingProces);
    }
    
    private void waiting(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException ex) {
            Logger.getLogger(SenderThread.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

}
