////////////////////////////////////////////////////////////////////////////////
// BGBase.h
// Virtual class/memory tracer declaration and misc functions
// Developed by Bjoern Ganster since 2005
////////////////////////////////////////////////////////////////////////////////

#ifndef BGBase__H
#define BGBase__H

#ifdef _WIN32
#include <Windows.h>
// At least for Windows' WriteFile, restrictions apply
#define ReadWriteBufMaxSize 64 * 1024
// Fix Windows' misbehaviour regarding min/max ...
#undef max
#undef min
#pragma warning (disable: 4482)
#include <SDL.h>
#define COUT std::wcout
#define CONSTEXPR const
#ifdef UNICODE
const wchar_t OSPathSep = L'\\';
#define PathChar wchar_t
#else
const char OSPathSep = '\\';
#define PathChar char
#endif
#else
#include <unistd.h>
#include <SDL2/SDL.h>
#define BYTE uint8_t
#define COUT std::wcout
#define CONSTEXPR constexpr
const char OSPathSep = '/';
#define PathChar char
#endif

#include <GL/glew.h>

#include "libinstrumentation.h"

#define storeClassID

////////////////////////////////////////////////////////////////////////////////
// Codepages

#define CP_ASCII 437
#define CP_UTF16_LE 1200
#define CP_UTF16_BE 1201
#define CP_UTF32 12000

////////////////////////////////////////////////////////////////////////////////
// Use C++11 features if supported

#if __cplusplus >= 201103 || (defined(_MSC_VER) && _MSC_VER >= 1900)
#define NOEXCEPT noexcept
#define OVERRIDE override 
#define FINAL final
#define NO_EXCEPT noexcept
#define ENUM_CLASS enum class
#define DELETE_FUNC(X) X = delete;
#else
#define NOEXCEPT
#define OVERRIDE
#define FINAL
#define NO_EXCEPT
#define ENUM_CLASS enum
#define DELETE_FUNC(X)
#endif

////////////////////////////////////////////////////////////////////////////////
// Operating system defines

#ifdef __APPLE__
typedef unsigned int uint;
#endif

#ifdef _WIN32
	typedef int BGKey;
	#ifdef UNICODE
	#define CP_SYSTEM CP_UTF16_LE
	#define OSPathSepStr "\\"
	#define PathCharMod "%S"
	#else
	#define CP_SYSTEM 1252
	#define PathCharMod "%s"
	#endif
#else
	#define OSPathSepStr "/"
	typedef char TCHAR;
	typedef uint BGKey;
	typedef int64_t INT64;
	typedef u_int16_t WORD;
	typedef u_int32_t DWORD;
	#define PathCharMod "%s"
	#define CP_UTF7 65000
	#define CP_UTF8 65001
	#define CP_SYSTEM CP_UTF8
#endif

////////////////////////////////////////////////////////////////////////////////

enum MessageType {
	ProcessGuiMessages,
	Log
};

////////////////////////////////////////////////////////////////////////////////
// File IDs

