#include <fstream>
#include "hook.h"

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

HK_ERROR HkGetCash(wstring wscCharname, int &iCash)
{
	HK_GET_CLIENTID(iClientID, wscCharname);

	if((iClientID != -1) && bIdString && HkIsInCharSelectMenu(iClientID))
		return HKE_NO_CHAR_SELECTED;
	else if((iClientID != -1) && !HkIsInCharSelectMenu(iClientID)) { // player logged in
		pub::Player::InspectCash(iClientID, iCash);
		return HKE_OK;
	} else { // player not logged in
		wstring wscDir;
		if(!HKHKSUCCESS(HkGetAccountDirName(wscCharname, wscDir)))
			return HKE_CHAR_DOES_NOT_EXIST;
		wstring wscFile;
		HkGetCharFileName(wscCharname, wscFile);

		string scCharFile  = scAcctPath + wstos(wscDir) + "\\" + wstos(wscFile) + ".fl";
		if(HkIsEncoded(scCharFile)) {
			string scCharFileNew = scCharFile + ".ini";
			if(!flc_decode(scCharFile.c_str(), scCharFileNew.c_str()))
				return HKE_COULD_NOT_DECODE_CHARFILE;

			iCash = IniGetI(scCharFileNew, "Player", "money", -1);
			DeleteFile(scCharFileNew.c_str());
		} else {
			iCash = IniGetI(scCharFile, "Player", "money", -1);
		}

		return HKE_OK;
	}
}

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

HK_ERROR HkAddCash(wstring wscCharname, int iAmount)
{
	HK_GET_CLIENTID(iClientID, wscCharname);

	uint iClientIDAcc = 0;
	if(iClientID == -1) {
		CAccount *acc = HkGetAccountByCharname(wscCharname);
		if(!acc)
			return HKE_CHAR_DOES_NOT_EXIST;
		iClientIDAcc = HkGetClientIdFromAccount(acc);
	} else
		iClientIDAcc = iClientID;

	if((iClientID != -1) && bIdString && HkIsInCharSelectMenu(iClientID))
		return HKE_NO_CHAR_SELECTED;
	else if((iClientID != -1) && !HkIsInCharSelectMenu(iClientID)) { // player logged in
		pub::Player::AdjustCash(iClientID, iAmount);
		return HKE_OK;
	} else { // player not logged in
		wstring wscDir;
		if(!HKHKSUCCESS(HkGetAccountDirName(wscCharname, wscDir)))
			return HKE_CHAR_DOES_NOT_EXIST;
		wstring wscFile;
		HkGetCharFileName(wscCharname, wscFile);

		string scCharFile  = scAcctPath + wstos(wscDir) + "\\" + wstos(wscFile) + ".fl";
		int iRet;
		if(HkIsEncoded(scCharFile)) {
			string scCharFileNew = scCharFile + ".ini";

			if(!flc_decode(scCharFile.c_str(), scCharFileNew.c_str()))
				return HKE_COULD_NOT_DECODE_CHARFILE;

			iRet = IniGetI(scCharFileNew, "Player", "money", -1);
			IniWrite(scCharFileNew, "Player", "money", itos(iRet + iAmount));

			if(!flc_encode(scCharFileNew.c_str(), scCharFile.c_str()))
				return HKE_COULD_NOT_ENCODE_CHARFILE;

			DeleteFile(scCharFileNew.c_str());
		} else {
			iRet = IniGetI(scCharFile, "Player", "money", -1);
			IniWrite(scCharFile, "Player", "money", itos(iRet + iAmount));
		}

		if(HkIsInCharSelectMenu(wscCharname) || (iClientIDAcc != -1))
		{ // money fix in case player logs in with this account
			bool bFound = false;
			foreach(ClientInfo[iClientIDAcc].lstMoneyFix, MONEY_FIX, i)
			{
				if((*i).wscCharname == wscCharname)
				{
					(*i).iAmount += iAmount;
					bFound = true;
				}
			}

			if(!bFound)
			{
				MONEY_FIX mf;
				mf.wscCharname = wscCharname;
				mf.iAmount = iAmount;
				ClientInfo[iClientIDAcc].lstMoneyFix.push_back(mf);
			}
		}

		return HKE_OK;
	}
}

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

HK_ERROR HkKick(CAccount *acc)
{
	acc->ForceLogout();
	return HKE_OK;
}

HK_ERROR HkKick(wstring wscCharname)
{
	HK_GET_CLIENTID(iClientID, wscCharname);

	if(iClientID == -1)
		return HKE_PLAYER_NOT_LOGGED_IN;

	CAccount *acc = Players.FindAccountFromClientID(iClientID);
	acc->ForceLogout();
	return HKE_OK;
}

HK_ERROR HkKickReason(wstring wscCharname, wstring wscReason)
{
	HK_GET_CLIENTID(iClientID, wscCharname);

	if(iClientID == -1)
		return HKE_PLAYER_NOT_LOGGED_IN;

	if(wscReason.length())
		HkMsgAndKick(iClientID, wscReason, set_iKickMsgPeriod);
	else
		HkKick(Players.FindAccountFromClientID(iClientID));

	return HKE_OK;
}

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

