package com.tecspy.protocol;

import java.io.IOException;

import org.apache.log4j.Logger;

/**
 * Is fed with bytes from an input stream and issues complete messages. The
 * messages are terminated by a single end byte, "etx".
 * 
 * @author Michael Erskine
 * 
 */
public abstract class EtxMessageByteSink implements ByteSink {
	/** Standard log4j logger */
	protected static Logger log = Logger.getLogger(EtxMessageByteSink.class);

	/**
	 * Currently forming message buffer - should be constructed to the expected
	 * size of typical messages rather than the maximum
	 */
	protected StringBuilder msg;

	/** Message end byte */
	protected byte etx = (byte) 0x03;

	/** Maximum message length */
	protected int maxmsg = 1000;

	/**
	 * An indication of the number of bytes read. NB: not necessarily 100%
	 * correct.
	 */
	public long byteTotal = 0;

	/**
	 * A subclass constructor must create the message buffer e.g.: -
	 * 
	 * <code>
	 * super();
	 * etx = (byte) 0xFE;
	 * maxmsg = 1000;
	 * msg = new StringBuilder(1000);
	 * </code>
	 * 
	 */
	public EtxMessageByteSink() {
	}

	protected abstract void handleMsg();

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ketech.swim.ByteSink#processBytes(byte[], int)
	 */
	public int processBytes(byte[] buf, int len) {
		// incrementing the byte total here rather than for each byte
		byteTotal += len;
		for (int i = 0; i < len; i++) { // foreach byte
			// we are waiting for data or etx
			if (buf[i] == etx) {
				// etx is a normal condition - flush
				handleMsg();
				msg.setLength(0);
			} else {
				// normal condition add byte to message
				char c = (char) buf[i];
				if (msg.length() >= maxmsg) {
					// interested in how this condition is unit tested - somehow
					// have a log4j logger log to somewhere inparticular that
					// can be examined
					log.error("maximum message length exceeded (" + maxmsg
							+ "): buffered data discarded");
					msg.setLength(0);
				}
				msg.append(c);
			}
		}
		return len;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ketech.swim.ByteSink#processEof()
	 */
	public abstract void processEof();

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ketech.swim.ByteSink#processException(java.io.IOException)
	 */
	public abstract void processException(IOException e);
}