// It should be easy to look up numbers here, so every identifier should be defined by a number
enum FileID {
	FILE_NO_BGBASE_CPP = 0,
	FILE_NO_BGCPSTRING_H = 1,
	FILE_NO_BGCPSTRING_CPP = 2,
	FILE_NO_BGSTRINGTEMPLATES_H = 3,
	FILE_NO_MEMORYBLOCK_H = 4,
	FILE_NO_MEMORYBLOCK_CPP = 5,
	FILE_NO_BGABSTRACTSTORAGE_H = 6,
	FILE_NO_BGABSTRACTSTORAGE_CPP = 7,
	FILE_NO_BGEXCEPTION_H = 8,
	FILE_NO_ARCHIVE_CPP = 9,
	FILE_NO_BIPEDMECH_CPP = 10,
	FILE_NO_INTERSECTIONS_CPP = 11,
	FILE_NO_MECHSHELL_CPP = 12,
	FILE_NO_MECHSIM_CPP = 13,
	FILE_NO_MESH_CPP = 14,
	FILE_NO_RENDERABLEOBJECT_CPP = 15,
	FILE_NO_RENDERTOTEXTURE_CPP = 16,
	FILE_NO_XMLTREE_CPP = 17,
	FILE_NO_VEHICLEAI_CPP = 18,
	FILE_NO_VEHICLES_CPP = 19,
	FILE_NO_MECHLAB_CPP = 20,
	FILE_NO_GLWINDOWCONTAINER_CPP = 21,
	FILE_NO_GLWINDOW_CPP = 22,
	FILE_NO_CONFIG_CPP = 23,
	FILE_NO_TEXTURE_CPP = 24,
	FILE_NO_START_GAME_SCREEN_CPP = 25,
	FILE_NO_COMBAT_VEHICLE_CPP = 26,
	FILE_NO_AEROTECH_CPP = 27,
	FILE_NO_MISSION_GOALS_CPP = 28,
	FILE_NO_MESH_POLYGON_CPP = 29,
	FILE_NO_MECHSIM_OVERLAY_CPP = 30,
	FILE_NO_MWBASE_CPP = 31,
	FILE_NO_GAMEOBJECT_CPP = 32,
	FILE_NO_RENDERABLEOBJECT_H = 33,
	FILE_NO_PLAYER_BIPED_MECH_CPP = 34,
	FILE_NO_GROUND_VEHICLE_AI_CPP = 35,
	FILE_NO_FIGHTER_PLAYER_CPP = 36,
	FILE_NO_FIGHTER_AI_CPP = 37,
	FILE_NO_GL_LINE_EDIT_CPP = 38,
	FILE_NO_AUDIO_CPP = 39,
	FILE_NO_SOUND_CPP = 40,
	FILE_NO_ARCHIVE_VIEWER_CPP = 50,
	FILE_NO_GLTABLEVIEW_CPP = 51,
	FILE_NO_FONT_CPP = 52,
	FILE_NO_GLLABEL_CPP = 53,
	FILE_NO_GLCONTEXTMENU_CPP = 54,
	FILE_NO_GLBUTTON_CPP = 55,
	FILE_NO_INSTALLERS_CPP = 58,
	FILE_NO_FIGHTER_AI_SPACE_CPP = 60,
	FILE_NO_POINT3D_CPP = 61,
	
	// Files unique to MechVM ...
	FILE_NO_LEVEL_ED_CPP = 100,
	FILE_NO_MECHWARRIOR_3_ZBD_CPP = 101,
	FILE_NO_MECHWARRIOR_INSTALLERS_CPP = 102,
	FILE_NO_TEXTURE_COMPILER_CPP = 103,
	FILE_NO_QUADMECH_CPP = 104,
	FILE_NO_MW2MECHIMPORTER_CPP = 105,
	FILE_NO_VTOL_AI_CPP = 106,
	FILE_NO_MECHWARRIOR_II_PRJ_CPP = 107,
	FILE_NO_ZFSF_CPP = 108,
	FILE_NO_ISO_9660_CPP = 109,
	FILE_NO_PALETTE_CPP = 110,
	FILE_NO_DATABASE_MW2_CPP = 111,
	FILE_NO_TESTS = 112,
	FILE_NO_GAMEZ_ARCHIVE_CPP = 113,
	FILE_NO_MECHWARRIOR3_ANIM_FILE_CPP = 114,
	FILE_NO_MECHWARRIOR3_MECH_IMPORTER_CPP = 115,
	FILE_NO_MECHWARRIOR3_READER_CPP = 116,
	FILE_NO_DIALOGS_CPP = 117,
	FILE_NO_GLSCROLLBAR_CPP = 118,
	FILE_NO_MW4_RESOURCE_FILE_READER_CPP = 119,
	FILE_NO_SMACKERVIEW_CPP = 120,
	FILE_NO_MECHVM_CPP = 20230407 // don't forget to update this ...
};

////////////////////////////////////////////////////////////////////////////////
// Class IDs

