#ifndef FILTERS_H
#define FILTERS_H

#include "cryptlib.h"
#include "misc.h"
#include "smartptr.h"

class Filter : public BufferedTransformation
{
public:
	Filter(BufferedTransformation *outQ = NULL);
	Filter(const Filter &source);

	bool Attachable() {return true;}
	void Detach(BufferedTransformation *newOut = NULL);
	void Attach(BufferedTransformation *newOut);
	void Close()
		{InputFinished(); outQueue->Close();}

	unsigned long MaxRetrieveable()
		{return outQueue->MaxRetrieveable();}

	unsigned int Get(byte &outByte)
		{return outQueue->Get(outByte);}
	unsigned int Get(byte *outString, unsigned int getMax)
		{return outQueue->Get(outString, getMax);}

	unsigned int Peek(byte &outByte) const
		{return outQueue->Peek(outByte);}

	BufferedTransformation *OutQueue() {return outQueue.get();}

protected:
	member_ptr<BufferedTransformation> outQueue;

private:
	void operator=(const Filter &);	// assignment not allowed
};

class BlockFilterBase : public Filter
{
public:
	BlockFilterBase(BlockTransformation &cipher,
					BufferedTransformation *outQueue);
	virtual ~BlockFilterBase() {}

	void Put(byte inByte)
	{
		if (inBufSize == S)
			ProcessBuf();
		inBuf[inBufSize++]=inByte;
	}

	void Put(const byte *inString, unsigned int length);

protected:
	void ProcessBuf();

	BlockTransformation &cipher;
	const unsigned int S;
	SecByteBlock inBuf;
	unsigned int inBufSize;
};

class BlockEncryptionFilter : public BlockFilterBase
{
public:
	BlockEncryptionFilter(BlockTransformation &cipher, BufferedTransformation *outQueue = NULL)
		: BlockFilterBase(cipher, outQueue) {}

protected:
	void InputFinished();
};

class BlockDecryptionFilter : public BlockFilterBase
{
public:
	BlockDecryptionFilter(BlockTransformation &cipher, BufferedTransformation *outQueue = NULL)
		: BlockFilterBase(cipher, outQueue) {}

protected:
	void InputFinished();
};

class StreamCipherFilter : public Filter
{
public:
	StreamCipherFilter(StreamCipher &c,
					   BufferedTransformation *outQueue = NULL)
		: Filter(outQueue), cipher(c) {}

	void Put(byte inByte)
		{outQueue->Put(cipher.ProcessByte(inByte));}

	void Put(const byte *inString, unsigned int length);

private:
	StreamCipher &cipher;
};

class HashFilter : public Filter
{
public:
	HashFilter(HashModule &hm, BufferedTransformation *outQueue = NULL)
		: Filter(outQueue), hash(hm) {}

	void InputFinished();

	void Put(byte inByte)
		{hash.Update(&inByte, 1);}

	void Put(const byte *inString, unsigned int length)
		{hash.Update(inString, length);}

private:
	HashModule &hash;
};

class Source : public Filter
{
public:
	Source(BufferedTransformation *outQ = NULL)
		: Filter(outQ) {}

	void Put(byte)
		{Pump(1);}
	void Put(const byte *, unsigned int length)
		{Pump(length);}
	void InputFinished()
		{PumpAll();}

	virtual unsigned int Pump(unsigned int size) =0;
	virtual unsigned long PumpAll() =0;
};

class Sink : public BufferedTransformation
{
public:
	unsigned long MaxRetrieveable()
		{return 0;}
	unsigned int Get(byte &)
		{return 0;}
	unsigned int Get(byte *, unsigned int)
		{return 0;}
	unsigned int Peek(byte &) const
		{return 0;}
};

class BitBucket : public Sink
{
public:
	void Put(byte) {}
	void Put(const byte *, unsigned int) {}
};

BufferedTransformation *Insert(const byte *in, unsigned int length, BufferedTransformation *outQueue);
unsigned int Extract(Source *source, byte *out, unsigned int length);

#endif