HK_ERROR HkBan(wstring wscCharname, bool bBan)
{
	HK_GET_CLIENTID(iClientID, wscCharname);

	CAccount *acc;
	if(iClientID != -1)
		acc = Players.FindAccountFromClientID(iClientID);
	else {
		if(!(acc = HkGetAccountByCharname(wscCharname)))
			return HKE_CHAR_DOES_NOT_EXIST;
	}

	wstring wscID = HkGetAccountID(acc);
	flstr *flStr = CreateWString(wscID.c_str());
	Players.BanAccount(*flStr, bBan);
	FreeWString(flStr);
	return HKE_OK;
}

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

HK_ERROR HkBeam(wstring wscCharname, wstring wscBasename)
{
	HK_GET_CLIENTID(iClientID, wscCharname);

	// check if logged in
	if(iClientID == -1)
		return HKE_PLAYER_NOT_LOGGED_IN;

	string scBasename = wstos(wscBasename);
	// check if ship in space
	uint iShip = 0;
	pub::Player::GetShip(iClientID, iShip);
	if(!iShip)
		return HKE_PLAYER_NOT_IN_SPACE;

	// get base id
	uint iBaseID;

	if(pub::GetBaseID(iBaseID, scBasename.c_str()) == -4)
	{
		string scBaseShortcut = IniGetS(set_scCfgFile, "names", wstos(wscBasename), "");
		if(!scBaseShortcut.length())
			return HKE_INVALID_BASENAME;

		if(pub::GetBaseID(iBaseID, scBaseShortcut.c_str()) == -4)
			return HKE_INVALID_BASENAME;
	}

	HkSaveChar(wscCharname); // maybe fixes crashes ?
	pub::Player::ForceLand(iClientID, iBaseID);
	HkSaveChar(wscCharname); // maybe fixes crashes ?
	return HKE_OK;
}

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

HK_ERROR HkSaveChar(wstring wscCharname)
{
	HK_GET_CLIENTID(iClientID, wscCharname);

	if(iClientID == -1)
		return HKE_PLAYER_NOT_LOGGED_IN;

	void *pJmp = (char*)hModServer + 0x7EFA8;
	char szNop[2] = { '\x90', '\x90' };
	char szTestAlAl[2] = { '\x74', '\x44' };
	WriteProcMem(pJmp, szNop, sizeof(szNop)); // nop the SinglePlayer() check
	pub::Save(iClientID, 1);
	WriteProcMem(pJmp, szTestAlAl, sizeof(szTestAlAl)); // restore

	return HKE_OK;
}

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

struct EQ_ITEM
{
	EQ_ITEM *next;
	uint i2;
	ushort s1;
	ushort sID;
	uint iGoodID;
	uint i3;
	bool bMounted;
	char sz[3];
	uint i6;
	uint iCount;
	bool bMission;
};

HK_ERROR HkEnumCargo(wstring wscCharname, list<CARGO_INFO> &lstCargo, int &iRemainingHoldSize)
{
	HK_GET_CLIENTID(iClientID, wscCharname);

	if(iClientID == -1 || HkIsInCharSelectMenu(iClientID))
		return HKE_PLAYER_NOT_LOGGED_IN;

	lstCargo.clear();

	char *szClassPtr;
	memcpy(&szClassPtr, &Players, 4);
	szClassPtr += 0x418 * (iClientID - 1);

	EQ_ITEM *eqLst;
	memcpy(&eqLst, szClassPtr + 0x27C, 4);
	EQ_ITEM *eq;
	eq = eqLst->next;
	while(eq != eqLst)
	{
		CARGO_INFO ci = {eq->sID, eq->iCount, eq->iGoodID, eq->bMission, eq->bMounted};
		lstCargo.push_back(ci);

		eq = eq->next;
	}

	float fRemHold;
	pub::Player::GetRemainingHoldSize(iClientID, fRemHold);
	iRemainingHoldSize = (int)fRemHold;
	return HKE_OK;
}

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

HK_ERROR HkRemoveCargo(wstring wscCharname, uint iID, int iCount)
{
	HK_GET_CLIENTID(iClientID, wscCharname);

	if(iClientID == -1 || HkIsInCharSelectMenu(iClientID))
		return HKE_PLAYER_NOT_LOGGED_IN;

	list <CARGO_INFO> lstCargo;
	int iHold;
	HkEnumCargo(wscCharname, lstCargo, iHold);
	foreach(lstCargo, CARGO_INFO, it)
	{
		if(((*it).iID == iID) && ((*it).iCount < iCount))
			iCount = (*it).iCount; // trying to remove more than actually there, thus fix
	}

	pub::Player::RemoveCargo(iClientID, iID, iCount);

	// anti-cheat related
/*	char *szClassPtr;
	memcpy(&szClassPtr, &Players, 4);
	szClassPtr += 0x418 * (iClientID - 1);
	EquipDescList *edlList = (EquipDescList*)szClassPtr + 0x328;
	const EquipDesc *ed = edlList->find_equipment_item(iID);
	if(ed)
	{
		ed->get_id();
		edlList->remove_equipment_item(
	} */


	return HKE_OK;
}

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

