/*
 * JBoss, the OpenSource J2EE webOS
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 *
 * Created on Mar 16, 2004
 */
package org.jboss.net.axis.transport.mailto.client;

import java.io.IOException;
import java.util.Date;

import javax.mail.AuthenticationFailedException;
import javax.mail.FetchProfile;
import javax.mail.Flags;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.NoSuchProviderException;
import javax.mail.Session;
import javax.mail.Store;

import org.apache.axis.AxisFault;
import org.apache.axis.MessageContext;
import org.apache.axis.message.addressing.AddressingHeaders;
import org.apache.axis.message.addressing.Constants;
import org.apache.axis.message.addressing.MessageID;


/**
 * <dl>
 * <dt><b>Title: </b><dd>Synchronous Mail Transport</dd>
 * <p>
 * <dt><b>Description: </b><dd>This is a Synchronous email Transport. It will block while waiting for a response.<br>
 * This client side sender is really only usefull for testing.</dd>
 * <p>
 * </dl>
 * @author <a href="mailto:jasone@greenrivercomputing.com">Jason Essington</a>
 * @version $Revision: 1.1 $
 */
public class SyncMailSender extends BaseMailSender
{
   // property names
   public static final String FOLDER_NAME = "FolderName";
   public static final String USERNAME = "Username";
   public static final String PASSWORD = "Password";

   // default folder name
   public static final String INBOX = "INBOX";

   public static final String TIMEOUT = "Timeout";
   public static final long DEFAULT_TIMEOUT = 1800000; // 30 minutes

   protected long getTimeout()
   {
      long timeout = DEFAULT_TIMEOUT;
      String to = (String) getOption(TIMEOUT);
      if (to != null)
      {
         try
         {
            timeout = Long.parseLong(to) * 60 * 1000;
         } catch (NumberFormatException e)
         {
            // this isn't really a big deal, we have a default timeout
            log.warn(to + "is not a valid number we will use 30 minutes instead.", e);
         }
      }
      return timeout;
   }
   protected void checkResponse(MessageContext ctx) throws AxisFault
   {
      String msgID = getMessageID(ctx);
      String folderName = getFolderName();
      long timeout = getTimeout();


      Session mail = getMailSession();
      Store store = null;
      Folder folder = null;

      try
      {
         store = mail.getStore();
         if (log.isDebugEnabled())
            log.debug(store.getClass().getName());
      }
      catch (NoSuchProviderException e)
      {
         // Could happen if <parameter name="mail.store.protocol" value="pop3"/> is not set
         log.fatal("The mail session does not have a default provider! Check the mail.store.protocal "
            + "parameter in the wsdd file", e);
         throw new AxisFault("No mail store provider has been configured.", e);
      }

      long end = new Date().getTime() + timeout;
      while (end > new Date().getTime())
      {

         try
         {
            //System.out.println("\n\n\t"+store.getURLName().getHost()+"\n\n");
            String user = (String) getOption(USERNAME);
            String password = (String) getOption(PASSWORD);
            if (user != null && password != null)
               store.connect(null, user, password);
            //store.connect(store.getURLName().getHost(), user, password);
            else
               store.connect();
         }
         catch (AuthenticationFailedException e)
         {
            // This could happen if the username and password defined in the mail-service.xml file are wrong or missing
            log.fatal("The User and/or Password defined in the mail-service.xml file could be wrong or missing", e);
            throw new AxisFault("Authentication Failed", e);
         }
         catch (MessagingException e)
         {
            // This is a bad thing. We couldn't connect to the mail store for some reason. 
            log.fatal("Unable to connect to the mail store.", e);
            throw new AxisFault("Unable to connect to the mail store", e);
         }
         catch (IllegalStateException e)
         {
            // This means the store is already connected! I suppose since this is what we wanted any way we could
            // continue along our merry way?
            log.warn("The store is already connected!", e);
         }

         try
         {
            folder = store.getFolder(folderName);

            if (log.isDebugEnabled())
               log.debug(folder.getClass().getName());

            // we can only continue if this folder actually exists!
            if (!folder.exists())
            {
               log.fatal("The folder '" + folderName + "' doe not exist.");
               // This was fatal, lets clean up and leave.
               closeStore(store);
               return;
            }
         }
         catch (MessagingException e)
         {
            log.fatal("Unable to retrieve the folder '" + folderName + "' from the store.", e);
            closeStore(store);
            throw new AxisFault("Unable to retrieve the folder '" + folderName + "' from the store.", e);
         }

         try
         {
            folder.open(Folder.READ_WRITE);
         }
         catch (MessagingException e)
         {
            // not sure what would cause this, but if it happens, it can't be good so log and leave
            log.fatal("Failed to open the folder'" + folderName + "'.", e);
            closeStore(store);
            throw new AxisFault("Failed to open the folder'" + folderName + "'.", e);
         }
         try
         {
            Message msg = fetchMessage(ctx, msgID, folder);

            //leave if there are no messages
            if (msg != null)
            {
               org.apache.axis.Message soapMsg = new org.apache.axis.Message(msg.getInputStream(), false, msg
                  .getContentType(), null);
               ctx.setResponseMessage(soapMsg);
               ctx.setPastPivot(true);
               msg.setFlag(Flags.Flag.DELETED, true);
               break;
            }
         }
         catch (IOException e)
         {
            log.fatal(e);
            throw new AxisFault("IOException", e);
         }
         catch (MessagingException e)
         {
            log.fatal(e);
            throw new AxisFault("messagingException", e);
         }
         finally
         {
            // now we are done, get out of here
            closeFolder(folder);
            closeStore(store);
         }
         try
         {
            // lets not be too iritating, we'll only check our mail every 60 seconds.
            Thread.sleep(60000);
         }
         catch (InterruptedException e1)
         {
            // oops, was bothering someone best just leave now
            break;
         }
      }
   }

