#pragma once

#define INLINE inline

#define BITS_PER_INT 32

INLINE int GetBitForBitnum(int bitNum)
{
	static int bitsForBitnum[] = {
		(1 << 0),  (1 << 1),  (1 << 2),	 (1 << 3),	(1 << 4),  (1 << 5),  (1 << 6),	 (1 << 7),	(1 << 8),  (1 << 9),  (1 << 10),
		(1 << 11), (1 << 12), (1 << 13), (1 << 14), (1 << 15), (1 << 16), (1 << 17), (1 << 18), (1 << 19), (1 << 20), (1 << 21),
		(1 << 22), (1 << 23), (1 << 24), (1 << 25), (1 << 26), (1 << 27), (1 << 28), (1 << 29), (1 << 30), (1 << 31),
	};

	return bitsForBitnum[(bitNum) & (BITS_PER_INT - 1)];
}

#undef BITS_PER_INT

using u8 = uint8_t;
using u16 = uint16_t;
using u32 = uint32_t;
using u64 = uint64_t;
using uptr = uintptr_t;

using i8 = int8_t;
using i16 = int16_t;
using i32 = int32_t;
using i64 = int64_t;
using iptr = intptr_t;

// Endianess, don't use on PPC64 nor ARM64BE
#define LittleDWord(val) (val)

static INLINE void StoreLittleDWord(u32* base, size_t dwordIndex, u32 dword)
{
	base[dwordIndex] = LittleDWord(dword);
}

static INLINE u32 LoadLittleDWord(u32* base, size_t dwordIndex)
{
	return LittleDWord(base[dwordIndex]);
}

#include <algorithm>

static inline const u32 s_nMaskTable[33] = {
	0,
	(1 << 1) - 1,
	(1 << 2) - 1,
	(1 << 3) - 1,
	(1 << 4) - 1,
	(1 << 5) - 1,
	(1 << 6) - 1,
	(1 << 7) - 1,
	(1 << 8) - 1,
	(1 << 9) - 1,
	(1 << 10) - 1,
	(1 << 11) - 1,
	(1 << 12) - 1,
	(1 << 13) - 1,
	(1 << 14) - 1,
	(1 << 15) - 1,
	(1 << 16) - 1,
	(1 << 17) - 1,
	(1 << 18) - 1,
	(1 << 19) - 1,
	(1 << 20) - 1,
	(1 << 21) - 1,
	(1 << 22) - 1,
	(1 << 23) - 1,
	(1 << 24) - 1,
	(1 << 25) - 1,
	(1 << 26) - 1,
	(1 << 27) - 1,
	(1 << 28) - 1,
	(1 << 29) - 1,
	(1 << 30) - 1,
	0x7fffffff,
	0xffffffff,
};

enum EBitCoordType
{
	kCW_None,
	kCW_LowPrecision,
	kCW_Integral
};

class BitBufferBase
{
  protected:
	INLINE void SetName(const char* name)
	{
		m_BufferName = name;
	}

  public:
	INLINE bool IsOverflowed()
	{
		return m_Overflow;
	}
	INLINE void SetOverflowed()
	{
		m_Overflow = true;
	}

	INLINE const char* GetName()
	{
		return m_BufferName;
	}

  private:
	const char* m_BufferName = "";

  protected:
	u8 m_Overflow = false;
};

class BFRead : public BitBufferBase
{
  public:
	BFRead() = default;

	INLINE BFRead(uptr data, size_t byteLength, size_t startPos = 0, const char* bufferName = 0)
	{
		StartReading(data, byteLength, startPos);

		if (bufferName)
			SetName(bufferName);
	}

  public:
	INLINE void StartReading(uptr data, size_t byteLength, size_t startPos = 0)
	{
		m_Data = reinterpret_cast<u32 const*>(data);
		m_DataIn = m_Data;

		m_DataBytes = byteLength;
		m_DataBits = byteLength << 3;

		m_DataEnd = reinterpret_cast<u32 const*>(reinterpret_cast<u8 const*>(m_Data) + m_DataBytes);

		Seek(startPos);
	}

