package org.bouncycastle.bcpg;

import java.io.*;

import org.bouncycastle.bcpg.sig.Exportable;
import org.bouncycastle.bcpg.sig.IssuerKeyID;
import org.bouncycastle.bcpg.sig.KeyExpirationTime;
import org.bouncycastle.bcpg.sig.KeyFlags;
import org.bouncycastle.bcpg.sig.PreferredAlgorithms;
import org.bouncycastle.bcpg.sig.Revocable;
import org.bouncycastle.bcpg.sig.SignatureCreationTime;
import org.bouncycastle.bcpg.sig.SignatureExpirationTime;
import org.bouncycastle.bcpg.sig.TrustSignature;

/**
 * reader for signature sub-packets
 */
public class SignatureSubpacketInputStream
    extends InputStream implements SignatureSubpacketTags
{
	InputStream	in;
	
    public SignatureSubpacketInputStream(
    	InputStream	in)
    {
    	this.in = in;
    }
    
    public int available()
    	throws IOException
    {
    	return in.available();
    }
    
    public int read()
    	throws IOException
    {
    	return in.read();
    }
    
    private void readFully(
    	byte[]	buf,
    	int		off,
    	int		len)
    	throws IOException
    {
    	if (len > 0)
    	{
    		int	b = this.read();
    		
			if (b < 0)
			{
				throw new EOFException();
			}
			
    		buf[off] = (byte)b;
    		off++;
    		len--;
    	}
    	
    	while (len > 0)
    	{
    		int	l = in.read(buf, off, len);
    		
    		if (l < 0)
    		{
    			throw new EOFException();
    		}
    		
    		off += l;
    		len -= l;
    	}
    }
	
    public SignatureSubpacket readPacket()
        throws IOException
    {
		int			l = this.read();
		int			bodyLen = 0;
		
    	if (l < 0)
    	{
    		return null;
    	}

		if (l < 192)
		{
			bodyLen = l;
		}
		else if (l < 223)
		{
		    bodyLen = ((l - 192) << 8) + (in.read()) + 192;
		}
		else if (l == 255)
		{
		    bodyLen = (in.read() << 24) | (in.read() << 16) |  (in.read() << 8)  | in.read();
		}

       int		tag = in.read();

       if (tag < 0)
       {
       		throw new EOFException("unexpected EOF reading signature sub packet");
       }
       
       byte[]	data = new byte[bodyLen - 1];
       
       this.readFully(data, 0, data.length);
       
       boolean	isCritical = ((tag & 0x80) != 0);
       int			type = tag & 0x7f;

       switch (type)
       {
       case CREATION_TIME:
       		return new SignatureCreationTime(isCritical, data);
		case KEY_EXPIRE_TIME:
			  return new KeyExpirationTime(isCritical, data);
		case EXPIRE_TIME:
			  return new SignatureExpirationTime(isCritical, data);
       case REVOCABLE:
       		return new Revocable(isCritical, data);
       case EXPORTABLE:
       		return new Exportable(isCritical, data);
       case ISSUER_KEY_ID:
       		return new IssuerKeyID(isCritical, data);
       case TRUST_SIG:
       		return new TrustSignature(isCritical, data);
       case PREFERRED_COMP_ALGS:
       case PREFERRED_HASH_ALGS:
       case PREFERRED_SYM_ALGS:
       		return new PreferredAlgorithms(type, isCritical, data);
       case KEY_FLAGS:
       		return new KeyFlags(isCritical, data);
       }

       return new SignatureSubpacket(type, isCritical, data);
    }
}