HK_ERROR HkAddCargo(wstring wscCharname, uint iGoodID, int iCount, bool bMission)
{
	HK_GET_CLIENTID(iClientID, wscCharname);

	if(iClientID == -1 || HkIsInCharSelectMenu(iClientID))
		return HKE_PLAYER_NOT_LOGGED_IN;

	// anti-cheat related
	char *szClassPtr;
	memcpy(&szClassPtr, &Players, 4);
	szClassPtr += 0x418 * (iClientID - 1);
	EquipDescList *edlList = (EquipDescList*)szClassPtr + 0x328;
	bool bCargoFound = true;
	if(!edlList->find_matching_cargo(iGoodID, 0, 1))
		bCargoFound = false;

	// add
	const GoodInfo *gi;
	if(!(gi = GoodList::find_by_id(iGoodID)))
		return HKE_INVALID_GOOD;

	bool bMultiCount;
	memcpy(&bMultiCount, (char*)gi + 0x70, 1);

	if(bMultiCount) { // it's a good that can have multiple units(commodities, missile ammo, etc)
		int iRet;

		// we need to do this, else server or client may crash
		list<CARGO_INFO> lstCargo;
		HkEnumCargo(wscCharname, lstCargo, iRet);
		foreach(lstCargo, CARGO_INFO, it)
		{
			if(((*it).iArchID == iGoodID) && ((*it).bMission != bMission))
			{
				HkRemoveCargo(wscCharname, (*it).iID, (*it).iCount);
				iCount += (*it).iCount;
			}
		}

		pub::Player::AddCargo(iClientID, iGoodID, iCount, 1, bMission);
	} else {
		for(int i = 0; (i < iCount); i++)
			pub::Player::AddCargo(iClientID, iGoodID, 1, 1, bMission);
	}

	uint iBase = 0;
	pub::Player::GetBase(iClientID, iBase);
	if(iBase)
	{ // player docked on base
		///////////////////////////////////////////////////
		// fix, else we get anti-cheat msg when undocking
		// this DOES NOT disable anti-cheat-detection, we're
		// just making some adjustments so that we dont get kicked
		
		// fix "Ship or Equipment not sold on base" kick
		if(!bCargoFound)
		{
			// get last equipid
			char *szLastEquipID = szClassPtr + 0x3C8;
			ushort sEquipID;
			memcpy(&sEquipID, szLastEquipID, 2);

			// add to check-list which is being compared to the users equip-list when saving char
			EquipDesc ed;
			memset(&ed, 0, sizeof(ed));
			ed.id = sEquipID;
			ed.count = iCount;
			ed.archid = iGoodID;
			edlList->add_equipment_item(ed, true);
		}

		// fix "Ship Related" kick, update crc
		ulong lCRC;
		__asm
		{
			mov ecx, [szClassPtr]
			call [CRCAntiCheat]
			mov [lCRC], eax
		}
		memcpy(szClassPtr + 0x320, &lCRC, 4);
	}

	return HKE_OK;
}

HK_ERROR HkAddCargo(wstring wscCharname, wstring wscGood, int iCount, bool bMission)
{
	uint iGoodID = ToInt(wscGood.c_str());
	if(!iGoodID)
		pub::GetGoodID(iGoodID, wstos(wscGood).c_str());
	if(!iGoodID)
		return HKE_INVALID_GOOD;

	return HkAddCargo(wscCharname, iGoodID, iCount, bMission);
}

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

