/*
	ZBinaryReader.hpp
	Author: Patrick Baggett <ptbaggett@762studios.com
	Created: 3/6/2013

	Purpose: 

	Binary stream reader interface

	License:

	TODO

*/

#pragma once

#ifndef _ZBINARYREADER_HPP
#define _ZBINARYREADER_HPP


#include <pstdint.h>
#include <SST/SST_Endian.h>

class ZBinaryReader
{
	public:
		ZBinaryReader(SST_ByteOrder streamOrder) :
		streamByteOrder(streamOrder)
		{

		}

		//SINGLE ELEMENT READ
		/*
		virtual public ZBinaryReader::ReadU8
	 
		Reads a single unsigned 8-bit value from the stream.
		No validation is performed.
	 
		@return (uint8_t) - value read from the stream
		*/
		virtual uint8_t ReadU8() = 0;

		/*
		virtual public ZBinaryReader::ReadU16
	 
		Reads a single unsigned 16-bit value from the stream.
		No validation is performed.
	 
		@return (uint16_t) - value read from the stream
		*/
		virtual uint16_t ReadU16() = 0;

		/*
		virtual public ZBinaryReader::ReadU32
	 
		Reads a single unsigned 32-bit value from the stream.
		No validation is performed.
	 
		@return (uint32_t) - value read from the stream
		*/
		virtual uint32_t ReadU32() = 0;

		/*
		virtual public ZBinaryReader::ReadU64
	 
		Reads a single unsigned 64-bit value from the stream.
		No validation is performed.
	 
		@return (uint64_t) - value read from the stream
		*/
		virtual uint64_t ReadU64() = 0;

		/*
		public ZBinaryReader::ReadF32
	 
		Reads a single 32-bit floating point value from the stream.
		No validation is performed.
	 
		@return (float) - value read from the stream
		*/
		float ReadF32() { return cast_i2f(ReadU32()); }

		/*
		public ZBinaryReader::ReadF64
	 
		Reads a single 64-bit floating point value from the stream.
		No validation is performed.
	 
		@return (double) - value read from the stream
		*/
		double ReadF64() { return cast_i2d(ReadU64()); }
		

		//=====================================================================
		//ARRAY READ
		//=====================================================================

		/*
		virtual public ZBinaryReader::ReadU8Array
	 
		Reads an array of 8-bit unsigned values from the stream.
		No validation is performed.
	 
		@param ptr - The base of the array
		@param count - Number of elements to read
		@return (void)
		*/
		virtual void ReadU8Array(uint8_t* ptr, size_t count) = 0;

		/*
		virtual public ZBinaryReader::ReadU16Array
	 
		Reads an array of 16-bit unsigned values from the stream.
		No validation is performed.
	 
		@param ptr - The base of the array
		@param count - Number of elements to read
		@return (void)
		*/
		virtual void ReadU16Array(uint16_t* ptr, size_t count) = 0;

		/*
		virtual public ZBinaryReader::ReadU32Array
	 
		Reads an array of 32-bit unsigned values from the stream.
		No validation is performed.
	 
		@param ptr - The base of the array
		@param count - Number of elements to read
		@return (void)
		*/
		virtual void ReadU32Array(uint32_t* ptr, size_t count) = 0;

		/*
		virtual public ZBinaryReader::ReadU64Array
	 
		Reads an array of 64-bit unsigned values from the stream.
		No validation is performed.
	 
		@param ptr - The base of the array
		@param count - Number of elements to read
		@return (void)
		*/
		virtual void ReadU64Array(uint64_t* ptr, size_t count) = 0;

		/*
		public ZBinaryReader::ReadF32Array
	 
		Reads an array of 32-bit floating point values from the stream.
		No validation is performed.
	 
		@param ptr - The base of the array
		@param count - Number of elements to read
		@return (void)
		*/
		void ReadF32Array(float* ptr, size_t count) { ReadU32Array((uint32_t*)ptr, count); }

		/*
		public ZBinaryReader::ReadF64Array
	 
		Reads an array of 64-bit floating point values from the stream.
		No validation is performed.
	 
		@param ptr - The base of the array
		@param count - Number of elements to read
		@return (void)
		*/
		void ReadF64Array(double* ptr, size_t count) { ReadU64Array((uint64_t*)ptr, count); }

		//=====================================================================
		//STREAM SEEKING / POSITIONING
		//=====================================================================

		/*
		virtual public ZBinaryReader::GetLength
	 
		Gets the total length of the stream in bytes.
		
		@return (size_t) - The total length of the stream
		*/
		virtual size_t GetLength() const = 0;

