//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Defines a common class for all objects in the world object tree.
//
//			Pointers to other objects in the object tree may be stored, but
//			they should be set via UpdateDependency rather than directly. This
//			insures proper linkage to the other object so that if it moves, is
//			removed from the world, or changes in any other way, the dependent
//			object is properly notified.
//
//=============================================================================//

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

#include "tier0/basetypes.h"

#pragma warning(push, 1)
#pragma warning(disable:4701 4702 4530)
#include <fstream>
#pragma warning(pop)
#include "BoundBox.h"
#include "MapPoint.h"
#include "utlvector.h"
#include "visgroup.h"
#include "fgdlib/wckeyvalues.h"
#include "tier1/smartptr.h"


class Box3D;
class CBaseTool;
class CChunkFile;
class GDclass;
class CMapClass;
class CMapEntity;
class CMapSolid;
class CMapView2D;
class CMapViewLogical;
class CMapWorld;
class CPoint;
class CRender3D;
class CSaveInfo;
class CSSolid;
class CVisGroupList;
class CMapFaceList;

struct MapError;

enum ChunkFileResult_t;

struct MapObjectPair_t
{
	CMapClass *pObject1;
	CMapClass *pObject2;
};

//-----------------------------------------------------------------------------
// Structure used for returning hits when calling ObjectsAt.
//-----------------------------------------------------------------------------
typedef struct HitInfo_s
{
	CMapClass *pObject;		// Pointer to the CMapAtom that was clicked on.
	unsigned int uData;		// Additional data provided by the CMapAtom object.
	unsigned int nDepth;	// Depth value of the object that was clicked on.
	VMatrix m_LocalMatrix;
} HitInfo_t;


//
// Passed into PrepareSelection to control what gets selected.
//
enum SelectMode_t
{
	selectGroups = 0,	// select groups, ungrouped entities, and ungrouped solids
	selectObjects,		// select entities and solids not in entities
	selectSolids,		// select point entities, solids in entities, solids
};

enum VisGroupSelection
{
	AUTO = 0,
	USER
};

// helper macro for linked lists as pointers
#define FOR_EACH_OBJ( listName, iteratorName ) \
	for( int iteratorName=0; iteratorName<(listName).Count(); iteratorName++)



typedef const char * MAPCLASSTYPE;
typedef BOOL (*ENUMMAPCHILDRENPROC)(CMapClass *, unsigned int dwParam);
typedef CUtlVector<CMapClass*> CMapObjectList;


#define MAX_ENUM_CHILD_DEPTH	16


struct EnumChildrenStackEntry_t
{
	CMapClass *pParent;
	int pos;
};


struct EnumChildrenPos_t
{
	EnumChildrenStackEntry_t Stack[MAX_ENUM_CHILD_DEPTH];
	int nDepth;
};


typedef struct
{
	MAPCLASSTYPE Type;
	CMapClass * (*pfnNew)();
} MCMSTRUCT;


// This is a reference-counted class that holds a pointer to an object.
// When the object goes away, it can set the pointer in here to NULL
// and anyone else who holds a reference to this can know that the 
// object has gone away. It's similar to the EHANDLEs in the engine,
// except there's no finite list of objects that's managed anywhere.
template<class T>
class CSafeObject
{
public:
	static CSmartPtr< CSafeObject< T > > Create( T *pObject )
	{
		CSafeObject<T> *pRet = new CSafeObject<T>( pObject );
		return CSmartPtr< CSafeObject< T> >( pRet );
	}
	
	void AddRef()
	{
		++m_RefCount;
	}
	void Release()
	{
		--m_RefCount;
		if ( m_RefCount <= 0 )
			delete this;
	}
	int GetRefCount() const
	{
		return m_RefCount;
	}	

public:	
	T *m_pObject;

private:
	CSafeObject( T *pObject )
	{
		m_RefCount = 0;
		m_pObject = pObject;
	}
		
private:
	int m_RefCount;	// This object goes away when all smart pointers to it go away.
};


