/*
   +----------------------------------------------------------------------+
   | Copyright (c) 2002-2005 J Smith <dark_panda@hushmail.com>            |
   | Crypto++ sources (not included) copyright (c) 1995-2003 Wei Dai      |
   +----------------------------------------------------------------------+
   | Check out COPYING for the exact license.                             |
   +----------------------------------------------------------------------+

$Id: jsink.h,v 1.5 2005/01/06 18:16:43 jay Exp $
*/

#ifndef __JSINK_H__
#define __JSINK_H__

#include "filters.h"

#ifdef PHP_WIN32
#include <cmath>
#endif

extern "C" {
#include "php.h"
}

using namespace CryptoPP;

class PHPStreamStore : public Store, private FilterPutSpaceHelper
{
	public:
		class Err : public Exception
		{
			public:
				Err(const std::string& s) : Exception(IO_ERROR, s) {}
		};

		class OpenErr : public Err
		{
			public:
				OpenErr(const std::string& filename) : Err("FileStore: error opening file for reading: " + filename) {}
		};

		class ReadErr : public Err
		{
			public:
				ReadErr() : Err("FileStore: error reading file") {}
		};

		PHPStreamStore() {}

		PHPStreamStore(php_stream** in)
		{
			StoreInitialize(MakeParameters("InputStreamPointer", *in));
		}

		PHPStreamStore(const char* filename)
		{
			StoreInitialize(MakeParameters("InputFileName", filename));
		}

		php_stream* GetStream()
		{
			return m_stream;
		}

		unsigned int Peek(byte &outByte) const;
		unsigned int TransferTo2(BufferedTransformation &target, unsigned long &transferBytes, const std::string &channel=NULL_CHANNEL, bool blocking=true);

		// These methods aren't actually used in the PHP extension, but we need to implement them anyway
		// because they're abstract...
		unsigned int CopyRangeTo2(BufferedTransformation& target, unsigned long& begin, unsigned long end = ULONG_MAX, const std::string& channel = NULL_CHANNEL, bool blocking = true) const { return 0; }
		unsigned long MaxRetrievable() const { return 0L; }

	private:
		void StoreInitialize(const NameValuePairs &parameters);
		php_stream* m_stream;

		byte* m_space;
		unsigned int m_len;
		bool m_waiting;
};

class PHPStreamSource : public SourceTemplate<PHPStreamStore>
{
	public:
		typedef PHPStreamStore::Err Err;
		typedef PHPStreamStore::OpenErr OpenErr;
		typedef PHPStreamStore::ReadErr ReadErr;

		PHPStreamSource(BufferedTransformation* attachment = NULL) : SourceTemplate<PHPStreamStore>(attachment) {}

		PHPStreamSource(php_stream** in, bool pumpAll, BufferedTransformation* attachment = NULL) : SourceTemplate<PHPStreamStore>(attachment)
		{
			SourceInitialize(pumpAll, MakeParameters("InputStreamPointer", *in));
		}

		PHPStreamSource(const char* filename, bool pumpAll, BufferedTransformation* attachment = NULL, bool binary = true) : SourceTemplate<PHPStreamStore>(attachment)
		{
			SourceInitialize(pumpAll, MakeParameters("InputFileName", filename)("InputBinaryMode", binary));
		}

		php_stream* GetStream()
		{
			return m_store.GetStream();
		}
};


class PHPStreamSink : public Sink
{
	public:
		class Err : public Exception
		{
			public:
				Err(const std::string& s) : Exception(IO_ERROR, s) {}
		};

		class OpenErr : public Err
		{
			public:
				OpenErr(const std::string &filename) : Err("FileSink: error opening file for writing: " + filename) {}
		};

		class WriteErr : public Err
		{
			public:
				WriteErr() : Err("FileSink: error writing file") {}
		};

		PHPStreamSink()
		{
			m_stream = NULL;
		}

		PHPStreamSink(php_stream** out)
		{
			IsolatedInitialize(MakeParameters("OutputStreamPointer", *out));
		}

		PHPStreamSink(const char* filename, bool binary = true)
		{
			IsolatedInitialize(MakeParameters("OutputFileName", filename)("OutputBinaryMode", binary));
		}

		php_stream* GetStream()
		{
			return m_stream;
		}

		void IsolatedInitialize(const NameValuePairs& parameters);
		unsigned int Put2(const byte* inString, unsigned int length, int messageEnd, bool blocking);

		// IsolatedFlush isn't actually used, but needs to be implemented because
		// it's abstract...
		bool IsolatedFlush(bool hardFlush, bool blocking) { return false; };

	private:
		php_stream* m_stream;
};

#endif