		/*
		virtual public ZBinaryReader::GetOffset
	 
		Gets the current (zero-based) offset into the stream from which the next read will
		be performed.
		
		@return (size_t) - The offset.
		*/
		virtual size_t GetOffset() const = 0;

		/*
		public ZBinaryReader::SeekForward
	 
		Advances the offset into the stream by the given number of bytes.
		No validation is performed.
		
		@param amount - The amount of seek forward by.
		@return (void)
		*/
		void SeekForward(size_t amount) { SeekTo(GetOffset() + amount); }

		/*
		public ZBinaryReader::SeekBackward
	 
		Rewinds the offset into the stream by the given number of bytes.
		No validation is performed.
		
		@param amount - The amount of seek backward by.
		@return (void)
		*/
		void SeekBackward(size_t amount) { SeekTo(GetOffset() - amount); }

		/*
		virtual public ZBinaryReader::SeekTo
	 
		Directly sets the offset into the stream from which reads will occur.
		No validation is performed.
		
		@param offset - The new offset
		@return (void)
		*/
		virtual void SeekTo(size_t offset) = 0;

		/*
		public ZBinaryReader::Rewind
	 
		Sets the offset to the start of the stream
		
		@param offset - The new offset
		@return (void)
		*/
		void Rewind() { SeekTo(0); }

		/*
		public ZBinaryReader::CanRead
	 
		Checks if the given number of bytes can be read
		
		@param bytes - The number of bytes that wish to be read
		@return (void)
		*/
		bool CanRead(size_t bytes) const { return (GetLength() - GetOffset() >= bytes); }

		//=====================================================================
		//SAFE SEEK FUNCTIONS
		//=====================================================================
		/*
		public ZBinaryReader::SafeSeekForward
	 
		Attempts to advance the offset into the stream by the given number of bytes. If
		this would produce an out-of-bounds result, then false is returned and the offset
		is not adjusted, otherwise, the offset is adjusted and true is returned.
		
		@param amount - The amount to seek forward by
		@return (bool) - True if the seek operation succeeded, false if the value was invalid
		*/
		bool SafeSeekForward(size_t amount)
		{
			bool ok = (GetLength() - GetOffset() > amount);	//i.e.	length > offset + amount
			if(ok)
				SeekTo(GetOffset() + amount);
			return ok;
		}

		/*
		public ZBinaryReader::SafeSeekBackward
	 
		Attempts to rewind the offset into the stream by the given number of bytes. If
		this would produce an out-of-bounds result, then false is returned and the offset
		is not adjusted, otherwise, the offset is adjusted and true is returned.
		
		@param amount - The amount to seek backward by
		@return (bool) - True if the seek operation succeeded, false if the value was invalid
		*/
		bool SafeSeekBackward(size_t amount)
		{
			bool ok = (GetOffset() >= amount);	//i.e.	offset - amount >= 0
			if(ok)
				SeekTo(GetOffset() - amount);
			return ok;
		}

		/*
		public ZBinaryReader::SafeSeekTo
	 
		Attempts to set the offset into the stream directly. If the new offset is
		out-of-bounds, then false is returned and the offset is not adjusted,
		otherwise, the offset is adjusted and true is returned.
		
		@return (bool) - True if the seek operation succeeded, false if the value was invalid
		*/
		bool SafeSeekTo(size_t offset)
		{
			bool ok = (offset < GetLength());
			if(ok) SeekTo(offset);
			return ok;
		}

		//=====================================================================
		//SAFE READ FUNCTIONS
		//=====================================================================
		/*
		public ZBinaryReader::SafeReadU8
	 
		Attempt to reads a single unsigned 8-bit value from the stream. If this would
		result in reading out of bounds, false is returned and *ptr is unmodified, otherwise
		the value is read and stored at *ptr and true is returned.
		No validation is performed.
	 
		@param ptr - The address to store the read value at
		@return (bool) - If the value was successfully read
		*/
		bool SafeReadU8(uint8_t* ptr)
		{
			bool ok = CanRead(sizeof(uint8_t));
			if(ok) *ptr = ReadU8();
			return ok;
		}

		/*
		public ZBinaryReader::SafeReadU16
	 
		Attempt to reads a single unsigned 16-bit value from the stream. If this would
		result in reading out of bounds, false is returned and *ptr is unmodified, otherwise
		the value is read and stored at *ptr and true is returned.
		No validation is performed.
	 
		@param ptr - The address to store the read value at
		@return (bool) - If the value was successfully read
		*/
		bool SafeReadU16(uint16_t* ptr)
		{
			bool ok = CanRead(sizeof(uint16_t));
			if(ok) *ptr = ReadU16();
			return ok;
		}