class CMapClass : public CMapPoint
{
public:
	//
	// Construction/destruction:
	//
	CMapClass(void);
	virtual ~CMapClass(void);
	
	const CSmartPtr< CSafeObject< CMapClass > >& GetSafeObjectSmartPtr();

	inline int GetID(void);
	inline void SetID(int nID);
	virtual size_t GetSize(void);

	//
	// Can belong to one or more visgroups:
	//
	virtual void AddVisGroup(CVisGroup *pVisGroup);
	int GetVisGroupCount(void);
	CVisGroup *GetVisGroup(int nIndex);
	void RemoveAllVisGroups(void);
	void RemoveVisGroup(CVisGroup *pVisGroup);
	int  IsInVisGroup(CVisGroup *pVisGroup);
	void SetColorVisGroup(CVisGroup *pVisGroup);
	virtual bool UpdateObjectColor();

	//
	// Can be tracked in the Undo/Redo system:
	//
	inline void SetTemporary(bool bTemporary) { m_bTemporary = bTemporary; }
	inline bool IsTemporary(void) const { return m_bTemporary; }
	union
	{
		struct
		{
			unsigned ID : 28;
			unsigned Types : 4;	// 0 - copy, 1 - relationship, 2 - delete
		} Kept;

		unsigned int dwKept;
	};

	//
	// Has children:
	//
	virtual void AddChild(CMapClass *pChild);
	virtual void CopyChildrenFrom(CMapClass *pobj, bool bUpdateDependencies);
	virtual void RemoveAllChildren(void);
	virtual void RemoveChild(CMapClass *pChild, bool bUpdateBounds = true);
	virtual void UpdateChild(CMapClass *pChild);

	inline int GetChildCount(void) { return( m_Children.Count()); }
	inline const CMapObjectList *GetChildren() { return &m_Children; }
		
	CMapClass *GetFirstDescendent(EnumChildrenPos_t &pos);
	CMapClass *GetNextDescendent(EnumChildrenPos_t &pos);

	virtual CMapClass *GetParent(void)
	{
		Assert( (m_pParent == NULL) || (dynamic_cast<CMapClass*>(m_pParent) != NULL) );
		return( (CMapClass*)m_pParent);
	}

	virtual void SetParent(CMapAtom *pParent)
	{
		Assert( (pParent == NULL) || (dynamic_cast<CMapClass*>(pParent) != NULL) );
		UpdateParent((CMapClass*)pParent);
	}

	const CMapObjectList *GetDependents() { return &m_Dependents; }

	virtual void FindTargetNames( CUtlVector< const char * > &Names ) { }
	virtual void ReplaceTargetname(const char *szOldName, const char *szNewName);

	//
	// Notifications.
	//
	virtual void OnAddToWorld(CMapWorld *pWorld);
	virtual void OnClone(CMapClass *pClone, CMapWorld *pWorld, const CMapObjectList &OriginalList, CMapObjectList &NewList);
	virtual void OnPreClone(CMapClass *pClone, CMapWorld *pWorld, const CMapObjectList &OriginalList, CMapObjectList &NewList);
	virtual void OnPrePaste(CMapClass *pCopy, CMapWorld *pSourceWorld, CMapWorld *pDestWorld, const CMapObjectList &OriginalList, CMapObjectList &NewList);
	virtual void OnPaste(CMapClass *pCopy, CMapWorld *pSourceWorld, CMapWorld *pDestWorld, const CMapObjectList &OriginalList, CMapObjectList &NewList);
	virtual void OnNotifyDependent(CMapClass *pObject, Notify_Dependent_t eNotifyType);
	virtual void OnParentKeyChanged(const char* key, const char* value) {}
	virtual void OnRemoveFromWorld(CMapWorld *pWorld, bool bNotifyChildren);
	virtual void OnUndoRedo(void) {}

	virtual bool OnApply( void ) { return true; }