enum BGClassID {
	//BGVectorID = 1,
	//BGStringID = 2,
	//BGStringBufID = 3,
	Point3DID = 4,
	ColorID = 5,
	TextureID = 6,
	MatrixID = 7,
	MatrixStackID = 8,
	XMLAttributeID = 9,
	XMLTreeNodeID = 10,
	XMLTreeID = 11,
	XMLTreeConstIteratorID = 12,
	XMLTreeIteratorID = 13,
	FileCacheID = 14,
	TextureCompilerID = 15,
	ManagedTextureID = 16,
	GLWindowID = 17,
	RenderableObjectID = 18,
	MeshPoint3DDataID = 19,
	MeshID = 20,
	MeshPolygonID = 21,
	MeshPolygonEdgeID = 22,
	MemoryBlockID = 23,
	MechBayChassisTableID = 24,
	WeaponsLockerID = 25,
	WeaponSystemID = 26,
	LoadFromFileBufID = 27,
	BGCPStringID = 28,
	BGCPStringBufID = 29,
	BGStringAttributeID = 30,
	MWArchiveID = 31,
	VehicleAI_ID = 32,
	GLContextMenuEntryID = 33,
	MechListID = 34,
	MW2MechImporterID = 35,
	FileListID = 36,
	BGProcessID = 37,
	ModelViewNormalTransformerID = 38,
	ManagedObjectID = 39,
	TextureTexelsID = 40,
	FileCacheBufID = 41,
	MemoryBlockBufID = 42,
	DepthBufferTexelsID = 43,
	shotID = 44,
	QuadMechID = 45,
	BipedMechID = 46,
	SPTreeID = 47,
	SPTreeCellID = 48,
	GameObjectID = 49,
	StartGameScreenID = 50,
	VehicleID = 51,
	CombatVehicleID = 52,
	AeroTechID = 53,
	SingleFileFakeArchiveID = 54,
	OneDimObjectID = 55,
	TwoDimObjectID = 56,
	BoundingBoxID = 57,
	GroundBasedVehicleAIID = 58,
	PlayerVehicleAIID = 59,
	VTOLAI_ID = 60,
	GroundVehicleAI_ID = 61,
	FighterAI_ID = 62,
	FighterPlayer_ID = 63,
	MechSim_ID = 64,
	GLSlider_ID = 65,
	GLWindowContainer_ID = 66,
	SmackerView_ID = 67,
	LevelEd_ID = 68,
	GLTableView_ID = 69,
	GLScrollbar_ID = 70,
	GLLineEdit_ID = 71,
	GLLabel_ID = 72,
	GLContextMenu_ID = 73,
	GLButton_ID = 74,
	MechVM_ID = 75,
	StartGameScreen_ID = 76,
	MechShell_ID = 77,
	GLCheckBox_ID = 78,
	MechLab_ID = 79,
	Installers_ID = 80,
	GLComboBox_ID = 81,
	ArchiveViewer_ID = 82,
	VTOL_ID = 83,
	Audio_ID = 84,
	Sound_ID = 85,
	MechWarriorIIPRJ_ID = 86,
	ZFSF_ID = 87,
	//Font_ID = 88, // moved to 94
	ISO9660_ID = 88,
	//FighterAISpace_ID = 89, // moved to 95
	GameZArchive_ID = 90,
	MechWarrior3AnimFile_ID = 91,
	MW3ReaderZbdArchive_ID = 92,
	Font_ID = 94,
	FighterAISpace_ID = 95,
	MW4ResourceFileReader_ID
};

////////////////////////////////////////////////////////////////////////////////
// Interface definition for memory tracking

class MemoryTracker {
public:
	// Destructor has to be virtual because we intend to derive from this class
	virtual ~MemoryTracker()
	{
	}

	// Register a new object
	virtual void registerObject(const char* className, int classID, size_t instanceSize) = 0;

	// Deregister an object that is about to be deleted
	virtual void deregisterObject(int classID, size_t instanceSize) = 0;

	// Print usage information
	virtual void dump() = 0;
};

MemoryTracker* getMemoryTracker();
void setMemoryTracker(MemoryTracker* mt);
void activateSimpleMemoryTracker();