		/*
		public ZBinaryReader::SafeReadU32
	 
		Attempt to reads a single unsigned 32-bit value from the stream. If this would
		result in reading out of bounds, false is returned and *ptr is unmodified, otherwise
		the value is read and stored at *ptr and true is returned.
		No validation is performed.
	 
		@param ptr - The address to store the read value at
		@return (bool) - If the value was successfully read
		*/
		bool SafeReadU32(uint32_t* ptr)
		{
			bool ok = CanRead(sizeof(uint32_t));
			if(ok) *ptr = ReadU32();
			return ok;
		}

		/*
		public ZBinaryReader::SafeReadU64
	 
		Attempt to reads a single unsigned 64-bit value from the stream. If this would
		result in reading out of bounds, false is returned and *ptr is unmodified, otherwise
		the value is read and stored at *ptr and true is returned.
		No validation is performed.
	 
		@param ptr - The address to store the read value at
		@return (bool) - If the value was successfully read
		*/
		bool SafeReadU64(uint64_t* ptr)
		{
			bool ok = CanRead(sizeof(uint64_t));
			if(ok) *ptr = ReadU64();
			return ok;
		}

		/*
		public ZBinaryReader::SafeReadF32
	 
		Attempt to reads a single 32-bit floating point value from the stream. If this would
		result in reading out of bounds, false is returned and *ptr is unmodified, otherwise
		the value is read and stored at *ptr and true is returned.
		No validation is performed.
	 
		@param ptr - The address to store the read value at
		@return (bool) - If the value was successfully read
		*/
		bool SafeReadF32(float* ptr)
		{
			bool ok = CanRead(sizeof(float));
			if(ok) *ptr = ReadF32();
			return ok;
		}

		/*
		public ZBinaryReader::SafeReadF64
	 
		Attempt to reads a single 64-bit floating point value from the stream. If this would
		result in reading out of bounds, false is returned and *ptr is unmodified, otherwise
		the value is read and stored at *ptr and true is returned.
		No validation is performed.
	 
		@param ptr - The address to store the read value at
		@return (bool) - If the value was successfully read
		*/
		bool SafeReadF64(double* ptr)
		{
			bool ok = CanRead(sizeof(double));
			if(ok) *ptr = ReadF64();
			return ok;
		}

		/*
		public ZBinaryReader::SafeReadU8Array
	 
		Attempt to reads an array of unsigned 8-bit values from the stream. If this would
		result in reading out of bounds, false is returned and *ptr is unmodified, otherwise
		the values are read and stored at *ptr and true is returned.
		No validation is performed.
	 
		@param ptr - The address to store the values
		@param count - The number of values to read
		@return (bool) - If the array was successfully read
		*/
		bool SafeReadU8Array(uint8_t* ptr, size_t count)
		{
			bool ok = CanRead(count);
			if(ok) ReadU8Array(ptr, count);
			return ok;
		}

		/*
		public ZBinaryReader::SafeReadU16Array
	 
		Attempt to reads an array of unsigned 16-bit values from the stream. If this would
		result in reading out of bounds, false is returned and *ptr is unmodified, otherwise
		the values are read and stored at *ptr and true is returned.
		No validation is performed.
	 
		@param ptr - The address to store the values
		@param count - The number of values to read
		@return (bool) - If the array was successfully read
		*/
		bool SafeReadU16Array(uint16_t* ptr, size_t count)
		{
			bool ok = (SIZE_MAX / count > sizeof(uint16_t));	//i.e. SIZE_MAX > count * size
			ok = ok && CanRead(count * sizeof(uint16_t));
			if(ok) ReadU16Array(ptr, count);
			return ok;
		}

		/*
		public ZBinaryReader::SafeReadU32Array
	 
		Attempt to reads an array of unsigned 32-bit values from the stream. If this would
		result in reading out of bounds, false is returned and *ptr is unmodified, otherwise
		the values are read and stored at *ptr and true is returned.
		No validation is performed.
	 
		@param ptr - The address to store the values
		@param count - The number of values to read
		@return (bool) - If the array was successfully read
		*/
		bool SafeReadU32Array(uint32_t* ptr, size_t count)
		{
			bool ok = (SIZE_MAX / count > sizeof(uint32_t));	//i.e. SIZE_MAX > count * size
			ok = ok && CanRead(count * sizeof(uint32_t));
			if(ok) ReadU32Array(ptr, count);
			return ok;
		}