HK_ERROR HkRename(wstring wscCharname, wstring wscNewCharname, bool bOnlyDelete)
{
	HK_GET_CLIENTID(iClientID, wscCharname);

	if((iClientID == -1) && !HkGetAccountByCharname(wscCharname))
		return HKE_CHAR_DOES_NOT_EXIST;

	if(!bOnlyDelete && HkGetAccountByCharname(wscNewCharname))
		return HKE_CHARNAME_ALREADY_EXISTS;

	if(!bOnlyDelete && (wscNewCharname.length() > 23))
		return HKE_CHARNAME_TOO_LONG;
	
	if(!bOnlyDelete && !wscNewCharname.length())
		return HKE_CHARNAME_TOO_SHORT;

	CAccount *acc;
	wstring wscOldCharname;
	if(iClientID != -1) {
		acc = Players.FindAccountFromClientID(iClientID);
		wscOldCharname = Players.GetActiveCharacterName(iClientID);
	} else {
		wscOldCharname = wscCharname;
		acc = HkGetAccountByCharname(wscCharname);
	}

	wstring wscAccountDirname;
	HkGetAccountDirName(acc, wscAccountDirname);
	wstring wscNewFilename;
	HkGetCharFileName(wscNewCharname, wscNewFilename);
	wstring wscOldFilename;
	HkGetCharFileName(wscOldCharname, wscOldFilename);

	string scNewCharfilePath = scAcctPath + wstos(wscAccountDirname) + "\\" + wstos(wscNewFilename) + ".fl";
	string scOldCharfilePath = scAcctPath + wstos(wscAccountDirname) + "\\" + wstos(wscOldFilename) + ".fl";
	if(!bOnlyDelete)
	{
		if(!flc_decode(scOldCharfilePath.c_str(), scNewCharfilePath.c_str()))
		{ // file wasn't encoded, thus simply rename it
			DeleteFile(scNewCharfilePath.c_str()); // just to get sure...
			CopyFile(scOldCharfilePath.c_str(), scNewCharfilePath.c_str(), FALSE);
		}
	}

	// delete old character
	flstr *str = CreateWString(wscOldCharname.c_str());
	HkLockAccountAccess(acc, true); // also kicks player on this account
	bool bRet = Players.DeleteCharacterFromName(*str);
	HkUnlockAccountAccess(acc);
	FreeWString(str);

	DeleteFile(scOldCharfilePath.c_str()); // doesn't delete when previously renamed, dunno why
	
	if(bOnlyDelete)
		return HKE_OK; // only delete char, thus we're finished

	wstring wscNewNameString = L"";
	for(uint i = 0; (i < wscNewCharname.length()); i++)
	{
		char cHiByte = wscNewCharname[i] >> 8;
		char cLoByte = wscNewCharname[i] & 0xFF;
		wchar_t wszBuf[8];
		swprintf(wszBuf, L"%02X%02X", ((uint)cHiByte) & 0xFF, ((uint)cLoByte) & 0xFF);
		wscNewNameString += wszBuf;
	}

	IniWrite(scNewCharfilePath, "Player", "Name", wstos(wscNewNameString));

	// we'll also need to add the new character into the flname searchtree in memory(which is usually build when
	// flserver starts). methods like FindAccountByCharacterName won't work else

	// insert into flname search tree
	struct TREENODE
	{
		TREENODE *pLeft;
		TREENODE *pParent;
		TREENODE *pRight;
		ulong l1;
		char *szFLName;
		uint lLength;
		ulong l2; // ??
		CAccount *acc;
	};

	char *pEBP;
	pEBP = (char*)&Players + 0x30;
	char *pEDI;
	memcpy(&pEDI, pEBP + 4, 4);
	TREENODE *leaf;
	memcpy(&leaf, pEBP + 8, 4);
	// edi+4 is where the root node is
	TREENODE *node;
	memcpy(&node, pEDI + 4, 4);

	bool bLeft = true;
	TREENODE *lastparent = (TREENODE*)(pEDI + 4);
	while(node != leaf)
	{
		lastparent = node;
		int iRet = strcmp(node->szFLName, wstos(wscNewFilename + L".fl").c_str());
		if(iRet >= 0) { // leftnode?
			node = node->pLeft;
			bLeft = true;
		} else { // rightnode?
			node = node->pRight;
			bLeft = false;
		}
	}

	// we need to use fl's new operator, else free might fail later when char gets deleted
	typedef void* (*_flnew)(unsigned int);
	_flnew flnew = (_flnew) SRV_ADDR(ADDR_FLNEW);

	TREENODE *tn = (TREENODE *)flnew(sizeof(TREENODE));
	memset(tn, 0, sizeof(TREENODE));
	tn->pLeft = leaf;
	tn->pParent = lastparent;
	tn->pRight = leaf;
	tn->szFLName = new char[32];
	strcpy(tn->szFLName, wstos(wscNewFilename + L".fl").c_str());
	tn->lLength = (uint)strlen(tn->szFLName);
	tn->l2 = 0x1F; // seems to be always 0x1F
	tn->acc = acc;
	if(bLeft)
		lastparent->pLeft = tn;
	else
		lastparent->pRight = tn;

	// finally we need to add the new char to the CAccount character list
	// else it will not be shown in the accounts list in the flserver window
	struct LISTNODE
	{
		LISTNODE *next;
		LISTNODE *prev;
		u_long   l1;
		wchar_t *wszCharname;
		ulong lArray[32];
	};
	LISTNODE *lnHead;
	memcpy(&lnHead, (char*)acc + 0x28, 4);

	LISTNODE *lnCur = lnHead->next;
	while(lnCur->next != lnHead)
		lnCur = lnCur->next;

	LISTNODE *ln = (LISTNODE *)flnew(sizeof(LISTNODE));
	ln->next = lnHead;
	ln->prev = lnCur;
	ln->wszCharname = new wchar_t[32];
	wcscpy(ln->wszCharname, wscNewCharname.c_str());
	lnCur->next = ln;

	return HKE_OK;
}

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

HK_ERROR HkMsgAndKick(uint iClientID, wstring wscReason, uint iIntervall)
{
	if(!ClientInfo[iClientID].tmKickTime)
	{
		wstring wscMsg = ReplaceStr(set_wscKickMsg, L"%reason", XMLText(wscReason));
		HkFMsg(iClientID, wscMsg);
		ClientInfo[iClientID].tmKickTime = timeInMS() + iIntervall;
	}

	return HKE_OK;
}

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