	INLINE void GrabNextDWord(bool overflow = false)
	{
		if (m_Data == m_DataEnd)
		{
			m_CachedBitsLeft = 1;
			m_CachedBufWord = 0;

			m_DataIn++;

			if (overflow)
				SetOverflowed();
		}
		else
		{
			if (m_DataIn > m_DataEnd)
			{
				SetOverflowed();
				m_CachedBufWord = 0;
			}
			else
			{
				m_CachedBufWord = LittleDWord(*(m_DataIn++));
			}
		}
	}

	INLINE void FetchNext()
	{
		m_CachedBitsLeft = 32;
		GrabNextDWord(false);
	}

	INLINE i32 ReadOneBit()
	{
		i32 ret = m_CachedBufWord & 1;

		if (--m_CachedBitsLeft == 0)
			FetchNext();
		else
			m_CachedBufWord >>= 1;

		return ret;
	}

	INLINE u32 ReadUBitLong(i32 numBits)
	{
		if (m_CachedBitsLeft >= numBits)
		{
			u32 ret = m_CachedBufWord & s_nMaskTable[numBits];

			m_CachedBitsLeft -= numBits;

			if (m_CachedBitsLeft)
				m_CachedBufWord >>= numBits;
			else
				FetchNext();

			return ret;
		}
		else
		{
			// need to merge words
			u32 ret = m_CachedBufWord;
			numBits -= m_CachedBitsLeft;

			GrabNextDWord(true);

			if (IsOverflowed())
				return 0;

			ret |= ((m_CachedBufWord & s_nMaskTable[numBits]) << m_CachedBitsLeft);

			m_CachedBitsLeft = 32 - numBits;
			m_CachedBufWord >>= numBits;

			return ret;
		}
	}

	INLINE i32 ReadSBitLong(int numBits)
	{
		i32 ret = ReadUBitLong(numBits);
		return (ret << (32 - numBits)) >> (32 - numBits);
	}

	INLINE u32 ReadUBitVar()
	{
		u32 ret = ReadUBitLong(6);

		switch (ret & (16 | 32))
		{
		case 16:
			ret = (ret & 15) | (ReadUBitLong(4) << 4);
			// Assert(ret >= 16);
			break;
		case 32:
			ret = (ret & 15) | (ReadUBitLong(8) << 4);
			// Assert(ret >= 256);
			break;
		case 48:
			ret = (ret & 15) | (ReadUBitLong(32 - 4) << 4);
			// Assert(ret >= 4096);
			break;
		}

		return ret;
	}

	INLINE u32 PeekUBitLong(i32 numBits)
	{
		i32 nSaveBA = m_CachedBitsLeft;
		i32 nSaveW = m_CachedBufWord;
		u32 const* pSaveP = m_DataIn;
		u32 nRet = ReadUBitLong(numBits);

		m_CachedBitsLeft = nSaveBA;
		m_CachedBufWord = nSaveW;
		m_DataIn = pSaveP;

		return nRet;
	}

	INLINE float ReadBitFloat()
	{
		u32 value = ReadUBitLong(32);
		return *reinterpret_cast<float*>(&value);
	}