////////////////////////////////////////////////////////////////////////////////
// Virtual class: introduces memory profiling and message passing to all
// subclasses

class MessageListener;

class VirtualClass {
public:
	// Default constructor
	// Stores the class name without allocating memory, so only pass static strings here!
	VirtualClass(const char* className, int classID);

	// Copy constructor
	VirtualClass (const VirtualClass& other);

	// Destructors of classes with virtual functions must also be virtual
	virtual ~VirtualClass();

	// Get class metainformation for memory tracking
	#ifdef storeClassID
	const char* getClassName() const
	{ return mClassName; }
	virtual int getClassID() const
	{ return mClassID; }
	#endif
protected:
   #ifdef storeClassID
	// It might seem tempting to define virtual functions instead of using member functions,
	// but we need access to these values during construction and destruction, where using 
	// virtual functions is unsafe
	const char* mClassName;
	int mClassID;
	#endif

	// Send messages to other objects
	bool sendMessage (MessageListener* receiver, int messageType, VirtualClass* data = nullptr);
};

////////////////////////////////////////////////////////////////////////////////
// Class for message listeners

class MessageListener: public VirtualClass {
public:
	// Constructor
	MessageListener(const char* className, int classID)
	: VirtualClass(className, classID)
	{
	}

	// Destructor
	~MessageListener() OVERRIDE
	{
	}

   // Function to receive messages
   virtual void receiveMessage (VirtualClass* /*sender*/, int /*MessageType*/, VirtualClass* /*data*/ = nullptr)
   {
   }
};

////////////////////////////////////////////////////////////////////////////////
// Memory allocation and deallocation with memory tracing

// Allocate memory with trace support, if enabled
void* traceAlloc (size_t bytes, const char* className, int classID);

// Free memory with trace support, if enabled
void traceFree (void* p, int classID, size_t usedMem);

// Free pointer and assign NULL
// Not to be used with objects allocated by ilAlloc, traceAlloc or anything in a MemoryBlock
template <typename T>
void safeDelete(T*& p)
{
   if (p != nullptr) {
      delete p;
      p = nullptr;
   }
}

// Free pointer and assign NULL (array variant)
// Not to be used with objects allocated by ilAlloc or traceAlloc
template <typename T>
void safeDeleteArr(T*& p)
{
	if (p != nullptr) {
		delete[] p;
		p = nullptr;
	}
}

// Test heap integrity by allocating and immediately freeing that memory
void testHeap();

////////////////////////////////////////////////////////////////////////////////
// Memory profiling support

// Get amount of memory used by typeID
size_t getMemoryUsedByType(int typeID);

// Dump memory counters for all classes
void dumpMemoryUsage();

// Initialize table of memory sizes
void initTypeSizes();

////////////////////////////////////////////////////////////////////////////////
// File help functions

// Load data from file
void* loadFromFile (const char* FileName, size_t& size);

#ifdef _WIN32
void* loadFromFile (const wchar_t* FN, size_t& size);
#endif

// Save data to file
bool saveToFile (const char* data, size_t size, const char* FN);

#ifdef _WIN32
bool saveToFile (const char* data, size_t size, const wchar_t* FN);
#endif

////////////////////////////////////////////////////////////////////////////////
// Provide simple timer functionality for non-Windows

#ifndef _WIN32
int GetTickCount();
#endif

////////////////////////////////////////////////////////////////////////////////
// Random functions

// Initialize randomizer - without this, the program produces the same 
// sequence of pseudorandom numbers every time the program is run
void randomize();

// Integer random, returns a value x satisfying a <= x < b
// Works around the Windows limitation of RAND_MAX = 0x7fff (not a joke, unfortunately)
template <typename T>
T irand(T a, T b)
{
	if (a >= b) {
		return a;
	}
	T x = 0;
	T y = 0;
	int randMaxBits = 15; // 15 bits are guaranteed by the standard

	while (y < b-a) {
		T z = rand();
		x = (x << randMaxBits) ^ z;
		y = (y << randMaxBits) | RAND_MAX;
	}

	return a + (x % (b-a));
}

