import java.io.*;
import java.util.*;


/**
 *  For writing output streams using the SFLP protocol
 *
 *  Note that all write operations are buffered.  
 *  The actual writing does not take place until 
 *  the flush method is called.
 *
 *  @version 1.0
 *  @author  Prof. David Bernstein, James madison University
 */
public class SFPOutputStream extends OutputStream
{
    protected int                sequenceNumber;
    protected DataOutputStream   out;
    protected LinkedList         buffer;





    /**
     * Construct a new SFPOutputStream
     *
     * @param out   The OutputStream to use
     */

    public SFPOutputStream(OutputStream out)
    {
       this.out = new DataOutputStream(out);
       sequenceNumber = 1;
       buffer = new LinkedList();
    }






    /**
     * Closes this Output stream (after first 
     * calling flush);
     */
    public void close() throws IOException
    {
       flush();
       out.close();
    }





    /**
     * Flushes this Output stream
     */
    public void flush() throws IOException
    {
       byte            b;
       Byte            byteObject;
       ListIterator    e;

       // Write the header
       writeHeader(SFPConnection.DATA, buffer.size());

       // Write the contents of the buffer
       e = buffer.listIterator();
       while (e.hasNext()) 
       {
          byteObject = (Byte)e.next();
          b = byteObject.byteValue();
          out.write(b);
       }

       // Flush the output stream
       out.flush();

       // Clear the buffer
       buffer.clear();
    }




    /**
     * Writes a single byte of data
     *
     * @param b   The byte to write 
     */
    protected void write(byte b) throws IOException
    {
       buffer.addLast(new Byte(b));
    }






    /**
     * Writes a single byte of data
     *
     * @param b   The 1-byte integer to write 
     */
    public void write(int b) throws IOException
    {
       write((byte)b);
    }





    /**
     * Writes an array of bytes
     *
     * @param b   The bytes to write
     * @param off The start offset of the data
     * @param len The maximum number of bytes read
     */
    public void write(byte[] b, int off, int len) throws IOException
    {
       int   i;

       for (i=0; i < len; i++) write(b[i+off]);
    }





    /**
     * Writes an array of bytes
     *
     * @param b   The bytes to write
     */
    public void write(byte[] b) throws IOException
    {
       int   i;

       for (i=0; i < b.length; i++) write(b[i]);
    }






    /**
     * Write the SFP header information
     *
     * @param frameType  The frame type
     * @param length     The length
     */
    protected void writeHeader(int frameType, int length) throws IOException
    {

       out.writeByte((int)('*'));
       out.writeByte(frameType);
       out.writeShort(sequenceNumber);
       out.writeShort(length);

       sequenceNumber++;
    }




    /**
     * Write a SIGNON header
     */
    protected void writeSIGNON() throws IOException
    {
       writeHeader(SFPConnection.SIGNON, 0);
       out.flush();
    }




}