	/*INLINE float ReadBitCoord() {
		i32   intval = 0, fractval = 0, signbit = 0;
		float value = 0.0;

		// Read the required integer and fraction flags
		intval = ReadOneBit();
		fractval = ReadOneBit();

		// If we got either parse them, otherwise it's a zero.
		if (intval || fractval) {
			// Read the sign bit
			signbit = ReadOneBit();

			// If there's an integer, read it in
			if (intval) {
				// Adjust the integers from [0..MAX_COORD_VALUE-1] to [1..MAX_COORD_VALUE]
				intval = ReadUBitLong(COORD_INTEGER_BITS) + 1;
			}

			// If there's a fraction, read it in
			if (fractval) {
				fractval = ReadUBitLong(COORD_FRACTIONAL_BITS);
			}

			// Calculate the correct floating point value
			value = intval + ((float)fractval * COORD_RESOLUTION);

			// Fixup the sign if negative.
			if (signbit)
				value = -value;
		}

		return value;
	}

	INLINE float ReadBitCoordMP() {
		i32   intval = 0, fractval = 0, signbit = 0;
		float value = 0.0;

		bool inBounds = ReadOneBit() ? true : false;

		// Read the required integer and fraction flags
		intval = ReadOneBit();

		// If we got either parse them, otherwise it's a zero.
		if (intval) {
			// Read the sign bit
			signbit = ReadOneBit();

			// If there's an integer, read it in
			// Adjust the integers from [0..MAX_COORD_VALUE-1] to [1..MAX_COORD_VALUE]
			if (inBounds)
				value = ReadUBitLong(COORD_INTEGER_BITS_MP) + 1;
			else
				value = ReadUBitLong(COORD_INTEGER_BITS) + 1;
		}

		// Fixup the sign if negative.
		if (signbit)
			value = -value;

		return value;
	}

	INLINE float ReadBitCellCoord(int bits, EBitCoordType coordType) {
		bool bIntegral = (coordType == kCW_Integral);
		bool bLowPrecision = (coordType == kCW_LowPrecision);

		int   intval = 0, fractval = 0;
		float value = 0.0;

		if (bIntegral)
			value = ReadUBitLong(bits);
		else {
			intval = ReadUBitLong(bits);

			// If there's a fraction, read it in
			fractval = ReadUBitLong(bLowPrecision ? COORD_FRACTIONAL_BITS_MP_LOWPRECISION : COORD_FRACTIONAL_BITS);

			// Calculate the correct floating point value
			value = intval + ((float)fractval * (bLowPrecision ? COORD_RESOLUTION_LOWPRECISION : COORD_RESOLUTION));
		}

		return value;
	}

	INLINE float ReadBitNormal() {
		// Read the sign bit
		i32 signbit = ReadOneBit();

		// Read the fractional part
		u32 fractval = ReadUBitLong(NORMAL_FRACTIONAL_BITS);

		// Calculate the correct floating point value
		float value = (float)fractval * NORMAL_RESOLUTION;

		// Fixup the sign if negative.
		if (signbit)
			value = -value;

		return value;
	}

	INLINE void ReadBitVec3Coord(Vector& fa) {
		i32 xflag, yflag, zflag;

		// This vector must be initialized! Otherwise, If any of the flags aren't set,
		// the corresponding component will not be read and will be stack garbage.
		fa.Init(0, 0, 0);

		xflag = ReadOneBit();
		yflag = ReadOneBit();
		zflag = ReadOneBit();

		if (xflag)
			fa[0] = ReadBitCoord();
		if (yflag)
			fa[1] = ReadBitCoord();
		if (zflag)
			fa[2] = ReadBitCoord();
	}

	INLINE void ReadBitVec3Normal(Vector& fa) {
		i32 xflag = ReadOneBit();
		i32 yflag = ReadOneBit();

		if (xflag)
			fa[0] = ReadBitNormal();
		else
			fa[0] = 0.0f;

		if (yflag)
			fa[1] = ReadBitNormal();
		else
			fa[1] = 0.0f;

		// The first two imply the third (but not its sign)
		i32 znegative = ReadOneBit();

		float fafafbfb = fa[0] * fa[0] + fa[1] * fa[1];
		if (fafafbfb < 1.0f)
			fa[2] = sqrt(1.0f - fafafbfb);
		else
			fa[2] = 0.0f;

		if (znegative)
			fa[2] = -fa[2];
	}

	INLINE void ReadBitAngles(QAngle& fa) {
		Vector tmp;
		ReadBitVec3Coord(tmp);
		fa.Init(tmp.x, tmp.y, tmp.z);
	}*/

	INLINE float ReadBitAngle(int numBits)
	{
		float shift = (float)(GetBitForBitnum(numBits));

		i32 i = ReadUBitLong(numBits);
		float fReturn = (float)i * (360.0 / shift);

		return fReturn;
	}

	INLINE i32 ReadChar()
	{
		return ReadSBitLong(sizeof(char) << 3);
	}
	INLINE u32 ReadByte()
	{
		return ReadUBitLong(sizeof(unsigned char) << 3);
	}

