/*
*
*   This program is free software; you can redistribute it and/or modify it
*   under the terms of the GNU General Public License as published by the
*   Free Software Foundation; either version 2 of the License, or (at
*   your option) any later version.
*
*   This program is distributed in the hope that it will be useful, but
*   WITHOUT ANY WARRANTY; without even the implied warranty of
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
*   General Public License for more details.
*
*   You should have received a copy of the GNU General Public License
*   along with this program; if not, write to the Free Software Foundation,
*   Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
*   In addition, as a special exception, the author gives permission to
*   link the code of this program with the Half-Life Game Engine ("HL
*   Engine") and Modified Game Libraries ("MODs") developed by Valve,
*   L.L.C ("Valve").  You must obey the GNU General Public License in all
*   respects for all of the code used other than the HL Engine and MODs
*   from Valve.  If you modify this file, you may extend this exception
*   to your version of the file, but you are not obligated to do so.  If
*   you do not wish to do so, delete this exception statement from your
*   version.
*
*/

#pragma once

#define IMPLEMENT_SAVERESTORE(derivedClass, baseClass)\
	int derivedClass::Save(CSave &save)\
	{\
		if (!baseClass::Save(save))\
			return 0;\
		return save.WriteFields(#derivedClass, this, m_SaveData, ARRAYSIZE(m_SaveData));\
	}\
	int derivedClass::Restore(CRestore &restore)\
	{\
		if (!baseClass::Restore(restore))\
			return 0;\
		return restore.ReadFields(#derivedClass, this, m_SaveData, ARRAYSIZE(m_SaveData));\
	}

enum GLOBALESTATE
{
	GLOBAL_OFF,
	GLOBAL_ON,
	GLOBAL_DEAD
};

typedef struct globalentity_s
{
	char name[64];
	char levelName[MAX_MAPNAME_LENGHT];
	GLOBALESTATE state;
	struct globalentity_s *pNext;

} globalentity_t;

struct HEADER
{
	unsigned short size;
	unsigned short token;
	char *pData;
};

const int MAX_ENTITY_ARRAY = 64;

class CBaseEntity;
class CSaveRestoreBuffer
{
public:
	CSaveRestoreBuffer();
	CSaveRestoreBuffer(SAVERESTOREDATA *pdata);
	~CSaveRestoreBuffer();

	int EntityIndex(entvars_t *pevLookup);
	int EntityIndex(edict_t *pentLookup);
	int EntityIndex(EOFFSET eoLookup);
	int EntityIndex(CBaseEntity *pEntity);
	int EntityFlags(int entityIndex, int flags);
	int EntityFlagsSet(int entityIndex, int flags);
	edict_t *EntityFromIndex(int entityIndex);
	unsigned short TokenHash(const char *pszToken);

protected:
	static const int m_Sizes[];

	SAVERESTOREDATA *m_pData;
	void BufferRewind(int size);
	unsigned int HashString(const char *pszToken);
};

class CSave: public CSaveRestoreBuffer
{
public:
	CSave(SAVERESTOREDATA *pdata) : CSaveRestoreBuffer(pdata) {};

	void WriteShort(const char *pname, const short *value, int count);
	void WriteInt(const char *pname, const int *value, int count);
	void WriteFloat(const char *pname, const float *value, int count);
	void WriteTime(const char *pname, const float *value, int count);
	void WriteData(const char *pname, int size, const char *pdata);
	void WriteString(const char *pname, const char *pstring);
	void WriteString(const char *pname, const int *stringId, int count);
	void WriteVector(const char *pname, const Vector &value);
	void WriteVector(const char *pname, const float *value, int count);
	void WritePositionVector(const char *pname, const Vector &value);
	void WritePositionVector(const char *pname, const float *value, int count);
	void WriteFunction(const char *pname, void **data, int count);
	int WriteEntVars(const char *pname, entvars_t *pev);
	int WriteFields(const char *pname, void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount);

private:
	int DataEmpty(const char *pdata, int size);
	void BufferField(const char *pname, int size, const char *pdata);
	void BufferString(char *pdata, int len);
	void BufferData(const char *pdata, int size);
	void BufferHeader(const char *pname, int size);
};

class CRestore: public CSaveRestoreBuffer
{
public:
	CRestore(SAVERESTOREDATA *pdata) : CSaveRestoreBuffer(pdata)
	{
		m_global = FALSE;
		m_precache = TRUE;
	}
	int ReadEntVars(const char *pname, entvars_t *pev);
	int ReadFields(const char *pname, void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount);
	int ReadField(void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount, int startField, int size, char *pName, void *pData);
	int ReadInt();
	short ReadShort();
	int ReadNamedInt(const char *pName);
	char *ReadNamedString(const char *pName);

	bool Empty() const { return (!m_pData || ((m_pData->pCurrentData - m_pData->pBaseData) >= m_pData->bufferSize)); }
	void SetGlobalMode(BOOL global) { m_global = global; }
	void PrecacheMode(BOOL mode) { m_precache = mode; }

private:
	char *BufferPointer();
	void BufferReadBytes(char *pOutput, int size);
	void BufferSkipBytes(int bytes);
	int BufferSkipZString();
	int BufferCheckZString(const char *string);
	void BufferReadHeader(HEADER *pheader);

private:
	BOOL m_global;
	BOOL m_precache;
};

class CGlobalState
{
public:
	CGlobalState();

	void Reset();
	void ClearStates();
	void EntityAdd(string_t globalname, string_t mapName, GLOBALESTATE state);
	void EntitySetState(string_t globalname, GLOBALESTATE state);
	void EntityUpdate(string_t globalname, string_t mapname);
	const globalentity_t *EntityFromTable(string_t globalname);
	GLOBALESTATE EntityGetState(string_t globalname);

	BOOL EntityInTable(string_t globalname) { return Find(globalname) ? TRUE : FALSE; }
	int Save(CSave &save);
	int Restore(CRestore &restore);
	void DumpGlobals();

	static TYPEDESCRIPTION m_SaveData[];
	static TYPEDESCRIPTION m_GlobalEntitySaveData[];

private:
	globalentity_t *Find(string_t globalname);

	globalentity_t *m_pList;
	int m_listCount;
};

void SaveGlobalState(SAVERESTOREDATA *pSaveData);
void RestoreGlobalState(SAVERESTOREDATA *pSaveData);
void ResetGlobalState();

extern CGlobalState gGlobalState;