///////////////////////////////////////////////////////////////////////////////
//
// File: AVP_Message_Compression.h
//
// Created by Martin G.Harris (5/2/2007)
// 
// Copyright (C) 2007 Rebellion, All Rights Reserved.
//
///////////////////////////////////////////////////////////////////////////////

#ifdef ASURA_USE_PRAGMA_ONCE
    #pragma once
#endif

#ifndef __AVP_MESSAGE_COMPRESSION_H__
#define __AVP_MESSAGE_COMPRESSION_H__

// Includes
#include "Core/Asura_BitPacker.h"
#include "Core/Asura_Maths.h"
#include "Network/Asura_Guid.h"


//////////////////////////
// AVP_Compressed_Angle //
//////////////////////////
// (AW - 10.08.2004) - Compress an angle into 16 bits. Gives an accuracy around 0.005 degrees.
// This is extremely accurate. Perhaps it should be reduced.
// MGH 6/2/2007: Copied from MiamiVice.
class AVP_Compressed_Angle
{
public:
	void FromAngle(float fAngle);
	void ToAngle(float &fAngle) const;
	static u_int GetNumberOfBits() {return 16;}
	u_short GetData() const {return m_usData;}
	void SetData(u_short usData) {m_usData = usData;}
private:
	u_short m_usData;
};

////////////////////////////////
// AVP_Compressed_Angle_Small //
////////////////////////////////
// MGH 18/2/2005: Here is a more compressed version.
// MGH 6/2/2007: Copied from MiamiVice.
class AVP_Compressed_Angle_Small
{
public:
	void FromAngle(float fAngle);
	void ToAngle(float &fAngle) const;
	static u_int GetNumberOfBits() {return 8;}

	u_short GetData() const { return m_usData; }
	void SetData(u_short usData) { m_usData = usData; }

	inline void Pack(Asura_BitPacker& xBitPacker) const
	{
		xBitPacker.Pack(m_usData, GetNumberOfBits());
	}
	inline void Unpack(Asura_BitUnpacker& xUnpacker)
	{
		m_usData = static_cast<u_short>(xUnpacker.Unpack(GetNumberOfBits()));
	}

	// Helper
	static u_short Compress(float fAngle) 
	{
		AVP_Compressed_Angle_Small xOrient;
		xOrient.FromAngle(fAngle);
		return xOrient.GetData();
	}

	// Helper
	static float Uncompress(u_int uData) 
	{
		float fOrient = 0.0f;
		AVP_Compressed_Angle_Small xOrient;
		xOrient.SetData(static_cast<u_short>(uData));
		xOrient.ToAngle(fOrient);
		return fOrient;
	}

private:
	u_short m_usData;
};


//////////////////////////
// AVP_Compressed_Float //
//////////////////////////
// Float in specified inclusive range [min, max], compressed to the specified number of bits.
// Represent min and max values exactly. Clamp the float to the range.
class AVP_Compressed_Float
{
public:
	AVP_Compressed_Float(float fMin, float fMax, u_int uNumBits)
		: m_uData(0), m_fMin(fMin), m_fMax(fMax), m_uNumBits(uNumBits)
	{}

	float GetMin() const { return m_fMin; }
	float GetMax() const { return m_fMax; }

	u_int GetNumberOfBits() const { return m_uNumBits; } // Max of 32 bits
	u_int GetNumBins() const { return (1<<GetNumberOfBits()); }

	inline void Pack(Asura_BitPacker& xBitPacker) const
	{
		xBitPacker.Pack(m_uData, GetNumberOfBits());
	}
	inline void Unpack(Asura_BitUnpacker& xUnpacker)
	{
		m_uData = xUnpacker.Unpack(GetNumberOfBits());
	}

	void FromFloat(float fValue, bool bAssertIfOutOfRange = true);
	void ToFloat(float& fValue) const;

private:
	u_int m_uData;

	float m_fMin;
	float m_fMax;
	u_int m_uNumBits;
};

#endif //__AVP_MESSAGE_COMPRESSION_H__