	INLINE i32 ReadShort()
	{
		return ReadSBitLong(sizeof(short) << 3);
	}
	INLINE u32 ReadWord()
	{
		return ReadUBitLong(sizeof(unsigned short) << 3);
	}

	INLINE i32 ReadLong()
	{
		return (i32)(ReadUBitLong(sizeof(i32) << 3));
	}
	INLINE float ReadFloat()
	{
		u32 temp = ReadUBitLong(sizeof(float) << 3);
		return *reinterpret_cast<float*>(&temp);
	}

	INLINE u32 ReadVarInt32()
	{
		constexpr int kMaxVarint32Bytes = 5;

		u32 result = 0;
		int count = 0;
		u32 b;

		do
		{
			if (count == kMaxVarint32Bytes)
				return result;

			b = ReadUBitLong(8);
			result |= (b & 0x7F) << (7 * count);
			++count;
		} while (b & 0x80);

		return result;
	}

	INLINE u64 ReadVarInt64()
	{
		constexpr int kMaxVarintBytes = 10;

		u64 result = 0;
		int count = 0;
		u64 b;

		do
		{
			if (count == kMaxVarintBytes)
				return result;

			b = ReadUBitLong(8);
			result |= static_cast<u64>(b & 0x7F) << (7 * count);
			++count;
		} while (b & 0x80);

		return result;
	}

	INLINE void ReadBits(uptr outData, u32 bitLength)
	{
		u8* out = reinterpret_cast<u8*>(outData);
		int bitsLeft = bitLength;

		// align output to dword boundary
		while (((uptr)out & 3) != 0 && bitsLeft >= 8)
		{
			*out = (unsigned char)ReadUBitLong(8);
			++out;
			bitsLeft -= 8;
		}

		// read dwords
		while (bitsLeft >= 32)
		{
			*((u32*)out) = ReadUBitLong(32);
			out += sizeof(u32);
			bitsLeft -= 32;
		}

		// read remaining bytes
		while (bitsLeft >= 8)
		{
			*out = ReadUBitLong(8);
			++out;
			bitsLeft -= 8;
		}

		// read remaining bits
		if (bitsLeft)
			*out = ReadUBitLong(bitsLeft);
	}

	INLINE bool ReadBytes(uptr outData, u32 byteLength)
	{
		ReadBits(outData, byteLength << 3);
		return !IsOverflowed();
	}

	INLINE bool ReadString(char* str, i32 maxLength, bool stopAtLineTermination = false, i32* outNumChars = 0)
	{
		bool tooSmall = false;
		int iChar = 0;

		while (1)
		{
			char val = ReadChar();

			if (val == 0)
				break;
			else if (stopAtLineTermination && val == '\n')
				break;

			if (iChar < (maxLength - 1))
			{
				str[iChar] = val;
				++iChar;
			}
			else
			{
				tooSmall = true;
			}
		}

		// Make sure it's null-terminated.
		// Assert(iChar < maxLength);
		str[iChar] = 0;

		if (outNumChars)
			*outNumChars = iChar;

		return !IsOverflowed() && !tooSmall;
	}

	INLINE char* ReadAndAllocateString(bool* hasOverflowed = 0)
	{
		char str[2048];

		int chars = 0;
		bool overflowed = !ReadString(str, sizeof(str), false, &chars);

		if (hasOverflowed)
			*hasOverflowed = overflowed;

		// Now copy into the output and return it;
		char* ret = new char[chars + 1];
		for (u32 i = 0; i <= chars; i++)
			ret[i] = str[i];

		return ret;
	}

	INLINE i64 ReadLongLong()
	{
		i64 retval;
		u32* longs = (u32*)&retval;

		// Read the two DWORDs according to network endian
		const short endianIndex = 0x0100;
		u8* idx = (u8*)&endianIndex;

		longs[*idx++] = ReadUBitLong(sizeof(i32) << 3);
		longs[*idx] = ReadUBitLong(sizeof(i32) << 3);

		return retval;
	}

