///////////////////////////////////////////////////////////////////////////////
//
// File   : AVP_AmmoStore.cpp
// Author : Chris Wallace
// Date   : 9-Sept-2008
//
// Copyright (C) 2008 Rebellion, All Rights Reserved.
//
///////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Include files...
////////////////////////////////////////////////////////////////////////////////

#include "Core/Asura.h"

// This file's header...
#include "Inventory/AVP_AmmoStore.h"

// Asura
#include "Blueprint/Asura_Blueprint_System.h"
#include "Core/Asura_Maths.h"
#include "Inventory/AVP_InventoryItem_SubType.h"

// AVP
#include "Blueprint/AVP_Blueprint_HashIDs.h"
#include "Entity/PlayerInfo/AVP_ServerEntity_PlayerInfo.h"
#include "Entity/PhysicalObject/Pickup/AVP_Pickup_Ammo.h"
#include "Entity/PhysicalObject/Pickup/AVP_Pickup_Weapon.h"

// Cheatmodes\Debug
#include "Core/AVP_Settings.h"

////////////////////////////////////////////////////////////////////////////////
// Public functions...
////////////////////////////////////////////////////////////////////////////////

AVP_AmmoStore::AVP_AmmoStore()
{
	InitialiseFromBlueprints();
}

AVP_AmmoStore::~AVP_AmmoStore()
{
	ClearAllAmmoTypes();
}

// Chunk processing functions...
void AVP_AmmoStore::ReadFromChunkStream(Asura_Chunk_Stream& xStream)
{
	u_int	uVersion;

	// Chunk version first
	xStream >> uVersion;

	Asura_Assert(uVersion <= AVP_AmmoStore::CHUNK_VERSION, "Unexpected chunk-version found for AmmoStore.");

	ClearAllAmmoTypes(); // Reset

	u_int uNumTypes = 0;

	xStream >> uNumTypes;

	for(u_int u=0; u<uNumTypes; ++u)
	{
		AVP_AmmoType* pxAmmoType = new AVP_AmmoType(xStream);
		if(pxAmmoType)
		{
			m_xHashedList.AddToList(pxAmmoType);
		}
	}
}

void AVP_AmmoStore::WriteToChunkStream(Asura_Chunk_Stream& xStream) const
{
	// Chunk version first
	xStream << AVP_AmmoStore::CHUNK_VERSION;

	u_int uNumAmmoTypes = m_xHashedList.GetNumEntries();

	xStream << uNumAmmoTypes;

	Asura_Hashed_List_It<AVP_AmmoType> xIt(&m_xHashedList);
	while(!xIt.Done())
	{
		AVP_AmmoType* pxAmmotype = xIt.GetCurrent();
		xIt.Next();
		if(pxAmmotype)
		{
			pxAmmotype->WriteToChunkStream(xStream);
		}
	}
}

void AVP_AmmoStore::InitialiseFromBlueprints(void)
{
	if(!m_xHashedList.IsEmpty())
	{
		ClearAllAmmoTypes();
	}

	u_int uNumAmmoTypes = Asura_Blueprint_System::GetNumChildBlueprints(uAVP_BLUEPRINT_TYPE_INVENTORY_ITEMS, uAVP_BLUEPRINT_NAME_INVENTORYITEM_AMMOTYPE, true);
	for(u_int u=0;u<uNumAmmoTypes;++u)
	{
		Asura_Blueprint* pxBlueprint = Asura_Blueprint_System::GetChildBlueprint(uAVP_BLUEPRINT_TYPE_INVENTORY_ITEMS, uAVP_BLUEPRINT_NAME_INVENTORYITEM_AMMOTYPE, true, static_cast<int>(u));
		if(pxBlueprint)
		{
			AVP_AmmoType* pxAmmoType = new AVP_AmmoType(pxBlueprint->GetHashID());
			if(pxAmmoType)
			{
				pxAmmoType->SetAmmoQuantity(pxBlueprint->GetParameterAsFloat(0x1A7037CF, 0.f));		//Asura_GetHashID("InitialQuantity")
				pxAmmoType->SetMaxQuantity(pxBlueprint->GetParameterAsFloat(0x28B065EF, 0.f));		//Asura_GetHashID("MaxQuantity")

				m_xHashedList.AddToList(pxAmmoType);
			}
		}
	}	
}

void AVP_AmmoStore::ClearAllAmmoTypes(void)
{
	Asura_Hashed_List_Deleter_It<AVP_AmmoType> xDelIt(&m_xHashedList);
	while(!xDelIt.Done())
	{
		AVP_AmmoType* pxAmmotype = xDelIt.GetCurrent();
		if(pxAmmotype)
		{
			xDelIt.DeleteCurrent();
			delete pxAmmotype;
		}
	}
}

float AVP_AmmoStore::GetAmmoQuantity(Asura_Hash_ID uType) const
{ 
	const AVP_AmmoType* pxAmmoType = m_xHashedList.FindObject(uType);
	if(pxAmmoType)
	{
		return pxAmmoType->GetAmmoQuantity();
	}

	// If no ammo type defined, return a large number that doesn't look stupid
	return 999.0f;
}

