/*
 * $Id: SimpleMessageListenerAdapter.java 320 2009-05-20 09:19:20Z lhoriman $
 * $URL: https://subethasmtp.googlecode.com/svn/trunk/src/org/subethamail/smtp/helper/SimpleMessageListenerAdapter.java $
 */
package org.subethamail.smtp.helper;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import org.subethamail.smtp.MessageContext;
import org.subethamail.smtp.MessageHandler;
import org.subethamail.smtp.MessageHandlerFactory;
import org.subethamail.smtp.RejectException;
import org.subethamail.smtp.TooMuchDataException;
import org.subethamail.smtp.helper.SmarterMessageListener.Receiver;
import org.subethamail.smtp.io.DeferredFileOutputStream;

/**
 * MessageHandlerFactory implementation which adapts to a collection of
 * SmarterMessageListeners.  This is actually half-way between the
 * SimpleMessageListener interface and the raw MessageHandler.
 *
 * The key point is that for any message, every accepted recipient will get a
 * separate delivery.
 *
 * @author Jeff Schnitzer
 */
public class SmarterMessageListenerAdapter implements MessageHandlerFactory
{
	/**
	 * 5 megs by default. The server will buffer incoming messages to disk
	 * when they hit this limit in the DATA received.
	 */
	private static int DEFAULT_DATA_DEFERRED_SIZE = 1024*1024*5;

	private Collection<SmarterMessageListener> listeners;
	private int dataDeferredSize;

	/**
	 * Initializes this factory with a single listener.
	 *
	 * Default data deferred size is 5 megs.
	 */
	public SmarterMessageListenerAdapter(SmarterMessageListener listener)
	{
		this(Collections.singleton(listener), DEFAULT_DATA_DEFERRED_SIZE);
	}

	/**
	 * Initializes this factory with the listeners.
	 *
	 * Default data deferred size is 5 megs.
	 */
	public SmarterMessageListenerAdapter(Collection<SmarterMessageListener> listeners)
	{
		this(listeners, DEFAULT_DATA_DEFERRED_SIZE);
	}

	/**
	 * Initializes this factory with the listeners.
	 * @param dataDeferredSize The server will buffer
	 *        incoming messages to disk when they hit this limit in the
	 *        DATA received.
	 */
	public SmarterMessageListenerAdapter(Collection<SmarterMessageListener> listeners, int dataDeferredSize)
	{
		this.listeners = listeners;
		this.dataDeferredSize = dataDeferredSize;
	}

	/* (non-Javadoc)
	 * @see org.subethamail.smtp.MessageHandlerFactory#create(org.subethamail.smtp.MessageContext)
	 */
	public MessageHandler create(MessageContext ctx)
	{
		return new Handler(ctx);
	}

	/**
	 * Class which implements the actual handler interface.
	 */
	class Handler implements MessageHandler
	{
		MessageContext ctx;
		String from;
		List<Receiver> deliveries = new ArrayList<Receiver>();

		/** */
		public Handler(MessageContext ctx)
		{
			this.ctx = ctx;
		}

		/** */
		public void from(String from) throws RejectException
		{
			this.from = from;
		}

		/** */
		public void recipient(String recipient) throws RejectException
		{
			for (SmarterMessageListener listener: SmarterMessageListenerAdapter.this.listeners)
			{
				Receiver rec = listener.accept(this.from, recipient);

				if (rec != null)
					this.deliveries.add(rec);
			}

			if (this.deliveries.isEmpty())
				throw new RejectException(553, "<" + recipient + "> address unknown.");
		}

		/** */
		public void data(InputStream data) throws TooMuchDataException, IOException
		{
			if (this.deliveries.size() == 1)
			{
				this.deliveries.get(0).deliver(data);
			}
			else
			{
				DeferredFileOutputStream dfos = new DeferredFileOutputStream(SmarterMessageListenerAdapter.this.dataDeferredSize);

				try
				{
					int value;
					while ((value = data.read()) >= 0)
					{
						dfos.write(value);
					}

					for (Receiver rec: this.deliveries)
					{
						rec.deliver(dfos.getInputStream());
					}
				}
				finally
				{
					dfos.close();
				}
			}
		}

		/** */
		public void done()
		{
			for (Receiver rec: this.deliveries)
			{
				rec.done();
			}
		}
	}
}