   protected String getFolderName()
   {
      String folder = (String) getOption(FOLDER_NAME);
      if (folder == null) folder = INBOX;
      return folder;
   }

   private Message fetchMessage(MessageContext ctx, String msgID, Folder folder) throws AxisFault
   {
      Message[] messages = new Message[0];
      Message resMsg = null;

      try
      {
         if (log.isDebugEnabled())
            log.debug("\nMessageCount: " + folder.getMessageCount() + "\nNewMessageCount: "
               + folder.getNewMessageCount() + "\nUnreadMessageCount: " + folder.getUnreadMessageCount());

         if (folder.getMessageCount() > 0)
         {
            messages = folder.getMessages();
            FetchProfile fp = new FetchProfile();
            fp.add(FetchProfile.Item.CONTENT_INFO);

            //address information
            fp.add(FetchProfile.Item.ENVELOPE);

            folder.fetch(messages, fp);

            for (int i = 0; i < messages.length; i++)
            {
               Message message = messages[i];
               String inReplyTo = null;
               try
               {
                  inReplyTo = message.getHeader(HEADER_IN_REPLY_TO)[0];
               }
               catch (NullPointerException e)
               {
                  // no In-Reply-To: header, so skip this msg
                  continue;
               }

               if (msgID.equals(inReplyTo))
               {
                  resMsg = message;
                  break;
               }
            }
         }
         else if (log.isDebugEnabled())
            log.debug("getMailMessages(Session mail)\n\t no mail!");
      }
      catch (NoSuchProviderException e)
      {
         log.error("NoSuchProviderException: getMailMessages(Session mail)\n", e);
         throw new AxisFault("NoSuchProviderException", e);
      }
      catch (MessagingException e)
      {
         log.error("MessagingException: getMailMessages(Session mail)\n", e);
         throw new AxisFault("MessagingException", e);
      }

      return resMsg;
   }

   private void closeFolder(Folder folder)
   {
      try
      {
         // expunge deleted messages on close
         folder.close(true);
      }
      catch (Exception ignore)
      {
      }
      return;
   }

   private void closeStore(Store store)
   {
      try
      {
         if (store != null)
            store.close();
      }
      catch (Exception ignore)
      {
      }
      return;
   }

   private String getMessageID(MessageContext ctx)
   {
      String id = "no message-id";
      
      if (ctx.containsProperty(Constants.ENV_ADDRESSING_REQUEST_HEADERS))
      {
         AddressingHeaders headers = (AddressingHeaders) ctx.getProperty(Constants.ENV_ADDRESSING_REQUEST_HEADERS);
         MessageID msgid = headers.getMessageID();
         if (msgid != null)
         {
            id = msgid.toString();
         }
      }
      return id;
   }

}