void AVP_AmmoStore::AddAmmoQuantity(float fQuantity, Asura_Hash_ID uType)
{
	AVP_AmmoType* pxAmmoType = m_xHashedList.FindObject(uType);
	if(pxAmmoType)
	{
		float fCurrent = pxAmmoType->GetAmmoQuantity();
		float fNew = Asura_Maths::Min(fCurrent + fQuantity, pxAmmoType->GetMaxQuantity());
		pxAmmoType->SetAmmoQuantity(fNew);
	} 
}

bool AVP_AmmoStore::IsFull(Asura_Hash_ID uType) const
{
	AVP_AmmoType* pxAmmoType = m_xHashedList.FindObject(uType);
	if(pxAmmoType)
	{
		return pxAmmoType->IsFull();
	} 
    // There is no Ammo of this type, so return false
	return false;
}

void AVP_AmmoStore::RemoveAmmoQuantity(float fQuantity, Asura_Hash_ID uType)
{
	AVP_AmmoType* pxAmmoType = m_xHashedList.FindObject(uType);
	if(pxAmmoType)
	{
		float fCurrent = pxAmmoType->GetAmmoQuantity();
		float fNew = Asura_Maths::Max(fCurrent - fQuantity, 0.0f);
		pxAmmoType->SetAmmoQuantity(fNew);
	} 
}

void AVP_AmmoStore::ReloadSubTypeFromAmmoStore(AVP_InventoryItem_SubType* pxSubType, AVP_AmmoStore* pxAmmoStore)
{
	// secondary...
	float fNewClipSize = 0.0f;

	float fSecondaryAmmoMax		= pxSubType->GetMaxQuantity();
	float fSecondaryAmmoCurrent	= pxSubType->GetCurrentQuantity();

	if(pxAmmoStore && !AVP_Settings::GetInfiniteAmmo())
	{
		Asura_Hash_ID uType(pxSubType->GetAmmoType());

		float fSecondaryAmmoStored	 = pxAmmoStore->GetAmmoQuantity(uType);
		float fSecondaryAmmoRequired = fSecondaryAmmoMax - fSecondaryAmmoCurrent;
		float fAmountToRemove = 0.0f;

		if(fSecondaryAmmoRequired>fSecondaryAmmoStored) // not enough
		{
			fNewClipSize = fSecondaryAmmoCurrent+fSecondaryAmmoStored;
			fAmountToRemove = fSecondaryAmmoStored;
		}
		else
		{
			fNewClipSize = fSecondaryAmmoCurrent+fSecondaryAmmoRequired;
			fAmountToRemove = fSecondaryAmmoRequired;
		}

		pxAmmoStore->RemoveAmmoQuantity(fAmountToRemove, uType);
	}
	else
	{
		fNewClipSize = fSecondaryAmmoMax;
	}

	pxSubType->SetCurrentQuantity(fNewClipSize);
}

float AVP_AmmoStore::GetIntelligentAmmoQuantity(int iIntelligentAmount, const AVP_Pickup_Ammo& xPickup)
{
	// Ammo pickups, random number between blueprinted min and max values - nice and simple.
	switch(iIntelligentAmount)// Sparse
	{
		case AVP_ServerEntity_PlayerInfo::INTELLIGENT_AMOUNT_SCARCE:
		{
			return static_cast<float>(Asura_Random::GetSeededInt( xPickup.GetScarceMinimum(), xPickup.GetScarceMaximum()));
		}
		case AVP_ServerEntity_PlayerInfo::INTELLIGENT_AMOUNT_MEDIUM:
		{
			return static_cast<float>(Asura_Random::GetSeededInt( xPickup.GetMediumMinimum(), xPickup.GetMediumMaximum()));
		}
		case AVP_ServerEntity_PlayerInfo::INTELLIGENT_AMOUNT_PLENTY:
		{
			return static_cast<float>(Asura_Random::GetSeededInt( xPickup.GetPlentyMinimum(), xPickup.GetPlentyMaximum()));
		}
		default :
			Asura_Assert(false, "Undefined pickup Intelligent amount!");
			return 0.0f;
	}
}

