using System;
using System.IO;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Security;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
	/// A one pass signature object.
    public class PgpOnePassSignature
    {
        private OnePassSignaturePacket sigPack;
        private int signatureType;
		private ISigner sig;
		private byte lastb;
		internal PgpOnePassSignature(
            BcpgInputStream bcpgInput)
            : this((OnePassSignaturePacket) bcpgInput.ReadPacket())
        {
        }
		internal PgpOnePassSignature(
            OnePassSignaturePacket sigPack)
        {
            this.sigPack = sigPack;
            this.signatureType = sigPack.SignatureType;
            try
            {
                this.sig = SignerUtilities.GetSigner(
					PgpUtilities.GetSignatureName(sigPack.KeyAlgorithm, sigPack.HashAlgorithm));
            }
            catch (Exception e)
            {
                throw new PgpException("can't set up signature object.",  e);
            }
        }
		/// Initialise the signature object for verification.
        public void InitVerify(
            PgpPublicKey pubKey)
        {
			lastb = 0;
			try
            {
                sig.Init(false, pubKey.GetKey());
            }
			catch (InvalidKeyException e)
            {
                throw new PgpException("invalid key.", e);
            }
        }
		public void Update(
            byte b)
        {
			if (signatureType == PgpSignature.CanonicalTextDocument)
			{
				doCanonicalUpdateByte(b);
			}
			else
			{
				sig.Update(b);
			}
        }
		private void doCanonicalUpdateByte(
			byte b)
		{
			if (b == '\r')
			{
				doUpdateCRLF();
			}
			else if (b == '\n')
			{
				if (lastb != '\r')
				{
					doUpdateCRLF();
				}
			}
			else
			{
				sig.Update(b);
			}
			lastb = b;
		}
		private void doUpdateCRLF()
		{
			sig.Update((byte)'\r');
			sig.Update((byte)'\n');
		}
		public void Update(
            byte[] bytes)
        {
            if (signatureType == PgpSignature.CanonicalTextDocument)
            {
                for (int i = 0; i != bytes.Length; i++)
                {
                    doCanonicalUpdateByte(bytes[i]);
                }
            }
            else
            {
                sig.BlockUpdate(bytes, 0, bytes.Length);
            }
        }
        public void Update(
            byte[]  bytes,
            int     off,
            int     length)
        {
            if (signatureType == PgpSignature.CanonicalTextDocument)
            {
                int finish = off + length;
                for (int i = off; i != finish; i++)
                {
                    doCanonicalUpdateByte(bytes[i]);
                }
            }
            else
            {
                sig.BlockUpdate(bytes, off, length);
            }
        }
		/// Verify the calculated signature against the passed in PgpSignature.
        public bool Verify(
            PgpSignature pgpSig)
        {
            byte[] trailer = pgpSig.GetSignatureTrailer();
			sig.BlockUpdate(trailer, 0, trailer.Length);
			return sig.VerifySignature(pgpSig.GetSignature());
        }
        public long KeyId
        {
			get { return sigPack.KeyId; }
        }
		public int SignatureType
        {
            get { return sigPack.SignatureType; }
        }
		public HashAlgorithmTag HashAlgorithm
		{
			get { return sigPack.HashAlgorithm; }
		}
		public PublicKeyAlgorithmTag KeyAlgorithm
		{
			get { return sigPack.KeyAlgorithm; }
		}
		public byte[] GetEncoded()
        {
            MemoryStream bOut = new MemoryStream();
            Encode(bOut);
            return bOut.ToArray();
        }
		public void Encode(
            Stream outStr)
        {
            BcpgOutputStream.Wrap(outStr).WritePacket(sigPack);
        }
    }
}