	INLINE bool Seek(size_t startPos)
	{
		bool bSucc = true;

		if (startPos < 0 || startPos > m_DataBits)
		{
			SetOverflowed();
			bSucc = false;
			startPos = m_DataBits;
		}

		// non-multiple-of-4 bytes at head of buffer. We put the "round off"
		// at the head to make reading and detecting the end efficient.
		int nHead = m_DataBytes & 3;

		int posBytes = startPos / 8;
		if ((m_DataBytes < 4) || (nHead && (posBytes < nHead)))
		{
			// partial first dword
			u8 const* partial = (u8 const*)m_Data;

			if (m_Data)
			{
				m_CachedBufWord = *(partial++);
				if (nHead > 1)
					m_CachedBufWord |= (*partial++) << 8;
				if (nHead > 2)
					m_CachedBufWord |= (*partial++) << 16;
			}

			m_DataIn = (u32 const*)partial;

			m_CachedBufWord >>= (startPos & 31);
			m_CachedBitsLeft = (nHead << 3) - (startPos & 31);
		}
		else
		{
			int adjustedPos = startPos - (nHead << 3);

			m_DataIn = reinterpret_cast<u32 const*>(reinterpret_cast<u8 const*>(m_Data) + ((adjustedPos / 32) << 2) + nHead);

			if (m_Data)
			{
				m_CachedBitsLeft = 32;
				GrabNextDWord();
			}
			else
			{
				m_CachedBufWord = 0;
				m_CachedBitsLeft = 1;
			}

			m_CachedBufWord >>= (adjustedPos & 31);
			m_CachedBitsLeft = std::min(m_CachedBitsLeft, u32(32 - (adjustedPos & 31))); // in case grabnextdword overflowed
		}

		return bSucc;
	}

	INLINE size_t GetNumBitsRead()
	{
		if (!m_Data)
			return 0;

		size_t nCurOfs = size_t(((iptr(m_DataIn) - iptr(m_Data)) / 4) - 1);
		nCurOfs *= 32;
		nCurOfs += (32 - m_CachedBitsLeft);

		size_t nAdjust = 8 * (m_DataBytes & 3);
		return std::min(nCurOfs + nAdjust, m_DataBits);
	}

	INLINE bool SeekRelative(size_t offset)
	{
		return Seek(GetNumBitsRead() + offset);
	}

	INLINE size_t TotalBytesAvailable()
	{
		return m_DataBytes;
	}

	INLINE size_t GetNumBitsLeft()
	{
		return m_DataBits - GetNumBitsRead();
	}
	INLINE size_t GetNumBytesLeft()
	{
		return GetNumBitsLeft() >> 3;
	}

  private:
	size_t m_DataBits; // 0x0010
	size_t m_DataBytes; // 0x0018

	u32 m_CachedBufWord; // 0x0020
	u32 m_CachedBitsLeft; // 0x0024

	const u32* m_DataIn; // 0x0028
	const u32* m_DataEnd; // 0x0030
	const u32* m_Data; // 0x0038
};

class BFWrite : public BitBufferBase
{
  public:
	BFWrite() = default;

	INLINE BFWrite(uptr data, size_t byteLength, const char* bufferName = 0)
	{
		StartWriting(data, byteLength);

		if (bufferName)
			SetName(bufferName);
	}

  public:
	INLINE void StartWriting(uptr data, size_t byteLength)
	{
		m_Data = reinterpret_cast<u32*>(data);
		m_DataOut = m_Data;

		m_DataBytes = byteLength;
		m_DataBits = byteLength << 3;

		m_DataEnd = reinterpret_cast<u32*>(reinterpret_cast<u8*>(m_Data) + m_DataBytes);
	}

	INLINE int GetNumBitsLeft()
	{
		return m_OutBitsLeft + (32 * (m_DataEnd - m_DataOut - 1));
	}

	INLINE void Reset()
	{
		m_Overflow = false;
		m_OutBufWord = 0;
		m_OutBitsLeft = 32;
		m_DataOut = m_Data;
	}

