#include "../common/debug.h"
#include <memory.h>
#include <iostream>
using namespace std;
#include "Loot.h"
#include "../common/timer.h"
#include "MMF.h"

MMF LootMMF;
const MMFLoot_Struct* MMFLootData = 0;
MMFLoot_Struct* MMFLootData_Writable = 0;
int32* LootTable;
int32* LootDrop;

#ifdef WIN32
	#define exportfunc extern "C" __declspec(dllexport)
#else
	#define exportfunc extern "C"
#endif

exportfunc const LootTable_Struct* GetLootTable(uint32 id) {
	return pGetLootTable(id);
};
exportfunc const LootDrop_Struct* GetLootDrop(uint32 id) {
	return pGetLootDrop(id);
};

exportfunc bool AddLootTable(uint32 id, const LootTable_Struct* lts) {
	return pAddLootTable(id, lts);
};
exportfunc bool AddLootDrop(uint32 id, const LootDrop_Struct* lds) {
	return pAddLootDrop(id, lds);
};

exportfunc bool DLLLoadLoot(CALLBACK_DBLoadLoot cbDBLoadLoot, 
					 int32 iLootTableStructsize, int32 iLootTableCount, int32 iMaxLootTable,
					 int32 iLootTableEntryStructsize, int32 iLootTableEntryCount,
					 int32 iLootDropStructsize, int32 iLootDropCount, int32 iMaxLootDrop,
					 int32 iLootDropEntryStructsize, int32 iLootDropEntryCount
					 ) {
	return pDLLLoadLoot(cbDBLoadLoot, 
					 iLootTableStructsize, iLootTableCount, iMaxLootTable,
					 iLootTableEntryStructsize, iLootTableEntryCount,
					 iLootDropStructsize, iLootDropCount, iMaxLootDrop,
					 iLootDropEntryStructsize, iLootDropEntryCount);
};


bool pAddLootTable(int32 id, const LootTable_Struct* lts) {
	if (!MMFLootData_Writable)
		return false;
	if (id > MMFLootData_Writable->MaxLootTableID)
		return false;
	if (!LootTable || LootTable[id] != 0)
		return false;
	
	int32 tmp = sizeof(LootTable_Struct) + (sizeof(LootTableEntries_Struct) * lts->NumEntries);
	if (MMFLootData_Writable->dataindex + tmp >= MMFLootData_Writable->datamax)
		return false;
	LootTable[id] = MMFLootData_Writable->dataindex;
	memcpy(&MMFLootData_Writable->data[MMFLootData_Writable->dataindex], lts, tmp);
	MMFLootData_Writable->dataindex += tmp;

	return true;
}

bool pAddLootDrop(int32 id, const LootDrop_Struct* lds) {
	if (!MMFLootData_Writable)
		return false;
	if (id > MMFLootData_Writable->MaxLootDropID)
		return false;
	if (!LootDrop || LootDrop[id] != 0)
		return false;
	
	int32 tmp = sizeof(LootDrop_Struct) + (sizeof(LootDropEntries_Struct) * lds->NumEntries);
	if (MMFLootData_Writable->dataindex + tmp >= MMFLootData_Writable->datamax)
		return false;
	LootDrop[id] = MMFLootData_Writable->dataindex;
	memcpy(&MMFLootData_Writable->data[MMFLootData_Writable->dataindex], lds, tmp);
	MMFLootData_Writable->dataindex += tmp;

	return true;
}