// Produce a floating point random number in given range
template <typename T>
T frand(T a = 0.0, T b = 1.0)
{
	// For Windows, RAND_MAX = 0x7fff. In some cases, more could be required. So we force a higher value here.
	const int frandMax = 1 << 23; // single precision float mantissa is 23 bits
	return a + (b-a) * ((T) irand<int>(0, frandMax)) / ((T) (frandMax)); 
}

////////////////////////////////////////////////////////////////////////////////
// Put b into the range of a, c

template <class Type>
   const Type& putInRange(const Type& min, const Type& val, const Type& max)
{
   if (val < min)
      return min;
   else if (val < max)
      return val;
   else
      return max;
}

////////////////////////////////////////////////////////////////////////////////
// Calculate minimum, maximum at the same time

template <class T>
void minmax (const T& a, const T& b, T& min, T& max)
{
   if (a < b) {
      T help = b; // Keep a copy in case of swap 
      min = a;
      max = help;
   } else {
      T help = a; // Keep a copy in case of swap 
      min = b;
      max = help;
   }
}

////////////////////////////////////////////////////////////////////////////////

// Test if file exists
bool fileExists (const PathChar* FN);

// Copy single file
void copyFile (const PathChar* sourceFN, const PathChar* targetFN);

////////////////////////////////////////////////////////////////////////////////
// Translate SDL keyboard event

bool loadKeyTable (const PathChar* path);
BGKey translateKey (BGKey key, bool shiftHeld, bool AltGRHeld);

void checkShiftAndControl (bool* shiftHeld, bool* controlHeld, bool* altGRDown);

////////////////////////////////////////////////////////////////////////////////
// Message IDs

const int ButtonClickedEvent = 0;
const int ToolbarButtonClickedEvent = 1;
const int UpdateWidgetsEvent = 2;
const int ValueChangedEvent = 3; // Sent by GLLineEdit, GLSlider
const int ContextMenuClickEvent = 4; // Sent by glutContextMenuEntry
const int TableItemSelected = 5; // Sent by GLTableView
const int ContextMenuRequested = 6;
const int CheckBoxChecked = 7;
const int CheckBoxUnchecked = 8;

////////////////////////////////////////////////////////////////////////////////
// Simple class to make sure that handles are closed 

#ifdef _WIN32

class Win32HandleCloser FINAL {
public:
	Win32HandleCloser(HANDLE handle = INVALID_HANDLE_VALUE);

	~Win32HandleCloser();

	void close();

	HANDLE get() const;

	bool valid();

	void operator=(HANDLE newVal);
private:
	HANDLE mHandle;
};

#endif

////////////////////////////////////////////////////////////////////////////////
// Translation support

bool loadLangFile (const PathChar* FileName, WORD cpHint);

////////////////////////////////////////////////////////////////////////////////

// Test various types of crashes
#ifdef _DEBUG
int crash(int method);
#endif

////////////////////////////////////////////////////////////////////////////////
// Update functions for min/max
// Returns true if the value actually was updated

template <typename T>
bool min_update(T& min, const T& newVal)
{
	if (newVal < min) {
		min = newVal;
		return true;
	} else {
		return false;
	}
}

template <typename T>
bool max_update(T& max, const T& newVal)
{
	if (newVal > max) {
		max = newVal;
		return true;
	} else {
		return false;
	}
}

////////////////////////////////////////////////////////////////////////////////
// Update min/max simultaneously

template <typename T>
void min_max_update(T& min, T& max, const T& newVal)
{
	if (newVal < min) {
		min = newVal;
	} else {
		// This test is only necessary if the first branch was not taken
		if (newVal > max) {
			max = newVal;
		}
	}
}

////////////////////////////////////////////////////////////////////////////////

void bgmkdir (const char* newDir);
#ifdef _WIN32
void bgmkdir (const wchar_t* newDir);
#endif

////////////////////////////////////////////////////////////////////////////////

int getLastFileNo();
int getLastLineNo();

void logStackTrace(const char* msg);

#endif