	INLINE void TempFlush()
	{
		if (m_OutBitsLeft != 32)
		{
			if (m_DataOut == m_DataEnd)
				SetOverflowed();
			else
				StoreLittleDWord(m_DataOut, 0, LoadLittleDWord(m_DataOut, 0) & ~s_nMaskTable[32 - m_OutBitsLeft] | m_OutBufWord);
		}

		m_Flushed = true;
	}

	INLINE u8* GetBasePointer()
	{
		TempFlush();
		return reinterpret_cast<u8*>(m_Data);
	}

	INLINE u8* GetData()
	{
		return GetBasePointer();
	}

	INLINE void Finish()
	{
		if (m_OutBitsLeft != 32)
		{
			if (m_DataOut == m_DataEnd)
				SetOverflowed();

			StoreLittleDWord(m_DataOut, 0, m_OutBufWord);
		}
	}

	INLINE void FlushNoCheck()
	{
		StoreLittleDWord(m_DataOut++, 0, m_OutBufWord);

		m_OutBitsLeft = 32;
		m_OutBufWord = 0;
	}

	INLINE void Flush()
	{
		if (m_DataOut == m_DataEnd)
			SetOverflowed();
		else
			StoreLittleDWord(m_DataOut++, 0, m_OutBufWord);

		m_OutBitsLeft = 32;
		m_OutBufWord = 0;
	}

	INLINE void WriteOneBitNoCheck(i32 value)
	{
		m_OutBufWord |= (value & 1) << (32 - m_OutBitsLeft);

		if (--m_OutBitsLeft == 0)
			FlushNoCheck();
	}

	INLINE void WriteOneBit(i32 value)
	{
		m_OutBufWord |= (value & 1) << (32 - m_OutBitsLeft);

		if (--m_OutBitsLeft == 0)
			Flush();
	}

	INLINE void WriteUBitLong(u32 data, i32 numBits, bool checkRange = true)
	{
		if (numBits <= m_OutBitsLeft)
		{
			if (checkRange)
				m_OutBufWord |= (data) << (32 - m_OutBitsLeft);
			else
				m_OutBufWord |= (data & s_nMaskTable[numBits]) << (32 - m_OutBitsLeft);

			m_OutBitsLeft -= numBits;

			if (m_OutBitsLeft == 0)
				Flush();
		}
		else
		{
			// split dwords case
			i32 overflowBits = (numBits - m_OutBitsLeft);
			m_OutBufWord |= (data & s_nMaskTable[m_OutBitsLeft]) << (32 - m_OutBitsLeft);
			Flush();
			m_OutBufWord = (data >> (numBits - overflowBits));
			m_OutBitsLeft = 32 - overflowBits;
		}
	}

	INLINE void WriteSBitLong(i32 data, i32 numBits)
	{
		WriteUBitLong((u32)data, numBits, false);
	}

	INLINE void WriteUBitVar(u32 n)
	{
		if (n < 16)
			WriteUBitLong(n, 6);
		else if (n < 256)
			WriteUBitLong((n & 15) | 16 | ((n & (128 | 64 | 32 | 16)) << 2), 10);
		else if (n < 4096)
			WriteUBitLong((n & 15) | 32 | ((n & (2048 | 1024 | 512 | 256 | 128 | 64 | 32 | 16)) << 2), 14);
		else
		{
			WriteUBitLong((n & 15) | 48, 6);
			WriteUBitLong((n >> 4), 32 - 4);
		}
	}

	INLINE void WriteBitFloat(float value)
	{
		auto temp = &value;
		WriteUBitLong(*reinterpret_cast<u32*>(temp), 32);
	}

	INLINE void WriteFloat(float value)
	{
		auto temp = &value;
		WriteUBitLong(*reinterpret_cast<u32*>(temp), 32);
	}

	INLINE bool WriteBits(const uptr data, i32 numBits)
	{
		u8* out = (u8*)data;
		i32 numBitsLeft = numBits;

		// Bounds checking..
		if ((GetNumBitsWritten() + numBits) > m_DataBits)
		{
			SetOverflowed();
			return false;
		}

		// !! speed!! need fast paths
		// write remaining bytes
		while (numBitsLeft >= 8)
		{
			WriteUBitLong(*out, 8, false);
			++out;
			numBitsLeft -= 8;
		}

		// write remaining bits
		if (numBitsLeft)
			WriteUBitLong(*out, numBitsLeft, false);

		return !IsOverflowed();
	}