		/*
		public ZBinaryReader::SafeReadU64Array
	 
		Attempt to reads an array of unsigned 64-bit values from the stream. If this would
		result in reading out of bounds, false is returned and *ptr is unmodified, otherwise
		the values are read and stored at *ptr and true is returned.
		No validation is performed.
	 
		@param ptr - The address to store the values
		@param count - The number of values to read
		@return (bool) - If the array was successfully read
		*/
		bool SafeReadU64Array(uint64_t* ptr, size_t count)
		{
			bool ok = (SIZE_MAX / count > sizeof(uint64_t));	//i.e. SIZE_MAX > count * size
			ok = ok && CanRead(count * sizeof(uint64_t));
			if(ok) ReadU64Array(ptr, count);
			return ok;
		}

		/*
		public ZBinaryReader::SafeReadF32Array
	 
		Attempt to reads an array of 32-bit floating point values from the stream. If this would
		result in reading out of bounds, false is returned and *ptr is unmodified, otherwise
		the values are read and stored at *ptr and true is returned.
		No validation is performed.
	 
		@param ptr - The address to store the values
		@param count - The number of values to read
		@return (bool) - If the array was successfully read
		*/
		bool SafeReadF32Array(float* ptr, size_t count) { return SafeReadU32Array((uint32_t*)ptr, count); }

		/*
		public ZBinaryReader::SafeReadF64Array
	 
		Attempt to reads an array of 64-bit floating point values from the stream. If this would
		result in reading out of bounds, false is returned and *ptr is unmodified, otherwise
		the values are read and stored at *ptr and true is returned.
		No validation is performed.
	 
		@param ptr - The address to store the values
		@param count - The number of values to read
		@return (bool) - If the array was successfully read
		*/
		bool SafeReadF64Array(double* ptr, size_t count) { return SafeReadU64Array((uint64_t*)ptr, count); }

		//=====================================================================
		//TYPED ALIASES
		//=====================================================================
		/*
		public ZBinaryReader::ReadI8
	 
		Reads a single signed 8-bit value from the stream.
		No validation is performed.
	 
		@return (int8_t) - value read from the stream
		*/
		int8_t ReadI8() { return (int8_t)ReadU8(); }

		/*
		public ZBinaryReader::ReadI16
	 
		Reads a single signed 16-bit value from the stream.
		No validation is performed.
	 
		@return (int16_t) - value read from the stream
		*/
		int16_t ReadI16() { return (int16_t)ReadU16(); }

		/*
		public ZBinaryReader::ReadI32
	 
		Reads a single signed 32-bit value from the stream.
		No validation is performed.
	 
		@return (int32_t) - value read from the stream
		*/
		int32_t ReadI32() { return (int32_t)ReadU32(); }

		/*
		public ZBinaryReader::ReadI64
	 
		Reads a single signed 64-bit value from the stream.
		No validation is performed.
	 
		@return (int64_t) - value read from the stream
		*/
		int64_t ReadI64() { return (int64_t)ReadU64(); }

		/*
		public ZBinaryReader::ReadI8Array
	 
		Reads an array of 8-bit signed values from the stream.
		No validation is performed.
	 
		@param ptr - The base of the array
		@param count - Number of elements to read
		@return (void)
		*/
		void ReadI8Array(int8_t* ptr, size_t count) { return ReadU8Array((uint8_t*)ptr, count); }

		/*
		public ZBinaryReader::ReadI16Array
	 
		Reads an array of 16-bit signed values from the stream.
		No validation is performed.
	 
		@param ptr - The base of the array
		@param count - Number of elements to read
		@return (void)
		*/
		void ReadI16Array(int16_t* ptr, size_t count) { return ReadU16Array((uint16_t*)ptr, count); }

		/*
		public ZBinaryReader::ReadI32Array
	 
		Reads an array of 32-bit signed values from the stream.
		No validation is performed.
	 
		@param ptr - The base of the array
		@param count - Number of elements to read
		@return (void)
		*/
		void ReadI32Array(int32_t* ptr, size_t count) { return ReadU32Array((uint32_t*)ptr, count); }

		/*
		public ZBinaryReader::ReadI64Array
	 
		Reads an array of 64-bit signed values from the stream.
		No validation is performed.
	 
		@param ptr - The base of the array
		@param count - Number of elements to read
		@return (void)
		*/
		void ReadI64Array(int64_t* ptr, size_t count) { return ReadU64Array((uint64_t*)ptr, count); }

		/*
		public ZBinaryReader::SafeReadI8
	 
		Attempt to reads a single signed 8-bit value from the stream. If this would
		result in reading out of bounds, false is returned and *ptr is unmodified, otherwise
		the value is read and stored at *ptr and true is returned.
		No validation is performed.
	 
		@param ptr - The address to store the read value at
		@return (bool) - If the value was successfully read
		*/
		bool SafeReadI8(int8_t* ptr) { return SafeReadU8((uint8_t*)ptr); }

