/*****************************************************************************
*
*  PROJECT:     Multi Theft Auto v1.0
*  LICENSE:     See LICENSE in the top level directory
*  FILE:        multiplayer_sa/CPopulationSA.cpp
*  PURPOSE:     Ped world population class
*  DEVELOPERS:  Ed Lyons <eai@opencoding.net>
*               Christian Myhre Lundheim <>
*               Cecill Etheredge <ijsf@gmx.net>
*
*  Multi Theft Auto is available from http://w...content-available-to-author-only...o.com/
*
*****************************************************************************/

#include "StdInc.h"

using std::list;

CPedSAInterface     * pPedStorage;
CVehicleSAInterface * pVehicleStorage;
CObjectSAInterface  * pObjectStorage;

CPopulationSA       * pSingleton;
DWORD               pedVtable;

VOID HOOK_EndOf_CPopulation__Add();
VOID HOOK_CPopulation__RemovePed();

VOID HOOK_CVehicle__Add();
VOID HOOK_CVehicle__Remove();

VOID HOOK_CObject__Add();
VOID HOOK_CObject__Remove();

CivilianAddHandler      * m_pCivilianAddHandler;
CivilianRemoveHandler   * m_pCivilianRemoveHandler;
VehicleAddHandler		* m_pVehicleAddHandler;
VehicleRemoveHandler	* m_pVehicleRemoveHandler;
ObjectAddHandler		* m_pObjectAddHandler;
ObjectRemoveHandler		* m_pObjectRemoveHandler;

CPopulationSA::CPopulationSA()
{
	dwPedCount = 0;
	dwVehicleCount = 0;
	dwObjectCount = 0;

    HookInstall(HOOKPOS_EndOf_CPopulation__Add, (DWORD)HOOK_EndOf_CPopulation__Add, 6);
    HookInstall(HOOKPOS_CPopulation__RemovePed, (DWORD)HOOK_CPopulation__RemovePed, 6);

#if HOOKPOS_CVehicle__Add != 0x00000000
	HookInstall(HOOKPOS_CVehicle__Add, (DWORD)HOOK_CVehicle__Add, 6);
#endif
#if HOOKPOS_CVehicle__Remove != 0x00000000
	HookInstall(HOOKPOS_CVehicle__Remove, (DWORD)HOOK_CVehicle__Remove, 6);
#endif
#if HOOKPOS_CObject__Add != 0x00000000
	HookInstall(HOOKPOS_CObject__Add, (DWORD)HOOK_CObject__Add, 6);
#endif
#if HOOKPOS_CObject__Remove != 0x00000000
	HookInstall(HOOKPOS_CObject__Remove, (DWORD)HOOK_CObject__Remove, 6);
#endif

    pSingleton = this;
    m_pCivilianRemoveHandler = NULL;
    m_pCivilianAddHandler = NULL;
	m_pObjectAddHandler = NULL;
	m_pObjectRemoveHandler = NULL;
	m_pVehicleAddHandler = NULL;
	m_pVehicleRemoveHandler = NULL;
}

VOID CPopulationSA::AddPed ( CCivilianPed * ped )
{
    CCivilianPedSA* pPedSA = dynamic_cast < CCivilianPedSA* > ( ped );
    if ( !pPedSA ) return;

    CEntitySAInterface* pPedSAInterface = pPedSA->GetInterface ();

    list < CCivilianPedSA* > ::iterator iter;
    for ( iter = peds.begin (); iter != peds.end (); ++iter )
    {
        if ( (*iter)->GetInterface() == pPedSAInterface )
        {
            return;
        }
    }

    peds.push_back ( pPedSA );
    dwPedCount ++;
}

VOID CPopulationSA::AddVehicle(CVehicle * vehicle)
{
	CVehicleSA* pVehicleSA = dynamic_cast < CVehicleSA* > (vehicle);
	if (!pVehicleSA) return;

	CEntitySAInterface* pVehicleSAInterface = pVehicleSA->GetInterface();

	list < CVehicleSA* > ::iterator iter;
	for (iter = vehicles.begin(); iter != vehicles.end(); ++iter)
	{
		if ((*iter)->GetInterface() == pVehicleSAInterface)
		{
			return;
		}
	}

	vehicles.push_back(pVehicleSA);
	dwVehicleCount++;
}

