package org.subethamail.smtp.internal.io;

import java.io.IOException;
import java.io.OutputStream;

/**
 * DotTerminatedOutputStream makes possible to end mail data with a
 * "CRLF . CRLF" sequence in such a way that no unnecessary beginning CRLF pair
 * is added if the original output already ends with it.
 *
 * See RFC 5321 4.1.1.4. second paragraph
 */
public final class DotTerminatedOutputStream extends OutputStream
{
	private static final byte[] DOT_CRLF = new byte[]{
			'.', '\r', '\n'
	};
	private static final byte[] CRLF_DOT_CRLF = new byte[]{
			'\r', '\n', '.', '\r', '\n'
	};

	/**
	 * The wrapped output stream.
	 */
	private final OutputStream out;

	/**
	 * The last bytes written out by the {@link #write()} function. The first
	 * byte in the array contains the penultimate, the second contains the last
	 * byte written out by the write function. It's initial value is CR LF, so
	 * it is possible to write out empty data.
	 */
	private final byte[] lastBytes = new byte[]{
			'\r', '\n'
	};

	public DotTerminatedOutputStream(OutputStream out)
	{
		this.out = out;
	}

	@Override
	public void write(int b) throws IOException
	{
		lastBytes[0] = lastBytes[1];
		lastBytes[1] = (byte) b;
		out.write(b);
	}

	@Override
	public void write(byte[] b, int off, int len) throws IOException
	{
		if (len == 1)
		{
			lastBytes[0] = lastBytes[1];
			lastBytes[1] = b[off];
		}
		else if (len >= 2)
		{
			lastBytes[0] = b[off + len - 2];
			lastBytes[1] = b[off + len - 1];
		}
		out.write(b, off, len);
	}

	/**
	 * Writes ". CR LF" to the wrapped stream, but prefixes it with another CR
	 * LF sequence if it is missing from the end.
	 *
	 * @throws IOException
	 */
	public void writeTerminatingSequence() throws IOException
	{
		if (lastBytes[0] == '\r' && lastBytes[1] == '\n')
			out.write(DOT_CRLF);
		else
			out.write(CRLF_DOT_CRLF);
	}

}