	//
	// Bounds calculation and intersection functions.
	//
	virtual void CalcBounds(BOOL bFullUpdate = FALSE);
	
	void GetCullBox(Vector &mins, Vector &maxs);
	void SetCullBoxFromFaceList( CMapFaceList *pFaces );
	void GetBoundingBox( Vector &mins, Vector &maxs );
	void SetBoundingBoxFromFaceList( CMapFaceList *pFaces );
	
	void GetRender2DBox(Vector &mins, Vector &maxs);

	// NOTE: Logical position is in global space
	virtual void SetLogicalPosition( const Vector2D &vecPosition ) {}
	virtual const Vector2D& GetLogicalPosition( );

	// NOTE: Logical bounds is in global space
	virtual void GetRenderLogicalBox( Vector2D &mins, Vector2D &maxs );

	// HACK: temp stuff to ease the transition to not inheriting from BoundBox
	void GetBoundsCenter(Vector &vecCenter) { m_Render2DBox.GetBoundsCenter(vecCenter); }
	void GetBoundsSize(Vector &vecSize) { m_Render2DBox.GetBoundsSize(vecSize); }
	inline bool IsInsideBox(Vector const &Mins, Vector const &Maxs) const { return(m_Render2DBox.IsInsideBox(Mins, Maxs)); }
	inline bool IsIntersectingBox(const Vector &vecMins, const Vector& vecMaxs) const { return(m_Render2DBox.IsIntersectingBox(vecMins, vecMaxs)); }

	virtual CMapClass *PrepareSelection(SelectMode_t eSelectMode);

	void PostUpdate(Notify_Dependent_t eNotifyType);
	static void UpdateAllDependencies(CMapClass *pObject);

	void SetOrigin(Vector& origin);

	// hierarchy
	virtual void UpdateAnimation( float animTime ) {}
	virtual bool GetTransformMatrix( VMatrix& matrix );
		
	virtual MAPCLASSTYPE GetType(void) = 0;
	virtual BOOL IsMapClass(MAPCLASSTYPE Type) = 0;
	virtual bool IsWorld() { return false; }

	virtual CMapClass *Copy(bool bUpdateDependencies);
	virtual CMapClass *CopyFrom(CMapClass *pFrom, bool bUpdateDependencies);

	virtual bool HitTest2D(CMapView2D *pView, const Vector2D &point, HitInfo_t &HitData);
	virtual bool HitTestLogical(CMapViewLogical *pView, const Vector2D &point, HitInfo_t &HitData);

	// Objects that can be clicked on and activated as tools implement this and return a CBaseTool-derived object.
	virtual CBaseTool *GetToolObject(int nHitData, bool bAttachObject) { return NULL; }

	//
	// Can be serialized:
	//
	virtual ChunkFileResult_t SaveVMF(CChunkFile *pFile, CSaveInfo *pSaveInfo);
	virtual ChunkFileResult_t SaveEditorData(CChunkFile *pFile);

	virtual bool ShouldSerialize(void) { return true; }
	virtual int SerializeRMF(std::fstream &File, BOOL bRMF);
	virtual int SerializeMAP(std::fstream &File, BOOL bRMF);
	virtual void PostloadWorld(CMapWorld *pWorld);		
	virtual void PresaveWorld(void) {}
	bool PostloadVisGroups( bool bIsLoading );

	virtual bool IsGroup(void) { return false; }
	virtual bool IsScaleable(void) { return false; }
	virtual bool IsClutter(void) { return false; }			// Whether this object should be hidden when the user hides helpers.
	virtual bool IsCulledByCordon(const Vector &vecMins, const Vector &vecMaxs);	// Whether this object is hidden based on its own intersection with the cordon, independent of its parent's intersection.
	virtual bool IsEditable( void );
	virtual bool ShouldSnapToHalfGrid() { return false; }
	virtual bool IsSolid( ) { return false; }

