//========= Copyright © 1996-2003, Valve LLC, All rights reserved. ============
//
// Purpose: 
//
//=============================================================================

#ifndef NETWORKVAR_H
#define NETWORKVAR_H
#ifdef _WIN32
#pragma once
#endif


#include "tier0/dbg.h"
#include "convar.h"

#if defined( CLIENT_DLL ) || defined( GAME_DLL )
	#include "basehandle.h"
#endif


#pragma warning( disable : 4284 ) // warning C4284: return type for 'CNetworkVarT<int>::operator ->' is 'int *' (ie; not a UDT or reference to a UDT.  Will produce errors if applied using infix notation)


#define MyOffsetOf( type, var ) ( (int)&((type*)0)->var )


extern bool g_bUseNetworkVars;


inline int InternalCheckDeclareClass( const char *pClassName, const char *pClassNameMatch, void *pTestPtr, void *pBasePtr )
{
	// This makes sure that casting from ThisClass to BaseClass works right. You'll get a compiler error if it doesn't
	// work at all, and you'll get a runtime error if you use multiple inheritance.
	Assert( pTestPtr == pBasePtr );
	
	// This is triggered by IMPLEMENT_SERVER_CLASS. It does DLLClassName::CheckDeclareClass( #DLLClassName ).
	// If they didn't do a DECLARE_CLASS in DLLClassName, then it'll be calling its base class's version
	// and the class names won't match.
	Assert( (void*)pClassName == (void*)pClassNameMatch );
	return 0;
}


