// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
#if 0
namespace frantic { namespace files {

#include <zlib.h>

#pragma warning( push )
#pragma warning( disable : 4511 4512 4800 )
#include <zfstream.h>
#pragma warning( pop )

template<class DelegateReader>
class zlib_reader {
public:
	inline zlib_reader();
	virtual ~zlib_reader() {
		close();
	}

	inline void open( const frantic::tstring& filePath );
	inline void close();

	/**
	 * Explicitly position the read pointer. Only supported before the first call to read(). Will seek in underlying file, not the de-compressed output.
	 * @param off The location to seek to, relative to the start of the stream.
	 * @return (*this)
	 */
	inline zlib_reader& seekg( std::ios::off_type off );

	/**
	 * Will read the specified number of decompressed characters from the stream and store them in the supplied buffer. Throws an exception if it cannot
	 * extract the specified amount of characters (This is different from std::istream).
	 * @param dest The destination of decompressed data
	 * @param readSize The number of decompressed characters to read.
	 * @return (*this)
	 */
	inline zlib_reader& read( char* dest, std::size_t readSize );

protected:
	virtual int init_stream( z_stream * stream ) {
		return inflateInit( stream );
	}

	DelegateReader m_delegate;

	bool m_bOpen;
	z_stream m_zstream;
};

template<class DelegateReader>
class gzip_reader : public zlib_reader<DelegateReader> {
public:
	gzip_reader() {
	}
private:
	int init_stream( z_stream * stream ) {
		return inflateInit2( stream, 16+MAX_WBITS );
	}
};

#ifdef WIN32
/**
 * This class is a Windows specific reader that uses Asynchronous I/O and two buffers to allow processing of one buffer's data while
 * the operating system (or ideally the storage driver directly via DMA) fills the other buffer. It also disables the Windows disk cache
 * for optimal throughput and control over read size.
 */
class win32_reader{
public:
	win32_reader();
	~win32_reader();

	void open( const frantic::tstring& filePath );
	void close();

	/** 
	 * Only supported before the first call to read(). Repositions the read pointer relative to the start of the file. There is an
	 * unspecified limit to how far the read pointer can be adjusted.
	 * @param off The offset from the start of the file to reposition to.
	 */
	void seekg( std::ios::off_type off );

	/**
	 * Will return a filled buffer from a previously started asynchronous read, and start a new asynchronous read. All buffers obtained
	 * from previous calls to read() are invalidated.
	 * @param[out] outSize The size of the returned buffer in bytes.
	 * @return A buffer filled with data read from the file.
	 */
	char* read( std::size_t& outSize );

private:
	//Buffer for reading data into, allocated using VirtualAlloc in order to get it aligned to a page (ie. 4K)
	LPVOID m_buffer;

	//Size of one of the buffers we are reading into. There are two, so the total allocation size is twice this number.
	DWORD m_singleBufferSize;

	//Handle to the open file.
	HANDLE m_fileHandle;

	//Will be non-zero if the last ReadFile call returned synchronously. This is the size of that read.
	DWORD m_lastSyncReadSize;
	
	//TRUE once we've read the entire file.
	BOOL m_eof;

	OVERLAPPED m_overlapped;

	//Current offset within the file of the last read operation started.
	LARGE_INTEGER m_fileOffset;

	//Total size of the file.
	LARGE_INTEGER m_fileSize;
	
	//Path to the file being read.
	frantic::tstring m_filePath;

	//Index of the buffer currently being filled by an asynchronous read operation.
	std::size_t m_curBuffer;

	//For supporting seekg(), this is an offset to apply to the front of the read buffer when returning the ptr.
	DWORD m_readOffset;
};

typedef zlib_reader<win32_reader> zlib_read_interface;
typedef gzip_reader<win32_reader> gzip_read_interface;
#else

/**
 * This is a simple portable implementation of the read interface. Used on non-Windows platforms at the moment.
 */
class basic_reader{
public:
	basic_reader();
	~basic_reader();

	void open( const frantic::tstring& filePath );
	
	void close();

	void seekg( std::ios::off_type off );

	char* read( std::size_t& outSize );

private:
	FILE* m_file;

	std::size_t m_bufferSize;
	boost::scoped_array<char> m_buffer;
};

typedef zlib_reader<basic_reader> zlib_read_interface;
typedef gzip_reader<basic_reader> gzip_read_interface;
#endif

template <class T>
zlib_reader<T>::zlib_reader(){
	m_bOpen = false;
	memset( &m_zstream, 0, sizeof(z_stream) );
}

template <class T>
void zlib_reader<T>::open( const frantic::tstring& filePath ){
	m_delegate.open( filePath );
	
	memset( &m_zstream, 0, sizeof(m_zstream) );
	if( Z_OK != init_stream( &m_zstream ) )
		throw std::runtime_error( "zlib_reader::open() inflateInit() failed:" + std::string( m_zstream.msg ? m_zstream.msg : "" ) );
	
	m_bOpen = true;
}

template <class T>
void zlib_reader<T>::close(){
	if(m_bOpen){
		inflateEnd(&m_zstream);
		m_bOpen = false;
	}

	m_delegate.close();
}

template <class T>
zlib_reader<T>& zlib_reader<T>::seekg( std::ios::off_type off ){
	m_delegate.seekg( off );
	return *this;
}

template <class T>
zlib_reader<T>& zlib_reader<T>::read( char* dest, std::size_t readSize ){
	m_zstream.avail_out = static_cast<uInt>(readSize);
	m_zstream.next_out = reinterpret_cast<Bytef*>(dest);

	do{
		if(m_zstream.avail_in == 0){
			std::size_t count;
			char* buffer = m_delegate.read( count );
			if( !buffer )
				throw std::runtime_error( "zlib_reader::read() Unexpected EOF" );

			m_zstream.avail_in = static_cast<uInt>(count);
			m_zstream.next_in = reinterpret_cast<Bytef*>(buffer);
		}

		int ret = inflate(&m_zstream, Z_NO_FLUSH);
		if(Z_OK != ret && Z_STREAM_END != ret)
			throw std::runtime_error( "zlib_reader::read() inflate() failed: " + std::string( m_zstream.msg ? m_zstream.msg : "" ) );
	}while(m_zstream.avail_out != 0);
		
	return *this;
}

} }
#endif