HK_ERROR HkKill(wstring wscCharname)
{
	HK_GET_CLIENTID(iClientID, wscCharname);

	// check if logged in
	if(iClientID == -1)
		return HKE_PLAYER_NOT_LOGGED_IN;

	uint iShip;
	pub::Player::GetShip(iClientID, iShip);
	if(!iShip)
		return HKE_PLAYER_NOT_IN_SPACE;

	pub::SpaceObj::SetRelativeHealth(iShip, 0.0f);
	return HKE_OK;
}

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

HK_ERROR HkGetReservedSlot(wstring wscCharname, bool &bResult)
{
	HK_GET_CLIENTID(iClientID, wscCharname);

	CAccount *acc;
	if(iClientID != -1)
		acc = Players.FindAccountFromClientID(iClientID);
	else
		acc = HkGetAccountByCharname(wscCharname);

	if(!acc)
		return HKE_CHAR_DOES_NOT_EXIST;

	wstring wscDir; 
	HkGetAccountDirName(acc, wscDir); 
	string scUserFile = scAcctPath + wstos(wscDir) + "\\flhookuser.ini";

	bResult = IniGetB(scUserFile, "Settings", "ReservedSlot", false);
	return HKE_OK;
}

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

HK_ERROR HkSetReservedSlot(wstring wscCharname, bool bReservedSlot)
{
	HK_GET_CLIENTID(iClientID, wscCharname);

	CAccount *acc;
	if(iClientID != -1)
		acc = Players.FindAccountFromClientID(iClientID);
	else
		acc = HkGetAccountByCharname(wscCharname);

	if(!acc)
		return HKE_CHAR_DOES_NOT_EXIST;

	wstring wscDir; 
	HkGetAccountDirName(acc, wscDir); 
	string scUserFile = scAcctPath + wstos(wscDir) + "\\flhookuser.ini";

	if(bReservedSlot)
		IniWrite(scUserFile, "Settings", "ReservedSlot", "yes");
	else
		IniWrite(scUserFile, "Settings", "ReservedSlot", "no");
	return HKE_OK;
}

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

struct AUTOBUY_CARTITEM
{
	uint iArchID;
	uint iCount;
	wstring wscDescription;
};

int HkPlayerAutoBuyGetCount(list<CARGO_INFO> &lstCargo, uint iItemArchID)
{
	foreach(lstCargo, CARGO_INFO, it)
	{
		if((*it).iArchID == iItemArchID)
			return (*it).iCount;
	}

	return 0;
}

#define ADD_EQUIP_TO_CART(desc)	{ aci.iArchID = eq->iAmmoArchID; \
								aci.iCount = 50 - HkPlayerAutoBuyGetCount(lstCargo, aci.iArchID); \
								aci.wscDescription = desc; \
								lstCart.push_back(aci); }