		/*
		public ZBinaryReader::SafeReadI16
	 
		Attempt to reads a single signed 16-bit value from the stream. If this would
		result in reading out of bounds, false is returned and *ptr is unmodified, otherwise
		the value is read and stored at *ptr and true is returned.
		No validation is performed.
	 
		@param ptr - The address to store the read value at
		@return (bool) - If the value was successfully read
		*/
		bool SafeReadI16(int16_t* ptr) { return SafeReadU16((uint16_t*)ptr); }

		/*
		public ZBinaryReader::SafeReadI32
	 
		Attempt to reads a single signed 32-bit value from the stream. If this would
		result in reading out of bounds, false is returned and *ptr is unmodified, otherwise
		the value is read and stored at *ptr and true is returned.
		No validation is performed.
	 
		@param ptr - The address to store the read value at
		@return (bool) - If the value was successfully read
		*/
		bool SafeReadI32(int32_t* ptr) { return SafeReadU32((uint32_t*)ptr); }

		/*
		public ZBinaryReader::SafeReadI64
	 
		Attempt to reads a single signed 64-bit value from the stream. If this would
		result in reading out of bounds, false is returned and *ptr is unmodified, otherwise
		the value is read and stored at *ptr and true is returned.
		No validation is performed.
	 
		@param ptr - The address to store the read value at
		@return (bool) - If the value was successfully read
		*/
		bool SafeReadI64(int64_t* ptr) { return SafeReadU64((uint64_t*)ptr); }

		/*
		public ZBinaryReader::SafeReadI8Array
	 
		Attempt to reads an array of signed 8-bit values from the stream. If this would
		result in reading out of bounds, false is returned and *ptr is unmodified, otherwise
		the values are read and stored at *ptr and true is returned.
		No validation is performed.
	 
		@param ptr - The address to store the values
		@param count - The number of values to read
		@return (bool) - If the array was successfully read
		*/
		bool SafeReadI8Array(int8_t* ptr, size_t count) { return SafeReadU8Array((uint8_t*)ptr, count); }

		/*
		public ZBinaryReader::SafeReadI16Array
	 
		Attempt to reads an array of signed 16-bit values from the stream. If this would
		result in reading out of bounds, false is returned and *ptr is unmodified, otherwise
		the values are read and stored at *ptr and true is returned.
		No validation is performed.
	 
		@param ptr - The address to store the values
		@param count - The number of values to read
		@return (bool) - If the array was successfully read
		*/
		bool SafeReadI16Array(int16_t* ptr, size_t count) { return SafeReadU16Array((uint16_t*)ptr, count); }

		/*
		public ZBinaryReader::SafeReadI32Array
	 
		Attempt to reads an array of signed 32-bit values from the stream. If this would
		result in reading out of bounds, false is returned and *ptr is unmodified, otherwise
		the values are read and stored at *ptr and true is returned.
		No validation is performed.
	 
		@param ptr - The address to store the values
		@param count - The number of values to read
		@return (bool) - If the array was successfully read
		*/
		bool SafeReadI32Array(int32_t* ptr, size_t count) { return SafeReadU32Array((uint32_t*)ptr, count); }

		/*
		public ZBinaryReader::SafeReadI64Array
	 
		Attempt to reads an array of signed 64-bit values from the stream. If this would
		result in reading out of bounds, false is returned and *ptr is unmodified, otherwise
		the values are read and stored at *ptr and true is returned.
		No validation is performed.
	 
		@param ptr - The address to store the values
		@param count - The number of values to read
		@return (bool) - If the array was successfully read
		*/
		bool SafeReadI64Array(int64_t* ptr, size_t count) { return SafeReadU64Array((uint64_t*)ptr, count); }


		//=====================================================================
		//GETTERS / SETTERS
		//=====================================================================
		/*
		public ZBinaryReader::GetStreamByteOrder
		
		Gets the byte order in which the stream (source data) is read as.
		
		@return (SST_ByteOrder) - The byte order
		*/
		SST_ByteOrder GetStreamByteOrder() const { return streamByteOrder; }

		/*
		public ZBinaryReader::SetStreamByteOrder
		
		Sets the byte order in which the stream (source data) is read as.
		
		@param newOrder - The new byte order
		@return (void)
		*/
		void SetStreamByteOrder(SST_ByteOrder newOrder) { streamByteOrder = newOrder; }

	private:
		SST_ByteOrder streamByteOrder;	//The current byte order the stream is being read in.

};

#endif