VOID CPopulationSA::AddObject(CObject * object)
{
	CObjectSA* pObjectSA = dynamic_cast < CObjectSA* > (object);
	if (!pObjectSA) return;

	CEntitySAInterface* pObjectSAInterface = pObjectSA->GetInterface();

	list < CObjectSA* > ::iterator iter;
	for (iter = objects.begin(); iter != objects.end(); ++iter)
	{
		if ((*iter)->GetInterface() == pObjectSAInterface)
		{
			return;
		}
	}

	objects.push_back(pObjectSA);
	dwObjectCount++;
}

VOID CPopulationSA::AddPed ( CPedSAInterface * ped )
{ 
    list < CCivilianPedSA* > ::iterator iter;
    for ( iter = peds.begin (); iter != peds.end (); ++iter )
    {
        if ( (*iter)->GetInterface() == ped )
        {
            return;
        }
    }

    //_asm int 3
    CCivilianPedSA * pedSA = dynamic_cast < CCivilianPedSA* > ( pGameInterface->GetPools()->AddCivilianPed((DWORD *)ped ) );
    if ( !pedSA ) return;

    //char szDebug[255] = {'\0'};
    DWORD dwPedInterface = (DWORD)pedSA->GetInterface();
    //sprintf ( szDebug, "Civ ped added (%d) (0x%X -> 0x%X)\n", dwPedCount+1, ped, dwPedInterface);
    //OutputDebugString ( szDebug );

    if ( m_pCivilianAddHandler )
        m_pCivilianAddHandler ( pedSA );

    peds.push_back (pedSA);
    dwPedCount ++;
}

VOID CPopulationSA::AddVehicle(CVehicleSAInterface * vehicle)
{
	list < CVehicleSA* > ::iterator iter;
	for (iter = vehicles.begin(); iter != vehicles.end(); ++iter)
	{
		if ((*iter)->GetInterface() == vehicle)
		{
			return;
		}
	}

	//_asm int 3
	CVehicleSA * vehicleSA = dynamic_cast < CVehicleSA* > (pGameInterface->GetPools()->AddVehicle((DWORD *)vehicle));
	if (!vehicleSA) return;

	//char szDebug[255] = { '\0' };
	DWORD dwVehicleInterface = (DWORD)vehicleSA->GetInterface();
	//sprintf(szDebug, "Vehicle added (%d) (0x%X -> 0x%X)\n", dwVehicleCount + 1, vehicle, dwVehicleInterface);
	//OutputDebugString ( szDebug );

	if (m_pVehicleAddHandler)
		m_pVehicleAddHandler(vehicleSA);

	vehicles.push_back(vehicleSA);
	dwVehicleCount++;
}

VOID CPopulationSA::AddObject(CObjectSAInterface * object)
{
	list < CObjectSA* > ::iterator iter;
	for (iter = objects.begin(); iter != objects.end(); ++iter)
	{
		if ((*iter)->GetInterface() == object)
		{
			return;
		}
	}

	//_asm int 3
	CObjectSA * ObjectSA = dynamic_cast < CObjectSA* > (pGameInterface->GetPools()->AddBuilding(object->m_nModelIndex));
	if (!ObjectSA) return;

	//char szDebug[255] = { '\0' };
	DWORD dwObjectInterface = (DWORD)ObjectSA->GetInterface();
	//sprintf(szDebug, "Object added (%d) (0x%X -> 0x%X)\n", dwObjectCount + 1, object, dwObjectInterface);
	//OutputDebugString ( szDebug );

	if (m_pObjectAddHandler)
		m_pObjectAddHandler(ObjectSA);

	objects.push_back(ObjectSA);
	dwObjectCount++;
}

VOID CPopulationSA::RemovePed ( CCivilianPed * ped )
{
    if ( !ped ) return;

    CCivilianPedSA* pPedSA = dynamic_cast < CCivilianPedSA* > ( ped );
    if ( !pPedSA ) return;

    ped->SetDoNotRemoveFromGameWhenDeleted ( true );
    pGameInterface->GetPools()->RemovePed ( (CPed*)ped );
    if ( !peds.empty () ) peds.remove ( pPedSA );
    dwPedCount--;
}