void HkPlayerAutoBuy(uint iClientID, uint iBaseID)
{
	// player cargo
	int iRemHoldSize;
	list<CARGO_INFO> lstCargo;
	HkEnumCargo(ARG_CLIENTID(iClientID), lstCargo, iRemHoldSize);

	// shopping cart
	list<AUTOBUY_CARTITEM> lstCart;

	if(ClientInfo[iClientID].bAutoBuyReload)
	{ // shield bats & nanobots
		Archetype::Ship *ship = Archetype::GetShip(Players[iClientID].lShipArchID);

		uint iNanobotsID;
		pub::GetGoodID(iNanobotsID, "ge_s_repair_01");
		uint iRemNanobots = ship->lMaxNanobots;
		uint iShieldBatsID;
		pub::GetGoodID(iShieldBatsID, "ge_s_battery_01");
		uint iRemShieldBats = ship->lMaxShieldBats;
		bool bNanobotsFound = false;
		bool bShieldBattsFound = false;
		foreach(lstCargo, CARGO_INFO, it)
		{
			AUTOBUY_CARTITEM aci;
			if((*it).iArchID == iNanobotsID) {
				aci.iArchID = iNanobotsID;
				aci.iCount = ship->lMaxNanobots - (*it).iCount;
				aci.wscDescription = L"Nanobots";
				lstCart.push_back(aci);
				bNanobotsFound = true;
			} else if((*it).iArchID == iShieldBatsID){
				aci.iArchID = iShieldBatsID;
				aci.iCount = ship->lMaxShieldBats - (*it).iCount;
				aci.wscDescription = L"Shield Batteries";
				lstCart.push_back(aci);
				bShieldBattsFound = true;
			}
		}

		if(!bNanobotsFound) 
		{ // no nanos found -> add all
			AUTOBUY_CARTITEM aci;
			aci.iArchID = iNanobotsID;
			aci.iCount = ship->lMaxNanobots;
			aci.wscDescription = L"Nanobots";
			lstCart.push_back(aci);
		} 

		if(!bShieldBattsFound) 
		{ // no batts found -> add all
			AUTOBUY_CARTITEM aci;
			aci.iArchID = iShieldBatsID;
			aci.iCount = ship->lMaxShieldBats;
			aci.wscDescription = L"Shield Batteries";
			lstCart.push_back(aci);
		}
	}

	if(ClientInfo[iClientID].bAutoBuyCD || ClientInfo[iClientID].bAutoBuyCM || ClientInfo[iClientID].bAutoBuyMines ||
		ClientInfo[iClientID].bAutoBuyMissiles || ClientInfo[iClientID].bAutoBuyTorps)
	{
		// add mounted equip to a new list and eliminate double equipment(such as 2x lancer etc)
		list<CARGO_INFO> lstMounted;
		foreach(lstCargo, CARGO_INFO, it)
		{
			if(!(*it).bMounted)
				continue;

			bool bFound = false;
			foreach(lstMounted, CARGO_INFO, it2)
			{
				if((*it2).iArchID == (*it).iArchID)
				{
					bFound = true;
					break;
				}
			}

			if(!bFound)
				lstMounted.push_back(*it);
		}

		uint iVFTableMines = (uint)hModCommon + ADDR_COMMON_VFTABLE_MINE;
		uint iVFTableCM = (uint)hModCommon + ADDR_COMMON_VFTABLE_CM;
		uint iVFTableGun = (uint)hModCommon + ADDR_COMMON_VFTABLE_GUN;

		// check mounted equip
		foreach(lstMounted, CARGO_INFO, it2)
		{
			uint i = (*it2).iArchID;
			AUTOBUY_CARTITEM aci;
			Archetype::Equipment *eq = Archetype::GetEquipment((*it2).iArchID);

			if(eq->iVFTable == iVFTableMines)
			{
				if(ClientInfo[iClientID].bAutoBuyMines)
					ADD_EQUIP_TO_CART(L"Mines")
				continue;
			}

			if(eq->iVFTable == iVFTableCM)
			{
				if(ClientInfo[iClientID].bAutoBuyCM)
					ADD_EQUIP_TO_CART(L"Countermeasures")
				continue;
			} 

			if(eq->iVFTable == iVFTableGun)
			{
				Archetype::Gun *gun = (Archetype::Gun *)eq;
				uint iGunType = gun->get_hp_type_by_index(0);
				if((iGunType == 36) && ClientInfo[iClientID].bAutoBuyTorps)
					ADD_EQUIP_TO_CART(L"Torpedos")
				else if((iGunType == 35) && ClientInfo[iClientID].bAutoBuyCD)
					ADD_EQUIP_TO_CART(L"Cruise Disruptors")
				else if(ClientInfo[iClientID].bAutoBuyMissiles)
					ADD_EQUIP_TO_CART(L"Missiles")

				continue;
			}
		}
	}

	// search base in base-info list
	BASE_INFO *bi = 0;
	foreach(lstBases, BASE_INFO, it3)
	{
		if(it3->iBaseID == iBaseID)
		{
			bi = &(*it3);
			break;
		}
	}

	if(!bi)
		return; // base not found

	int iCash;
	HkGetCash(ARG_CLIENTID(iClientID), iCash);

	foreach(lstCart, AUTOBUY_CARTITEM, it4)
	{
		if(!(*it4).iCount || !Arch2Good((*it4).iArchID))
			continue;

		// check if good is available and if player has the neccessary rep
		bool bGoodAvailable = false;
		foreach(bi->lstMarketMisc, DATA_MARKETITEM, itmi)
		{
			if(itmi->iArchID == it4->iArchID)
			{
				// get base rep
				int iSolarRep;
				pub::SpaceObj::GetSolarRep(bi->iObjectID, iSolarRep);
				uint iBaseRep;
				pub::Reputation::GetAffiliation(iSolarRep, iBaseRep);
				if(iBaseRep == -1)
					continue; // rep can't be determined yet(space object not created yet?)

				// get player rep
				int iRepID;
				pub::Player::GetRep(iClientID, iRepID);

				// check if rep is sufficient
				float fPlayerRep;
				pub::Reputation::GetGroupFeelingsTowards(iRepID, iBaseRep, fPlayerRep);
				if(fPlayerRep < itmi->fRep)
					break; // bad rep, not allowed to buy
				bGoodAvailable = true;
				break;
			}
		}

		if(!bGoodAvailable)
			continue; // base does not sell this item or bad rep

		float fPrice;
		if(pub::Market::GetPrice(iBaseID, (*it4).iArchID, fPrice) == -1)
			continue; // good not available

		Archetype::Equipment *eq = Archetype::GetEquipment((*it4).iArchID);
		if(iRemHoldSize < (eq->fVolume * (*it4).iCount))
		{
			uint iNewCount = iRemHoldSize / (uint)eq->fVolume;
			if(!iNewCount) {
//				PrintUserCmdText(iClientID, L"Auto-Buy(%s): FAILED! Insufficient cargo space", (*it4).wscDescription.c_str());
				continue;
			} else
				(*it4).iCount = iNewCount;
		}

		int iCost = ((int)fPrice * (*it4).iCount);
		if(iCash < iCost)
			PrintUserCmdText(iClientID, L"Auto-Buy(%s): FAILED! Insufficient Credits", (*it4).wscDescription.c_str());
		else {
			HkAddCash(ARG_CLIENTID(iClientID), -iCost);
			iCash -= iCost;
			iRemHoldSize -= ((int)eq->fVolume * (*it4).iCount);
			HkAddCargo(ARG_CLIENTID(iClientID), (*it4).iArchID, (*it4).iCount, false);
			PrintUserCmdText(iClientID, L"Auto-Buy(%s): Bought %u unit(s), cost: %s$", (*it4).wscDescription.c_str(), (*it4).iCount, ToMoneyStr(iCost).c_str());
		}
	}

}

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