	// searching
	virtual CMapEntity *FindChildByKeyValue( const char* key, const char* value, bool *bIsInInstance = NULL, VMatrix *InstanceMatrix = NULL );

	// HACK: get the world that this object is contained within.
	static CMapWorld *GetWorldObject(CMapAtom *pStart);
    
	virtual const char* GetDescription() { return ""; }

	BOOL EnumChildren(ENUMMAPCHILDRENPROC pfn, unsigned int dwParam = 0, MAPCLASSTYPE Type = NULL);
	BOOL EnumChildrenRecurseGroupsOnly(ENUMMAPCHILDRENPROC pfn, unsigned int dwParam, MAPCLASSTYPE Type = NULL);
	BOOL IsChildOf(CMapAtom *pObject);

	virtual bool ShouldAppearInLightingPreview(void)
	{
		return true; //false;
	}

	virtual bool ShouldAppearInRaytracedLightingPreview(void)
	{
		return false;
	}

	inline bool IsVisible(void) { return(m_bVisible); }
	void SetVisible(bool bVisible);

	inline bool IsVisGroupShown(void) { return m_bVisGroupShown && m_bVisGroupAutoShown; }
	void VisGroupShow(bool bShow, VisGroupSelection eVisGroup = USER);
	bool CheckVisibility(bool bIsLoading = false);

	//
	// Visible2D functions are used only for hiding solids being morphed. Remove?
	//
	bool IsVisible2D(void) { return m_bVisible2D; }
	void SetVisible2D(bool bVisible2D) { m_bVisible2D = bVisible2D; }

	// Is this class potentially visible in 2D visio view?
	virtual bool IsLogical(void) { return false; }

	// Is this class actually visible in 2D visio view?
	virtual bool IsVisibleLogical(void) { return false; }

	//
	// Overridden to set the render color of each of our children.
	//
	virtual void SetRenderColor(unsigned char red, unsigned char green, unsigned char blue);
	virtual void SetRenderColor(color32 rgbColor);

	//
	// Can be rendered:
	//
	virtual void Render2D(CRender2D *pRender);
	virtual void Render3D(CRender3D *pRender);
	virtual void RenderLogical( CRender2D *pRender ) {}
	virtual bool RenderPreload(CRender3D *pRender, bool bNewContext);
	inline int GetRenderFrame(void) { return(m_nRenderFrame); }
	inline void SetRenderFrame(int nRenderFrame) { m_nRenderFrame = nRenderFrame; }

	SelectionState_t SetSelectionState(SelectionState_t eSelectionState);

	//
	// Has a set of editor-specific properties that are loaded from the VMF file.
	// The keys are freed after being handled by the map post-load code.
	//
	int GetEditorKeyCount(void);
	const char *GetEditorKey(int nIndex);
	const char *GetEditorKeyValue(int nIndex);
	const char *GetEditorKeyValue(const char *szKey);
	void RemoveEditorKeys(void);
	void SetEditorKeyValue(const char *szKey, const char *szValue);

	virtual void InstanceMoved( void );

public:

	// Set to true while loading a VMF file so it can delay certain calls like UpdateBounds.
	// Drastically speeds up load times.
	static bool s_bLoadingVMF;

protected:

	//
	// Implements CMapAtom transformation interface:
	//
	virtual void DoTransform(const VMatrix &matrix);
		
	//
	// Serialization callbacks.
	//
	static ChunkFileResult_t LoadEditorCallback(CChunkFile *pFile, CMapClass *pObject);
	static ChunkFileResult_t LoadEditorKeyCallback(const char *szKey, const char *szValue, CMapClass *pObject);

	//
	// Has a list of objects that must be notified if it changes size or position.
	//
	void AddDependent(CMapClass *pDependent);
	void NotifyDependents(Notify_Dependent_t eNotifyType);
	void RemoveDependent(CMapClass *pDependent);
	virtual void UpdateDependencies(CMapWorld *pWorld, CMapClass *pObject) {};
	CMapClass *UpdateDependency(CMapClass *pOldAttached, CMapClass *pNewAttached);