#ifdef _DEBUG

	#define DECLARE_CLASS( className, baseClassName ) \
		typedef baseClassName BaseClass; \
		typedef className ThisClass; \
		template <typename T> friend int CheckDeclareClass_Access(T *); \
		static int CheckDeclareClass( const char *pShouldBe ) \
		{ \
			InternalCheckDeclareClass( pShouldBe, #className, (ThisClass*)0xFFFFF, (BaseClass*)(ThisClass*)0xFFFFF ); \
			return BaseClass::CheckDeclareClass( #baseClassName ); \
		}

	// Use this macro when you have a base class, but it's part of a library that doesn't use network vars
	// or any of the things that use ThisClass or BaseClass.
	#define DECLARE_CLASS_GAMEROOT( className, baseClassName ) \
		typedef baseClassName BaseClass; \
		typedef className ThisClass; \
		template <typename T> friend int CheckDeclareClass_Access(T *); \
		static int CheckDeclareClass( const char *pShouldBe ) \
		{ \
			return InternalCheckDeclareClass( pShouldBe, #className, (ThisClass*)0xFFFFF, (BaseClass*)(ThisClass*)0xFFFFF ); \
		}

	// This is a yucky special-case macro we use when using DECLARE_CLASS from within a templated class.
	// If you make a friend of a global template function from inside a template class, it confuses
	// the compiler and thinks you're making an ambiguous call to the template function when you're not.
	// So to fix it in these rare cases, we use this macro.
	#define DECLARE_CLASS_NOFRIEND( className, baseClassName ) \
		typedef baseClassName BaseClass; \
		typedef className ThisClass; \
		static int CheckDeclareClass( const char *pShouldBe ) \
		{ \
			return 1; \
		}

	#define DECLARE_CLASS_NOBASE( className ) \
		typedef className ThisClass; \
		template <typename T> friend int CheckDeclareClass_Access(T *); \
		static int CheckDeclareClass( const char *pShouldBe ) \
		{ \
			return InternalCheckDeclareClass( pShouldBe, #className, 0, 0 ); \
		}

#else

	#define DECLARE_CLASS( className, baseClassName ) \
		typedef baseClassName BaseClass; \
		typedef className ThisClass;

	#define DECLARE_CLASS_GAMEROOT( className, baseClassName )	DECLARE_CLASS( className, baseClassName )
	#define DECLARE_CLASS_NOFRIEND( className, baseClassName )	DECLARE_CLASS( className, baseClassName )
	#define DECLARE_CLASS_NOBASE( className )					typedef className ThisClass;

#endif




// All classes that contain CNetworkVars need a NetworkStateChanged() function. If the class is not an entity,
// it needs to forward the call to the entity it's in. These macros can help.
	
	// These macros setup an entity pointer in your class. Use IMPLEMENT_NETWORKVAR_CHAIN before you do
	// anything inside the class itself.
	class CBaseEntity;
	class CAutoInitEntPtr
	{
	public:
		CAutoInitEntPtr()
		{
			m_pEnt = NULL;
		}
		CBaseEntity *m_pEnt;
	};

	#define DECLARE_NETWORKVAR_CHAIN() \
		CAutoInitEntPtr __m_pChainEntity; \
		void NetworkStateChanged() { if ( g_bUseNetworkVars ) __m_pChainEntity.m_pEnt->NetworkStateChanged(); }

	#define IMPLEMENT_NETWORKVAR_CHAIN( varName ) \
		(varName)->__m_pChainEntity.m_pEnt = this;



// Use this macro when you want to embed a structure inside your entity and have CNetworkVars in it.
template< class T >
static inline void DispatchNetworkStateChanged( T *pObj )
{
	if ( g_bUseNetworkVars )
		pObj->NetworkStateChanged();
}

#define DECLARE_EMBEDDED_NETWORKVAR() \
	template <typename T> friend int ServerClassInit(T *);	\
	template <typename T> friend int ClientClassInit(T *); \
	virtual void NetworkStateChanged() {}

#define CNetworkVarEmbedded( type, name ) \
	class NetworkVar_##name; \
	friend class NetworkVar_##name; \
	static inline int GetOffset_##name() { return MyOffsetOf(ThisClass,name); } \
	typedef ThisClass ThisClass_##name; \
	class NetworkVar_##name : public type \
	{ \
	public: \
		template< class T > NetworkVar_##name& operator=( const T &val ) { *((type*)this) = val; return *this; } \
		virtual void NetworkStateChanged() \
		{ \
			DispatchNetworkStateChanged( (ThisClass_##name*)( ((char*)this) - GetOffset_##name() ) ); \
		} \
	}; \
	NetworkVar_##name name; 



template< class Type, class Changer >
class CNetworkVarBase
{
public:
	const Type& operator=( const Type &val ) 
	{ 
		return Set( val ); 
	}
	
	const Type& operator=( const CNetworkVarBase<Type,Changer> &val ) 
	{ 
		return Set( val.m_Value ); 
	}
	
	const Type& Set( const Type &val )
	{
		if ( m_Value != val )
		{
			NetworkStateChanged();
			m_Value = val;
		}
		return m_Value;
	}
	
	Type& GetForModify()
	{
		NetworkStateChanged();
		return m_Value;
	}

	const Type& operator+=( const Type &val ) 
	{
		return Set( m_Value + val ); 
	}

	const Type& operator-=( const Type &val ) 
	{
		return Set( m_Value - val ); 
	}
	
	const Type& operator/=( const Type &val ) 
	{
		return Set( m_Value / val ); 
	}
	
	const Type& operator*=( const Type &val ) 
	{
		return Set( m_Value * val ); 
	}
	
	const Type& operator^=( const Type &val ) 
	{
		return Set( m_Value ^ val ); 
	}

	const Type& operator|=( const Type &val ) 
	{
		return Set( m_Value | val ); 
	}

	const Type& operator++()
	{
		return (*this += 1);
	}

	Type operator--()
	{
		return (*this -= 1);
	}
	
	Type operator++( int ) // postfix version..
	{
		Type val = m_Value;
		(*this += 1);
		return val;
	}

	Type operator--( int ) // postfix version..
	{
		Type val = m_Value;
		(*this -= 1);
		return val;
	}
	
	// For some reason the compiler only generates type conversion warnings for this operator when used like 
	// CNetworkVarBase<unsigned char> = 0x1
	// (it warns about converting from an int to an unsigned char).
	template< class C >
	const Type& operator&=( C val ) 
	{	
		return Set( m_Value & val ); 
	}

	operator const Type() const 
	{
		return m_Value; 
	}
	
	const Type& Get() const 
	{
		return m_Value; 
	}
	
	const Type* operator->() const 
	{
		return &m_Value; 
	}

	Type m_Value;

protected:
	void NetworkStateChanged()
	{
		Changer::NetworkStateChanged( this );
	}
};


template< class Type, class Changer >
class CNetworkColor32Base : public CNetworkVarBase< Type, Changer >
{
public:
	inline void Init( byte rVal, byte gVal, byte bVal )
	{
		SetR( rVal );
		SetG( gVal );
		SetB( bVal );
	}
	inline void Init( byte rVal, byte gVal, byte bVal, byte aVal )
	{
		SetR( rVal );
		SetG( gVal );
		SetB( bVal );
		SetA( aVal );
	}

	const Type& operator=( const Type &val ) 
	{ 
		return Set( val ); 
	}

	const Type& operator=( const CNetworkColor32Base<Type,Changer> &val ) 
	{ 
		return CNetworkVarBase<Type,Changer>::Set( val.m_Value );
	}
	
	inline byte GetR() const { return m_Value.r; }
	inline byte GetG() const { return m_Value.g; }
	inline byte GetB() const { return m_Value.b; }
	inline byte GetA() const { return m_Value.a; }
	inline void SetR( byte val ) { SetVal( m_Value.r, val ); }
	inline void SetG( byte val ) { SetVal( m_Value.g, val ); }
	inline void SetB( byte val ) { SetVal( m_Value.b, val ); }
	inline void SetA( byte val ) { SetVal( m_Value.a, val ); }

protected:
	inline void SetVal( byte &out, const byte &in )
	{
		if ( out != in )
		{
			NetworkStateChanged();
			out = in;
		}
	}
};


// Network vector wrapper.
template< class Type, class Changer >
class CNetworkVectorBase : public CNetworkVarBase< Type, Changer >
{
public:
	inline void Init( float ix=0, float iy=0, float iz=0 ) 
	{
		SetX( ix );
		SetY( iy );
		SetZ( iz );
	}
	
	const Type& operator=( const Type &val ) 
	{ 
		return CNetworkVarBase< Type, Changer >::Set( val ); 
	}

	const Type& operator=( const CNetworkVectorBase<Type,Changer> &val ) 
	{ 
		return CNetworkVarBase<Type,Changer>::Set( val.m_Value );
	}

	inline float GetX() const { return m_Value.x; }
	inline float GetY() const { return m_Value.y; }
	inline float GetZ() const { return m_Value.z; }
	inline float operator[]( int i ) const { return m_Value[i]; }

	inline void SetX( float val ) { DetectChange( m_Value.x, val ); }
	inline void SetY( float val ) { DetectChange( m_Value.y, val ); }
	inline void SetZ( float val ) { DetectChange( m_Value.z, val ); }
	inline void Set( int i, float val ) { DetectChange( m_Value[i], val ); }

	bool operator==( const Type &val ) const 
	{ 
		return m_Value == (Type)val; 
	}

	bool operator!=( const Type &val ) const 
	{
		return m_Value != (Type)val; 
	}

	const Type operator+( const Type &val ) const 
	{
		return m_Value + val; 
	}

	const Type operator-( const Type &val ) const
	{ 
		return m_Value - val; 
	}

	const Type operator*( const Type &val ) const
	{
		return m_Value * val; 
	}

	const Type& operator*=( float val )
	{
		return CNetworkVarBase< Type, Changer >::Set( m_Value * val );
	}

	const Type operator*( float val ) const
	{
		return m_Value * val; 
	}

	const Type operator/( const Type &val ) const
	{
		return m_Value / val; 
	}

private:
	inline void DetectChange( float &out, float in ) 
	{
		if ( out != in ) 
		{
			NetworkStateChanged();
			out = in;
		}
	}
};



// Network ehandle wrapper.
#if defined( CLIENT_DLL ) || defined( GAME_DLL )
	template< class Type, class Changer >
	class CNetworkHandleBase : public CNetworkVarBase< CBaseHandle, Changer >
	{
	public:
		const Type* operator=( const Type *val ) 
		{ 
			return Set( val ); 
		}
			
		const Type& operator=( const CNetworkHandleBase<Type,Changer> &val ) 
		{ 
			const CBaseHandle &handle = CNetworkVarBase<CBaseHandle,Changer>::Set( val.m_Value );
			return *(const Type*)handle.Get();
		}

		bool operator !() const 
		{ 
			return !m_Value.Get(); 
		}
		
		operator Type*() const 
		{ 
			return static_cast< Type* >( m_Value.Get() );
		}

		const Type* Set( const Type *val )
		{
			if ( m_Value != val )
			{
				NetworkStateChanged();
				m_Value = val;
			}
			return val;
		}
		
		Type* Get() const 
		{ 
			return static_cast< Type* >( m_Value.Get() );
		}

		Type* operator->() const 
		{ 
			return static_cast< Type* >( m_Value.Get() );
		}

		bool operator==( const Type *val ) const 
		{
			return m_Value == val; 
		}

		bool operator!=( const Type *val ) const 
		{
			return m_Value != val;
		}
	};



	#define CNetworkHandle( type, name ) CNetworkHandleInternal( type, name, NetworkStateChanged )

	#define CNetworkHandleInternal( type, name, stateChangedFn ) \
		NETWORK_VAR_START( type, name ) \
		NETWORK_VAR_END( type, name, CNetworkHandleBase, stateChangedFn )
#endif


// Use this macro to define a network variable.
#define CNetworkVar( type, name ) \
	NETWORK_VAR_START( type, name ) \
	NETWORK_VAR_END( type, name, CNetworkVarBase, NetworkStateChanged )


// Use this macro when you have a base class with a variable, and it doesn't have that variable in a SendTable,
// but a derived class does. Then, the entity is only flagged as changed when the variable is changed in
// an entity that wants to transmit the variable.
	#define CNetworkVarForDerived( type, name ) \
		virtual void NetworkStateChanged_##name() {} \
		NETWORK_VAR_START( type, name ) \
		NETWORK_VAR_END( type, name, CNetworkVarBase, NetworkStateChanged_##name )

	#define CNetworkVectorForDerived( name ) \
		virtual void NetworkStateChanged_##name() {} \
		CNetworkVectorInternal( Vector, name, NetworkStateChanged_##name )
		
	#define CNetworkHandleForDerived( type, name ) \
		virtual void NetworkStateChanged_##name() {} \
		CNetworkHandleInternal( type, name, NetworkStateChanged_##name )
		
	#define CNetworkArrayForDerived( type, name, count ) \
		virtual void NetworkStateChanged_##name() {} \
		CNetworkArrayInternal( type, name, count, NetworkStateChanged_##name )
		
	#define IMPLEMENT_NETWORK_VAR_FOR_DERIVED( name ) \
		virtual void NetworkStateChanged_##name() { if ( g_bUseNetworkVars ) NetworkStateChanged(); }


// Vectors + some convenient helper functions.
#define CNetworkVector( name ) CNetworkVectorInternal( Vector, name, NetworkStateChanged )
#define CNetworkQAngle( name ) CNetworkVectorInternal( QAngle, name, NetworkStateChanged )

#define CNetworkVectorInternal( type, name, stateChangedFn ) \
	NETWORK_VAR_START( type, name ) \
	NETWORK_VAR_END( type, name, CNetworkVectorBase, stateChangedFn )


// Helper for color32's. Contains GetR(), SetR(), etc.. functions.
#define CNetworkColor32( name ) \
	NETWORK_VAR_START( color32, name ) \
	NETWORK_VAR_END( color32, name, CNetworkColor32Base, NetworkStateChanged )


#define CNetworkString( name, length ) \
	class NetworkVar_##name; \
	friend class NetworkVar_##name; \
	typedef ThisClass MakeANetworkVar_##name; \
	class NetworkVar_##name \
	{ \
	public: \
		operator const char*() const { return m_Value; } \
		const char* Get() const { return m_Value; } \
		char* GetForModify() \
		{ \
			NetworkStateChanged(); \
			return m_Value; \
		} \
	protected: \
		void NetworkStateChanged() \
		{ \
			if ( g_bUseNetworkVars ) ((ThisClass*)(((char*)this) - MyOffsetOf(ThisClass,name)))->NetworkStateChanged(); \
		} \
	private: \
		char m_Value[length]; \
	}; \
	NetworkVar_##name name;




// Use this to define networked arrays.
// You can access elements for reading with operator[], and you can set elements with the Set() function.
#define CNetworkArrayInternal( type, name, count, stateChangedFn ) \
	class NetworkVar_##name; \
	friend class NetworkVar_##name; \
	typedef ThisClass MakeANetworkVar_##name; \
	class NetworkVar_##name \
	{ \
	public: \
		template <typename T> friend int ServerClassInit(T *);	\
		const type& operator[]( int i ) const { return Get( i ); } \
		const type& Get( int i ) const { Assert( i >= 0 && i < count ); return m_Value[i]; } \
		type& GetForModify( int i ) { NetworkStateChanged(); Assert( i >= 0 && i < count ); return m_Value[i]; } \
		void Set( int i, const type &val ) { Assert( i >= 0 && i < count ); m_Value[i] = val; } \
		const type* Base() const { return m_Value; } \
		int Count() const { return count; } \
	protected: \
		void NetworkStateChanged() \
		{ \
			if ( g_bUseNetworkVars ) ((ThisClass*)(((char*)this) - MyOffsetOf(ThisClass,name)))->stateChangedFn(); \
		} \
		type m_Value[count]; \
	}; \
	NetworkVar_##name name;


#define CNetworkArray( type, name, count )  CNetworkArrayInternal( type, name, count, NetworkStateChanged )


// Internal macros used in definitions of network vars.
#define NETWORK_VAR_START( type, name ) \
	class NetworkVar_##name; \
	friend class NetworkVar_##name; \
	typedef ThisClass MakeANetworkVar_##name; \
	class NetworkVar_##name \
	{ \
	public: \
		template <typename T> friend int ServerClassInit(T *);


#define NETWORK_VAR_END( type, name, base, stateChangedFn ) \
		static void NetworkStateChanged( void *ptr ) \
		{ \
			if ( g_bUseNetworkVars ) ((ThisClass*)(((char*)ptr) - MyOffsetOf(ThisClass,name)))->stateChangedFn(); \
		} \
	}; \
	base< type, NetworkVar_##name > name;



#endif // NETWORKVAR_H