HK_ERROR HkResetRep(wstring wscCharname)
{
	HK_GET_CLIENTID(iClientID, wscCharname);

	// check if logged in
	if(iClientID == -1)
		return HKE_PLAYER_NOT_LOGGED_IN;

	INI_Reader ini;
	if(!ini.open("mpnewcharacter.fl", false))
		return HKE_MPNEWCHARACTERFILE_NOT_FOUND_OR_INVALID;

	ini.read_header();
	if(!ini.is_header("Player"))
	{
		ini.close();
		return HKE_MPNEWCHARACTERFILE_NOT_FOUND_OR_INVALID;
	}

	int iPlayerRep;
	pub::Player::GetRep(iClientID, iPlayerRep);
	while(ini.read_value())
	{
		if(ini.is_value("house"))
		{
			float fRep = ini.get_value_float(0);
			const char *szRepGroupName = ini.get_value_string(1);

			uint iRepGroupID;
			pub::Reputation::GetReputationGroup(iRepGroupID, szRepGroupName);
			pub::Reputation::SetReputation(iPlayerRep, iRepGroupID, fRep);
		}
	}

	ini.close();
	return HKE_OK;
}

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

HK_ERROR HkSetRep(wstring wscCharname, wstring wscRepGroup, float fValue)
{
	HK_GET_CLIENTID(iClientID, wscCharname);
	// check if logged in
	if(iClientID == -1)
		return HKE_PLAYER_NOT_LOGGED_IN;

	uint iRepGroupID;
	pub::Reputation::GetReputationGroup(iRepGroupID, wstos(wscRepGroup).c_str());
	if(iRepGroupID == -1)
		return HKE_INVALID_REP_GROUP;

	int iPlayerRep;
	pub::Player::GetRep(iClientID, iPlayerRep);
	pub::Reputation::SetReputation(iPlayerRep, iRepGroupID, fValue);
	return HKE_OK;
}

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

HK_ERROR HkGetGroupMembers(wstring wscCharname, list<GROUP_MEMBER> &lstMembers)
{
	lstMembers.clear();
	HK_GET_CLIENTID(iClientID, wscCharname);

	// check if logged in
	if(iClientID == -1)
		return HKE_PLAYER_NOT_LOGGED_IN;

	// hey, at least it works!
	vector<uint> vMembers;
	char szBuf[1024] = "";
	pub::Player::GetGroupMembers(iClientID, *((vector<uint>*)szBuf));
	vMembers = *((vector<uint>*)szBuf);

	for(uint i = 0 ; (i < vMembers.size()); i++)
	{
		GROUP_MEMBER gm;
		gm.iClientID = vMembers[i];
		gm.wscCharname = Players.GetActiveCharacterName(vMembers[i]);
		lstMembers.push_back(gm);
	}

	return HKE_OK;
}

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

HK_ERROR HkReadCharFile(wstring wscCharname, list<wstring> &lstOutput)
{
	lstOutput.clear();
	HK_GET_CLIENTID(iClientID, wscCharname);

	wstring wscDir;
	CAccount *acc;
	if(iClientID != -1) {
		acc = Players.FindAccountFromClientID(iClientID);
		const wchar_t *wszCharname = Players.GetActiveCharacterName(iClientID);
		if(!wszCharname)
			return HKE_NO_CHAR_SELECTED;

		if(!HKHKSUCCESS(HkGetAccountDirName(wszCharname, wscDir)))
			return HKE_CHAR_DOES_NOT_EXIST;
	} else {
		if(!HKHKSUCCESS(HkGetAccountDirName(wscCharname, wscDir)))
			return HKE_CHAR_DOES_NOT_EXIST;
	}

	wstring wscFile;
	HkGetCharFileName(wscCharname, wscFile);
	string scCharFile  = scAcctPath + wstos(wscDir) + "\\" + wstos(wscFile) + ".fl";
	string scFileToRead;
	bool bDeleteAfter;
	if(HkIsEncoded(scCharFile)) {
		string scCharFileNew = scCharFile + ".ini";
		if(!flc_decode(scCharFile.c_str(), scCharFileNew.c_str()))
			return HKE_COULD_NOT_DECODE_CHARFILE;
		scFileToRead = scCharFileNew;
		bDeleteAfter = true;
	} else {
		scFileToRead = scCharFile;
		bDeleteAfter = false;
	}

	ifstream ifs;
	ifs.open(scFileToRead.c_str(), ios_base::in);
	if(!ifs.is_open())
		return HKE_UNKNOWN_ERROR;

	string scLine;
	while(getline(ifs, scLine))
		lstOutput.push_back(stows(scLine));
	ifs.close();
	if(bDeleteAfter)
		DeleteFile(scFileToRead.c_str());
	return HKE_OK;
}

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

