//////////////////////////////////////////////////////////////////////////// 
// 
// Copyright (C) DSTC Pty Ltd (ACN 052 372 577) 1993, 1994, 1995.
// Unpublished work.  All Rights Reserved.
// 
// The software contained on this media is the property of the
// DSTC Pty Ltd.  Use of this software is strictly in accordance
// with the license agreement in the accompanying LICENSE.DOC 
// file. If your distribution of this software does not contain 
// a LICENSE.DOC file then you have no rights to use this 
// software in any manner and should contact DSTC at the address 
// below to determine an appropriate licensing arrangement.
// 
//      DSTC Pty Ltd
//      Level 7, GP South
//      University of Queensland
//      St Lucia, 4072
//      Australia
//      Tel: +61 7 3365 4310
//      Fax: +61 7 3365 4311
//      Email: jcsi@dstc.qut.edu.au
// 
// This software is being provided "AS IS" without warranty of
// any kind.  In no event shall DSTC Pty Ltd be liable for
// damage of any kind arising out of or in connection with
// the use or performance of this software.
// 
//////////////////////////////////////////////////////////////////////////// 

package com.dstc.security.ssl;

import java.io.IOException;
import java.io.EOFException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

import javax.net.ssl.SSLProtocolException;

/**
 * An input stream for SSLProtocolUnit objects
 *
 * @author Ming Yung
 */
final class ProtocolUnitInputStream extends XDRInputStream
{
  private SSLContext ctx;
  private ByteArrayInputStream puBuffer = new ByteArrayInputStream(new byte[0]);
  private byte[] packet;
  private boolean empty = true;
  private byte type;

  ProtocolUnitInputStream(SSLContext ctx, InputStream is)
  {
    super(is);
    this.ctx = ctx;
  }

  boolean bufferEmpty()
  {
    return (empty || (puBuffer.available() == 0));
  }

  SSLProtocolUnit readProtocolUnit() throws IOException
  {
    if (bufferEmpty())
    {
      try
      {
        type = super.readByte();
      }
      catch (EOFException e)
      {
        return null;
      }

      if (Debug.debug >= Debug.DEBUG_RECORD)
        Debug.debug("Type: " + type);
  
      if ((type & 0x80) == 0x80)
      {
        //V2ClientHello with two-byte header
        int recordLength = (type & 0x7f) >> 8 | super.readByte();
        byte[] contents = super.readBytes(recordLength);
  
        return new V2ClientHello((ServerHandShaker)
          ctx.getHandShaker(), contents);
      }
  
      byte[] version = super.readBytes(2);
  
      if (Debug.debug >= Debug.DEBUG_RECORD)
        Debug.debug("Major, minor: " + version[0] + 
                               ", " + version[1]);
  
      int length = super.readShort();
  
      if (Debug.debug >= Debug.DEBUG_RECORD)
        Debug.debug("Record length: " + length);
  
      packet = ctx.decryptAndVerifyMac(type, super.readBytes(length));

      puBuffer = new ByteArrayInputStream(packet);
      empty = false;
    }

    switch (type)
    {
      case SSLProtocolUnit.CHANGE_CIPHER_SPEC:
        empty = true;
        return new ChangeCipherSpec(packet);

      case SSLProtocolUnit.ALERT:
        empty = true;
        return new Alert(packet);

      case SSLProtocolUnit.APPLICATION_DATA:
        empty = true;
        return new ApplicationData(packet);

      case SSLProtocolUnit.HANDSHAKE:
        XDRInputStream is = new XDRInputStream(puBuffer);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        XDROutputStream os = new XDROutputStream(baos);
    
        os.writeByte(is.readByte());
      
        int len = is.read3Bytes();
        os.write3Bytes(len);
        os.write(is.readBytes(len));
        byte[] contents = baos.toByteArray();

        if (Debug.debug >= Debug.DEBUG_RECORD)
          Debug.debug("Content length: " + contents.length);

        return HandShake.getInstance(ctx.getHandShaker(), contents);

      default:
        throw new SSLProtocolException("Bad ContentType received");
    }
  }
}