	void UpdateParent(CMapClass *pNewParent);

	void SetBoxFromFaceList( CMapFaceList *pFaces, BoundBox &Box );

	CSmartPtr< CSafeObject< CMapClass > > m_pSafeObject;

	BoundBox m_CullBox;				// Our bounds for culling in the 3D views and intersecting with the cordon.
	BoundBox m_BoundingBox;			// Our bounds for brushes / entities themselves.  This size may be smaller than m_CullBox ( i.e. spheres are not included )
	BoundBox m_Render2DBox;			// Our bounds for rendering in the 2D views.

	CMapObjectList m_Children;		// Each object can have many children. Children usually transform with their parents, etc.
	CMapObjectList m_Dependents;	// Objects that this object should notify if it changes.

	int m_nID;						// This object's unique ID.
	bool m_bTemporary;				// Whether to track this object for Undo/Redo.
	int m_nRenderFrame;				// Frame counter used to avoid rendering the same object twice in a 3D frame.

	bool m_bVisible2D;				// Whether this object is visible in the 2D view. Currently only used for morphing.
	bool m_bVisible;				// Whether this object is currently visible in the 2D and 3D views based on ALL factors: visgroups, cordon, etc.

	bool m_bVisGroupShown;			// Whether this object is shown or hidden by user visgroups. Kept separate from m_bVisible so we can
	// reflect this state in the visgroups list independent of the cordon, hide entities state, etc.

	bool m_bVisGroupAutoShown;		// Whether this object is shown or hidden by auto visgroups.

	CVisGroupList	m_VisGroups;	// Visgroups to which this object belongs, EMPTY if none.
	CVisGroup *m_pColorVisGroup;	// The visgroup from which we get our color, NULL if none.

	WCKeyValuesT<WCKVBase_Vector> *m_pEditorKeys;		// Temporary storage for keys loaded from the "editor" chunk of the VMF file, freed after loading.
	
	friend class CTrackEntry;						// Friends with Undo/Redo system so that parentage can be changed.
	friend void FixHiddenObject(MapError *pError);	// So that the Check for Problems dialog can fix visgroups problems.
};


//-----------------------------------------------------------------------------
// Purpose: Returns this object's unique ID.
//-----------------------------------------------------------------------------
int CMapClass::GetID(void)
{
	return(m_nID);
}


//-----------------------------------------------------------------------------
// Purpose: Sets this object's unique ID.
//-----------------------------------------------------------------------------
void CMapClass::SetID(int nID)
{
	m_nID = nID;
}

class CMapClassManager
{
public:

	virtual ~CMapClassManager();
	CMapClassManager(MAPCLASSTYPE Type, CMapClass * (*pfnNew)());

	static CMapClass * CreateObject(MAPCLASSTYPE Type);
};


#define MAPCLASS_TYPE(class_name) \
	(class_name::__Type)


#define IMPLEMENT_MAPCLASS(class_name) \
	char * class_name::__Type = #class_name; \
	MAPCLASSTYPE class_name::GetType() { return __Type; }	\
	BOOL class_name::IsMapClass(MAPCLASSTYPE Type) \
		{ return (Type == __Type) ? TRUE : FALSE; } \
	CMapClass * class_name##_CreateObject() \
		{ return new class_name; } \
	CMapClassManager mcm_##class_name(class_name::__Type, \
		class_name##_CreateObject);


#define DECLARE_MAPCLASS(class_name,class_base) \
	typedef class_base BaseClass; \
	static char * __Type; \
	virtual MAPCLASSTYPE GetType(); \
	virtual BOOL IsMapClass(MAPCLASSTYPE Type);


class CCheckFaceInfo
{
public:

	CCheckFaceInfo() { iPoint = -1; }
	char szDescription[128];
	int iPoint;
};


#endif // MAPCLASS_H