	INLINE bool WriteBytes(const uptr data, i32 numBytes)
	{
		return WriteBits(data, numBytes << 3);
	}

	INLINE i32 GetNumBitsWritten()
	{
		return (32 - m_OutBitsLeft) + (32 * (m_DataOut - m_Data));
	}

	INLINE i32 GetNumBytesWritten()
	{
		return (GetNumBitsWritten() + 7) >> 3;
	}

	INLINE void WriteChar(i32 val)
	{
		WriteSBitLong(val, sizeof(char) << 3);
	}

	INLINE void WriteByte(i32 val)
	{
		WriteUBitLong(val, sizeof(unsigned char) << 3, false);
	}

	INLINE void WriteShort(i32 val)
	{
		WriteSBitLong(val, sizeof(short) << 3);
	}

	INLINE void WriteWord(i32 val)
	{
		WriteUBitLong(val, sizeof(unsigned short) << 3);
	}

	INLINE bool WriteString(const char* str)
	{
		if (str)
			while (*str)
				WriteChar(*(str++));

		WriteChar(0);

		return !IsOverflowed();
	}

	INLINE void WriteLongLong(i64 val)
	{
		u32* pLongs = (u32*)&val;

		// Insert the two DWORDS according to network endian
		const short endianIndex = 0x0100;
		u8* idx = (u8*)&endianIndex;

		WriteUBitLong(pLongs[*idx++], sizeof(i32) << 3);
		WriteUBitLong(pLongs[*idx], sizeof(i32) << 3);
	}

	/*INLINE void WriteBitCoord(const float f) {
		i32 signbit = (f <= -COORD_RESOLUTION);
		i32 intval = (i32)abs(f);
		i32 fractval = abs((i32)(f * COORD_DENOMINATOR)) & (COORD_DENOMINATOR - 1);

		// Send the bit flags that indicate whether we have an integer part and/or a fraction part.
		WriteOneBit(intval);
		WriteOneBit(fractval);

		if (intval || fractval) {
			// Send the sign bit
			WriteOneBit(signbit);

			// Send the integer if we have one.
			if (intval) {
				// Adjust the integers from [1..MAX_COORD_VALUE] to [0..MAX_COORD_VALUE-1]
				intval--;
				WriteUBitLong((u32)intval, COORD_INTEGER_BITS);
			}

			// Send the fraction if we have one
			if (fractval) {
				WriteUBitLong((u32)fractval, COORD_FRACTIONAL_BITS);
			}
		}
	}

	INLINE void WriteBitCoordMP(const float f) {
		i32 signbit = (f <= -COORD_RESOLUTION);
		i32 intval = (i32)abs(f);
		i32 fractval = (abs((i32)(f * COORD_DENOMINATOR)) & (COORD_DENOMINATOR - 1));

		bool bInBounds = intval < (1 << COORD_INTEGER_BITS_MP);

		WriteOneBit(bInBounds);

		// Send the sign bit
		WriteOneBit(intval);

		if (intval) {
			WriteOneBit(signbit);

			// Send the integer if we have one.
			// Adjust the integers from [1..MAX_COORD_VALUE] to [0..MAX_COORD_VALUE-1]
			intval--;

			if (bInBounds)
				WriteUBitLong((u32)intval, COORD_INTEGER_BITS_MP);
			else
				WriteUBitLong((u32)intval, COORD_INTEGER_BITS);
		}
	}

	INLINE void WriteBitCellCoord(const float f, int bits, EBitCoordType coordType) {
		bool bIntegral = (coordType == kCW_Integral);
		bool bLowPrecision = (coordType == kCW_LowPrecision);

		i32 intval = (i32)abs(f);
		i32 fractval = bLowPrecision ? (abs((i32)(f * COORD_DENOMINATOR_LOWPRECISION)) & (COORD_DENOMINATOR_LOWPRECISION - 1)) :
	(abs((i32)(f * COORD_DENOMINATOR)) & (COORD_DENOMINATOR - 1));

		if (bIntegral)
			WriteUBitLong((u32)intval, bits);
		else {
			WriteUBitLong((u32)intval, bits);
			WriteUBitLong((u32)fractval, bLowPrecision ? COORD_FRACTIONAL_BITS_MP_LOWPRECISION : COORD_FRACTIONAL_BITS);
		}
	}*/