bool pDLLLoadLoot(CALLBACK_DBLoadLoot cbDBLoadLoot, 
					 int32 iLootTableStructsize, int32 iLootTableCount, int32 iMaxLootTable,
					 int32 iLootTableEntryStructsize, int32 iLootTableEntryCount,
					 int32 iLootDropStructsize, int32 iLootDropCount, int32 iMaxLootDrop,
					 int32 iLootDropEntryStructsize, int32 iLootDropEntryCount
					 ) {
#if 0
cout << "iLootTableCount: " << iLootTableCount << endl;
cout << "iMaxLootTable: " << iMaxLootTable << endl;
cout << "iLootTableEntryCount: " << iLootTableEntryCount << endl;
cout << "iLootDropCount: " << iLootDropCount << endl;
cout << "iMaxLootDrop: " << iMaxLootDrop << endl;
cout << "iLootDropEntryCount: " << iLootDropEntryCount << endl;
#endif
	if (iLootTableStructsize != sizeof(LootTable_Struct)) {
		cout << "Error: EMuShareMem: DLLLoadLoot: iLootTableStructsize != sizeof(LootTable_Struct)" << endl;
		cout << "Item_Struct has changed, EMuShareMem.dll needs to be recompiled." << endl;
		return false;
	}
	if (iLootTableEntryStructsize != sizeof(LootTableEntries_Struct)) {
		cout << "Error: EMuShareMem: DLLLoadLoot: iLootTableEntryStructsize != sizeof(LootTableEntries_Struct)" << endl;
		cout << "Item_Struct has changed, EMuShareMem.dll needs to be recompiled." << endl;
		return false;
	}
	if (iLootDropStructsize != sizeof(LootDrop_Struct)) {
		cout << "Error: EMuShareMem: DLLLoadLoot: iLootDropStructsize != sizeof(LootDrop_Struct)" << endl;
		cout << "Item_Struct has changed, EMuShareMem.dll needs to be recompiled." << endl;
		return false;
	}
	if (iLootDropEntryStructsize != sizeof(LootDropEntries_Struct)) {
		cout << "Error: EMuShareMem: DLLLoadLoot: iLootDropEntryStructsize != sizeof(LootDropEntries_Struct)" << endl;
		cout << "Item_Struct has changed, EMuShareMem.dll needs to be recompiled." << endl;
		return false;
	}

	int32 tmpMemSize = sizeof(MMFLoot_Struct) + 256
		+ (sizeof(int32) * (iMaxLootTable+1))
		+ (sizeof(LootTable_Struct) * iLootTableCount) + (sizeof(LootTableEntries_Struct) * iLootTableEntryCount)
		+ (sizeof(int32) * (iMaxLootDrop+1))
		+ (sizeof(LootDrop_Struct) * iLootDropCount) + (sizeof(LootDropEntries_Struct) * iLootDropEntryCount)
		;
	if (LootMMF.Open("EQEMuLoot", tmpMemSize)) {
		if (LootMMF.CanWrite()) {
			MMFLootData_Writable = (MMFLoot_Struct*) LootMMF.GetWriteableHandle();
			if (!MMFLootData_Writable) {
				cout << "Error: EMuShareMem: DLLLoadLoot: !MMFLootData_Writable" << endl;
				return false;
			}

			memset(MMFLootData_Writable, 0, tmpMemSize);
			MMFLootData_Writable->LootTableCount = iLootTableCount;
			MMFLootData_Writable->MaxLootTableID = iMaxLootTable;
			MMFLootData_Writable->LootDropCount = iLootDropCount;
			MMFLootData_Writable->MaxLootDropID = iMaxLootDrop;
			MMFLootData_Writable->datamax = tmpMemSize - sizeof(MMFLoot_Struct);

			MMFLootData_Writable->dataindex = 0;
			MMFLootData_Writable->LootTableOffset = MMFLootData_Writable->dataindex;
			MMFLootData_Writable->dataindex += (sizeof(int32) * (iMaxLootTable+1));
			MMFLootData_Writable->LootDropOffset = MMFLootData_Writable->dataindex;
			MMFLootData_Writable->dataindex += (sizeof(int32) * (iMaxLootDrop+1));

			LootTable = (int32*) &MMFLootData_Writable->data[MMFLootData_Writable->LootTableOffset];
			LootDrop = (int32*) &MMFLootData_Writable->data[MMFLootData_Writable->LootDropOffset];

			// use a callback so the DB functions are done in the main exe
			// this way the DLL doesnt have to open a connection to mysql
			if (!cbDBLoadLoot()) {
				cout << "Error: EMuShareMem: DLLLoadLoot: !cbDBLoadLoot" << endl;
				return false;
			}

			MMFLootData_Writable = 0;
			LootMMF.SetLoaded();
		}
		else {
			if (!LootMMF.IsLoaded()) {
				Timer::SetCurrentTime();
				int32 starttime = Timer::GetCurrentTime();
				while ((!LootMMF.IsLoaded()) && ((Timer::GetCurrentTime() - starttime) < 300000)) {
					Sleep(10);
					Timer::SetCurrentTime();
				}
				if (!LootMMF.IsLoaded()) {
					cout << "Error: EMuShareMem: DLLLoadLoot: !LootMMF.IsLoaded() (timeout)" << endl;
					return false;
				}
			}
		}
	}
	else {
		cout << "Error Loading Loot: Loot.cpp: pDLLLoadLoot: Open() == false" << endl;
		return false;
	}
	MMFLootData = (const MMFLoot_Struct*) LootMMF.GetHandle();
	if (!MMFLootData) {
		cout << "Error: EMuShareMem: DLLLoadLoot: !MMFLootData" << endl;
		MMFLootData = 0;
		return false;
	}
	if (MMFLootData->LootTableCount != iLootTableCount
		|| MMFLootData->MaxLootTableID != iMaxLootTable
		|| MMFLootData->LootDropCount != iLootDropCount
		|| MMFLootData->MaxLootDropID != iMaxLootDrop) {
		cout << "Error: EMuShareMem: DLLLoadLoot: Count/Max mismatch" << endl;
		MMFLootData = 0;
		return false;
	}
	LootTable = (int32*) &MMFLootData->data[MMFLootData->LootTableOffset];
	LootDrop = (int32*) &MMFLootData->data[MMFLootData->LootDropOffset];
	return true;
};

const LootTable_Struct* pGetLootTable(uint32 id) {
	if (MMFLootData == 0 || !LootMMF.IsLoaded())
		return 0;
	if (id > MMFLootData->MaxLootTableID || LootTable[id] == 0)
		return 0;
	return (LootTable_Struct*) &MMFLootData->data[LootTable[id]];
}

const LootDrop_Struct* pGetLootDrop(uint32 id) {
	if (MMFLootData == 0 || !LootMMF.IsLoaded())
		return 0;
	if (id > MMFLootData->MaxLootDropID || LootDrop[id] == 0)
		return 0;
	return (LootDrop_Struct*) &MMFLootData->data[LootDrop[id]];
}