VOID CPopulationSA::RemoveVehicle(CVehicle * vehicle)
{
	if (!vehicle) return;

	CVehicleSA* pVehicleSA = dynamic_cast < CVehicleSA* > (vehicle);
	if (!pVehicleSA) return;

	vehicle->SetDoNotRemoveFromGameWhenDeleted(true);
	pGameInterface->GetPools()->RemoveVehicle((CVehicle*)vehicle);
	if (!vehicles.empty()) vehicles.remove(pVehicleSA);
	dwVehicleCount--;
}

VOID CPopulationSA::RemoveObject(CObject * object)
{
	if (!object) return;

	CObjectSA* pObjectSA = dynamic_cast < CObjectSA* > (object);
	if (!pObjectSA) return;

	object->SetDoNotRemoveFromGameWhenDeleted(true);
	pGameInterface->GetPools()->RemoveObject((CObject*)object);
	if (!objects.empty()) objects.remove(pObjectSA);
	dwObjectCount--;
}

VOID CPopulationSA::RemovePed ( CPedSAInterface * ped )
{
    list < CCivilianPedSA* > ::iterator iter;
    for ( iter = peds.begin (); iter != peds.end (); ++iter )
    {
        if ( (*iter)->GetInterface() == ped )
        {
            //char szDebug[255] = {'\0'};
            //sprintf ( szDebug, "Civ ped removed (%d)\n", dwPedCount - 1);
            pGameInterface->GetPools()->RemovePed ( (CPed *)(CCivilianPed *)(*iter), false );
            //OutputDebugString ( szDebug );
            
            if ( m_pCivilianRemoveHandler )
                m_pCivilianRemoveHandler ( (*iter) );

            peds.erase ( iter );
            dwPedCount--;
            return;
        }
    }
    //OutputDebugString ( "Tried to remove Civ Ped, but Civ Ped not found!\n" );
}

VOID CPopulationSA::RemoveVehicle(CVehicleSAInterface * vehicle)
{
	list < CVehicleSA* > ::iterator iter;
	for (iter = vehicles.begin(); iter != vehicles.end(); ++iter)
	{
		if ((*iter)->GetInterface() == vehicle)
		{
			//char szDebug[255] = {'\0'};
			//sprintf ( szDebug, "Civ ped removed (%d)\n", dwPedCount - 1);
			pGameInterface->GetPools()->RemoveVehicle((CVehicle*)(*iter), false);
			//OutputDebugString ( szDebug );

			if (m_pVehicleRemoveHandler)
				m_pVehicleRemoveHandler((*iter));

			vehicles.erase(iter);
			dwVehicleCount--;
			return;
		}
	}
	//OutputDebugString ( "Tried to remove Civ Ped, but Civ Ped not found!\n" );
}

VOID CPopulationSA::RemoveObject(CObjectSAInterface * object)
{
	list < CObjectSA* > ::iterator iter;
	for (iter = objects.begin(); iter != objects.end(); ++iter)
	{
		if ((*iter)->GetInterface() == object)
		{
			//char szDebug[255] = {'\0'};
			//sprintf ( szDebug, "Civ ped removed (%d)\n", dwPedCount - 1);
			pGameInterface->GetPools()->RemoveObject((CObject*)(*iter), false);
			//OutputDebugString ( szDebug );

			if (m_pObjectRemoveHandler)
				m_pObjectRemoveHandler((*iter));

			objects.erase(iter);
			dwObjectCount--;
			return;
		}
	}
	//OutputDebugString ( "Tried to remove Civ Ped, but Civ Ped not found!\n" );
}

DWORD CPopulationSA::GetPedCount ( )
{
    return dwPedCount;
}

DWORD CPopulationSA::GetVehicleCount()
{
	return dwVehicleCount;
}

DWORD CPopulationSA::GetObjectCount()
{
	return dwObjectCount;
}

CCivilianPed * CPopulationSA::GetFirstPed ()
{
    if ( peds.size () > 0 )
    {
        pedIter = peds.begin ();
        return *peds.begin ();
    }
    else
    {
        return NULL;
    }
}

CVehicle * CPopulationSA::GetFirstVehicle()
{
	if (vehicles.size() > 0)
	{
		vehIter = vehicles.begin();
		return *vehicles.begin();
	}
	else
	{
		return NULL;
	}
}