	INLINE void SeekToBit(int bit)
	{
		TempFlush();

		m_DataOut = m_Data + (bit / 32);
		m_OutBufWord = LoadLittleDWord(m_DataOut, 0);
		m_OutBitsLeft = 32 - (bit & 31);
	}

	/*INLINE void WriteBitVec3Coord(const Vector& fa) {
		i32 xflag, yflag, zflag;

		xflag = (fa[0] >= COORD_RESOLUTION) || (fa[0] <= -COORD_RESOLUTION);
		yflag = (fa[1] >= COORD_RESOLUTION) || (fa[1] <= -COORD_RESOLUTION);
		zflag = (fa[2] >= COORD_RESOLUTION) || (fa[2] <= -COORD_RESOLUTION);

		WriteOneBit(xflag);
		WriteOneBit(yflag);
		WriteOneBit(zflag);

		if (xflag)
			WriteBitCoord(fa[0]);
		if (yflag)
			WriteBitCoord(fa[1]);
		if (zflag)
			WriteBitCoord(fa[2]);
	}

	INLINE void WriteBitNormal(float f) {
		i32 signbit = (f <= -NORMAL_RESOLUTION);

		// NOTE: Since +/-1 are valid values for a normal, I'm going to encode that as all ones
		u32 fractval = abs((i32)(f * NORMAL_DENOMINATOR));

		// clamp..
		if (fractval > NORMAL_DENOMINATOR)
			fractval = NORMAL_DENOMINATOR;

		// Send the sign bit
		WriteOneBit(signbit);

		// Send the fractional component
		WriteUBitLong(fractval, NORMAL_FRACTIONAL_BITS);
	}

	INLINE void WriteBitVec3Normal(const Vector& fa) {
		i32 xflag, yflag;

		xflag = (fa[0] >= NORMAL_RESOLUTION) || (fa[0] <= -NORMAL_RESOLUTION);
		yflag = (fa[1] >= NORMAL_RESOLUTION) || (fa[1] <= -NORMAL_RESOLUTION);

		WriteOneBit(xflag);
		WriteOneBit(yflag);

		if (xflag)
			WriteBitNormal(fa[0]);
		if (yflag)
			WriteBitNormal(fa[1]);

		// Write z sign bit
		i32 signbit = (fa[2] <= -NORMAL_RESOLUTION);
		WriteOneBit(signbit);
	}*/

	INLINE void WriteBitAngle(float angle, int numBits)
	{
		u32 shift = GetBitForBitnum(numBits);
		u32 mask = shift - 1;

		i32 d = (i32)((angle / 360.0) * shift);
		d &= mask;

		WriteUBitLong((u32)d, numBits);
	}

	INLINE bool WriteBitsFromBuffer(BFRead* in, int numBits)
	{
		while (numBits > 32)
		{
			WriteUBitLong(in->ReadUBitLong(32), 32);
			numBits -= 32;
		}

		WriteUBitLong(in->ReadUBitLong(numBits), numBits);
		return !IsOverflowed() && !in->IsOverflowed();
	}

	/*INLINE void WriteBitAngles(const QAngle& fa) {
		// FIXME:
		Vector tmp(fa.x, fa.y, fa.z);
		WriteBitVec3Coord(tmp);
	}*/

  private:
	size_t m_DataBits = 0;
	size_t m_DataBytes = 0;

	u32 m_OutBufWord = 0;
	u32 m_OutBitsLeft = 32;

	u32* m_DataOut = nullptr;
	u32* m_DataEnd = nullptr;
	u32* m_Data = nullptr;

	bool m_Flushed = false; // :flushed:
};

#undef INLINE