HK_ERROR HkWriteCharFile(wstring wscCharname, wstring wscData)
{
	HK_GET_CLIENTID(iClientID, wscCharname);

	wstring wscDir;
	CAccount *acc;
	if(iClientID != -1) {
		acc = Players.FindAccountFromClientID(iClientID);
		const wchar_t *wszCharname = Players.GetActiveCharacterName(iClientID);
		if(!wszCharname)
			return HKE_NO_CHAR_SELECTED;

		if(!HKHKSUCCESS(HkGetAccountDirName(wszCharname, wscDir)))
			return HKE_CHAR_DOES_NOT_EXIST;
	} else {
		if(!HKHKSUCCESS(HkGetAccountDirName(wscCharname, wscDir)))
			return HKE_CHAR_DOES_NOT_EXIST;
	}

	wstring wscFile;
	HkGetCharFileName(wscCharname, wscFile);
	string scCharFile  = scAcctPath + wstos(wscDir) + "\\" + wstos(wscFile) + ".fl";
	string scFileToWrite;
	bool bEncode;
	if(HkIsEncoded(scCharFile)) {
		scFileToWrite = scCharFile + ".ini";
		bEncode = true;
	} else {
		scFileToWrite = scCharFile;
		bEncode = false;
	}

	ofstream ofs;
	ofs.open(scFileToWrite.c_str(), ios_base::out);
	if(!ofs.is_open())
		return HKE_UNKNOWN_ERROR;

	size_t iPos;
	while((iPos = wscData.find(L"\\n")) != -1)
	{
		wstring wscLine = wscData.substr(0, iPos);
		ofs << wstos(wscLine) << endl;
		wscData.erase(0, iPos + 2);
	}

	if(wscData.length())
		ofs << wstos(wscData);

	ofs.close();
	if(bEncode)
	{
		flc_encode(scFileToWrite.c_str(), scCharFile.c_str());
		DeleteFile(scFileToWrite.c_str());
	}
	return HKE_OK;
}

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

HK_ERROR HkCloak(uint iClientID)
{

	ClientInfo[iClientID].bIsCloaking = true;
	ClientInfo[iClientID].bWantsCloak = false;
	ClientInfo[iClientID].bCloaked = false;
	ClientInfo[iClientID].tmCloakTime = timeInMS();

	XActivateEquip ActivateEq;
	ActivateEq.bActivate = true;
	ActivateEq.l1 = ClientInfo[iClientID].iShip;
	ActivateEq.sID = ClientInfo[iClientID].iCloakSlot;
	Server.ActivateEquip(iClientID,ActivateEq);

	PrintUserCmdText(iClientID, L" Cloaking");
	
	return HKE_OK;

}

HK_ERROR HkUnCloak(uint iClientID)
{

	ClientInfo[iClientID].bIsCloaking = false;
	ClientInfo[iClientID].bWantsCloak = false;
	ClientInfo[iClientID].bCloaked = false;
	ClientInfo[iClientID].tmCloakTime = timeInMS();

	XActivateEquip ActivateEq;
	ActivateEq.bActivate = false;
	ActivateEq.l1 = ClientInfo[iClientID].iShip;
	ActivateEq.sID = ClientInfo[iClientID].iCloakSlot;
	Server.ActivateEquip(iClientID,ActivateEq);

	PrintUserCmdText(iClientID, L" Uncloaking");

	return HKE_OK;

}

HK_ERROR HkInitCloakSettings(uint iClientID)
{

	ClientInfo[iClientID].bCanCloak = false;

	wstring wscCharname = Players.GetActiveCharacterName(iClientID);

    list<CARGO_INFO> lstEquipment;
	int iRemaining;
	if (HKHKSUCCESS(HkEnumCargo(wscCharname,lstEquipment,iRemaining))) {
		foreach(lstEquipment, CARGO_INFO, it)
		{
			if((*it).bMounted) {
				// check for mounted cloak device
				foreach(set_lstCloakDevices,INISECTIONVALUE,it2) {
					uint iArchIDCloak = CreateID((*it2).scKey.c_str());
					if ((*it).iArchID == iArchIDCloak){
						ClientInfo[iClientID].iCloakingTime = atoi((*it2).scValue.c_str());
						ClientInfo[iClientID].bCanCloak = true;
						ClientInfo[iClientID].bIsCloaking = false;
						ClientInfo[iClientID].bWantsCloak = false;
						ClientInfo[iClientID].bCloaked = false;
						ClientInfo[iClientID].iCloakSlot = (*it).iID;
						ClientInfo[iClientID].tmCloakTime = timeInMS();
						break;
					}
				}
			}
		}
	}

	return HKE_OK;
}