CObject * CPopulationSA::GetFirstObject()
{
	if (objects.size() > 0)
	{
		objectIter = objects.begin();
		return *objects.begin();
	}
	else
	{
		return NULL;
	}
}

CCivilianPed * CPopulationSA::GetNextPed ()
{
    ++( pedIter );
    if ( pedIter != peds.end () )
    {
        return *pedIter;
    }
    else
    {
        return NULL;
    }
}

CVehicle * CPopulationSA::GetNextVehicle()
{
	++(vehIter);
	if (vehIter != vehicles.end())
	{
		return *vehIter;
	}
	else
	{
		return NULL;
	}
}

CObject * CPopulationSA::GetNextObject()
{
	++(objectIter);
	if (objectIter != objects.end())
	{
		return *objectIter;
	}
	else
	{
		return NULL;
	}
}

void CPopulationSA::SetCivilianAddHandler ( CivilianAddHandler * pCivilianAddHandler )
{
    m_pCivilianAddHandler = pCivilianAddHandler;
}

void CPopulationSA::SetCivilianRemoveHandler ( CivilianRemoveHandler * pCivilianRemoveHandler )
{
    m_pCivilianRemoveHandler = pCivilianRemoveHandler;
}

void CPopulationSA::SetObjectAddHandler(ObjectAddHandler * pObjectAddHandler)
{
	m_pObjectAddHandler = pObjectAddHandler;
}

void CPopulationSA::SetObjectRemoveHandler(ObjectRemoveHandler * pObjectRemoveHandler)
{
	m_pObjectRemoveHandler = pObjectRemoveHandler;
}

void CPopulationSA::SetVehicleAddHandler(VehicleAddHandler * pVehicleAddHandler)
{
	m_pVehicleAddHandler = pVehicleAddHandler;
}

void CPopulationSA::SetVehicleRemoveHandler(VehicleRemoveHandler * pVehicleRemoveHandler)
{
	m_pVehicleRemoveHandler = pVehicleRemoveHandler;
}

VOID _declspec(naked) HOOK_EndOf_CPopulation__Add()
{
    _asm
    {
        mov     pPedStorage, eax
        pushad
    }

    pSingleton->AddPed ( pPedStorage );

    _asm
    {
        popad
        add     esp, 0x3C
        retn
    }
}

VOID _declspec(naked) HOOK_CPopulation__RemovePed()
{
    /*
    00610F20  /$ 56             PUSH ESI
    00610F21  |. 8B7424 08      MOV ESI,DWORD PTR SS:[ESP+8]
    00610F25  |. 56             PUSH ESI
    */

    _asm
    {
        
        push    esi
        mov     esi, [esp+8]
        push    esi
        mov     pPedStorage, esi
        mov     ecx, [esi]
        mov     pedVtable, ecx
        pushad
    }

    if ( pedVtable == VTBL_CPlayerPed )
    {
        _asm
        {
            popad
            pop     esi
            pop     esi
            retn
        }
    }

    pSingleton->RemovePed(pPedStorage);

    _asm
    {
        popad
        mov     ecx, HOOKPOS_CPopulation__RemovePed
        add     ecx, 6
        jmp     ecx
    }
}

//I really just copied the ASM code, probably will crash
VOID _declspec(naked) HOOK_CVehicle__Add()
{
	_asm
	{
		mov     pVehicleStorage, eax
			pushad
	}

	pSingleton->AddVehicle(pVehicleStorage);

	_asm
	{
		popad
			add     esp, 0x3C
			retn
	}	
}

VOID _declspec(naked) HOOK_CVehicle__Remove()
{
#pragma warning ("Put ASM shit here just like in HOOK_EndOf_CPopulation__Add() to make it work")
	pSingleton->RemoveVehicle(pVehicleStorage);
}

VOID _declspec(naked) HOOK_CObject__Add()
{
	_asm
	{
		mov     pObjectStorage, eax
			pushad
	}

	pSingleton->AddObject(pObjectStorage);

	_asm
	{
		popad
			add     esp, 0x3C
			retn
	}
}
VOID _declspec(naked) HOOK_CObject__Remove()
{
	pSingleton->RemoveObject(pObjectStorage);
}