float AVP_AmmoStore::GetIntelligentAmmoQuantity(int iIntelligentAmount, const AVP_Pickup& xPickup)
{
	// This function is never going to be speedy - it tries to find the intelligent ammo values from the ammo pickups,
	// given that we have a weapon pickup that has no such knowledge. For now we find the weapon, its subtypes, its ammo type, 
	// and then its pickup to do this. Not nice! Luckily this does not need to be done often.

	Asura_Hash_ID uMinHash = ASURA_HASH_ID_UNSET;
	Asura_Hash_ID uMaxHash = ASURA_HASH_ID_UNSET;
	switch(iIntelligentAmount)// Sparse
	{
		case AVP_ServerEntity_PlayerInfo::INTELLIGENT_AMOUNT_SCARCE:
		{
			uMinHash = uAVP_BLUEPRINT_PARAM_PICKUP_SCARCE_MINIMUM;
			uMaxHash = uAVP_BLUEPRINT_PARAM_PICKUP_SCARCE_MAXIMUM;
			break;
		}
		case AVP_ServerEntity_PlayerInfo::INTELLIGENT_AMOUNT_MEDIUM:
		{
			uMinHash = uAVP_BLUEPRINT_PARAM_PICKUP_MEDIUM_MINIMUM;
			uMaxHash = uAVP_BLUEPRINT_PARAM_PICKUP_MEDIUM_MAXIMUM;
			break;
		}
		case AVP_ServerEntity_PlayerInfo::INTELLIGENT_AMOUNT_PLENTY:
		{
			uMinHash = uAVP_BLUEPRINT_PARAM_PICKUP_PLENTY_MINIMUM;
			uMaxHash = uAVP_BLUEPRINT_PARAM_PICKUP_PLENTY_MAXIMUM;
			break;
		}
		default :
		{
			Asura_Assert(false, "Undefined pickup Intelligent amount!");
			return 0.0f;// don't search for blueprints if error here	 
		}	
	}

	Asura_Hash_ID uInventoryItemHash = AVP_Settings::IsMultiPlayer() ? xPickup.GetMultiplayerInventoryItemBlueprintHash() : xPickup.GetSinglePlayerInventoryItemBlueprintHash();;
	Asura_Blueprint* pxBlueprint = Asura_Blueprint_System::GetBlueprintByHashID(uAVP_BLUEPRINT_TYPE_INVENTORY_ITEMS, uInventoryItemHash);
	if(pxBlueprint)
	{
		Asura_Value xValue;
		if(pxBlueprint->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_SUBTYPE_PRIMARY, xValue))
		{
			Asura_Hash_ID uPrimarySubTypeHash = xValue.GetHashIDValue();
			
			Asura_Blueprint* pxSubBlueprint = Asura_Blueprint_System::GetBlueprintByHashID(uAVP_BLUEPRINT_TYPE_INVENTORY_ITEMS, uPrimarySubTypeHash);
			if(pxSubBlueprint)
			{
				if(pxSubBlueprint->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_AMMOTYPE, xValue))
				{
					Asura_Hash_ID uPrimaryAmmoTypeHash = xValue.GetHashIDValue();
					Asura_Blueprint* pxAmmoBlueprint = Asura_Blueprint_System::GetBlueprintByHashID(uAVP_BLUEPRINT_TYPE_INVENTORY_ITEMS, uPrimaryAmmoTypeHash);
					if(pxAmmoBlueprint)
					{
						if(pxAmmoBlueprint->GetParameterValueByHashID(uAVP_BLUEPRINT_PARAM_PICKUP, xValue))
						{
							Asura_Hash_ID uPrimaryPickupHash = xValue.GetHashIDValue();

							Asura_Blueprint* pxPickupBlueprint = Asura_Blueprint_System::GetBlueprintByHashID(uAVP_BLUEPRINT_TYPE_PICKUPS, uPrimaryPickupHash);
							if(pxPickupBlueprint)
							{
								int iMin = 0;
								int iMax = 0;
								if(pxPickupBlueprint->GetParameterValueByHashID(uMinHash, xValue))
								{
									iMin = xValue.GetIntValue();
								}
								if(pxPickupBlueprint->GetParameterValueByHashID(uMaxHash, xValue))
								{
									iMax = xValue.GetIntValue();
								}
								return static_cast<float>(Asura_Random::GetSeededInt(iMin, iMax));
							}
						}
					}
				}
			}
		}
	}

	// Something went wrong
	Asura_Assert(false, "Couldn't find blueprint for Intelligent amount!");
	return 0.0f; 
}

///////////////////////////////////////////////////////////////////////
// AVP_AmmoType
///////////////////////////////////////////////////////////////////////

AVP_AmmoType::AVP_AmmoType(Asura_Chunk_Stream& xStream)
: m_uTypeHash(ASURA_HASH_ID_UNSET)
, m_fQuantity(0.0f)
, m_fMaxQuantity(0.0f)
{
	ReadFromChunkStream(xStream);
}

void AVP_AmmoType::Reset(void)
{
	m_uTypeHash = ASURA_HASH_ID_UNSET;
	m_fQuantity = 0.f;
	m_fMaxQuantity = 0.f;
}

// Chunk processing functions...
void AVP_AmmoType::ReadFromChunkStream(Asura_Chunk_Stream& xStream)
{
	// Chunk version first
	u_int uVersion;
	xStream >> uVersion;

	Asura_Assert(uVersion <= AVP_AmmoType::CHUNK_VERSION, "Unexpected chunk-version found for AmmoStore.");

	xStream >> m_uTypeHash;
	xStream >> m_fMaxQuantity;
	xStream >> m_fQuantity;
}

void AVP_AmmoType::WriteToChunkStream(Asura_Chunk_Stream& xStream) const
{
	// Chunk version first
	xStream << AVP_AmmoType::CHUNK_VERSION;

	xStream << m_uTypeHash;
	xStream << m_fMaxQuantity;
	xStream << m_fQuantity;
}

// End of file

