/******************************************************************
  
  Module:  inventroy.cpp
  
  Author: Sean Craig
  
  Copyright 2005 Sony Online Entertainment.  All rights reserved.
  
*******************************************************************/

//-------------------------------------------------------- Includes
#include "inventory.h"
#include "gameobj.h"
#include "stats.h"
#include "registry.h"
#include "titan.h"
#include "SyScene.h"
#include "SyCSprite.h"
#include "SyESFParse.h"
#include "graphic.h"
#include "rule_inventory.h"
#include "database.h"
#include "physics.h"

//---------------------------------------------- Class Declarations
//--------------------------------------------------------- Globals


//----------------------------------------- Functions Declarations
//------------------------------------ Member Functions Definitions

//------------------------------------ cInventory

cInventory::cInventory() : 
    mOwner(NULL)
{  
  InitPropObject( mCLASSID );
}

void 
cInventory::SetOwner(cGameObject *owner)
{
  mOwner = owner;
}

int           
cInventory::InitPropClass()
{

/* Add the class */

  AddClass( mCLASSID, 
            "cInventory", 
            NULL,         // CRO: abstract base class has no creator
            mCLASSID, 
            0 ); 
  return 0;
}

SyPropObject* 
cInventory::Creator()
{
  SyAssertf(0,"Trying to object-factory an abstract base class");

  return(NULL);
}

//------------------------------------ cInventoryNone

cInventoryNone::cInventoryNone()
{
  InitPropObject( mCLASSID );
}

int           
cInventoryNone::InitPropClass()
{

/* Add the class */

  AddSubClass( mCLASSID, 
               cInventory::mCLASSID,
               mCLASSID,
               "cInventoryNone", 
               Creator, 
               mCLASSID, 
               0 ); 
  return 0;
}

SyPropObject* 
cInventoryNone::Creator()
{
  SyPropObject *pObject;

  pObject = SyNew cInventoryNone();
  if(pObject == NULL)
  {
    SyAssert(0);
    return(NULL);
  }

  return(pObject);
}

//------------------------------------ cInventoryContainer

cInventoryContainer::cInventoryContainer()
{
  InitPropObject( mCLASSID );
}

cInventoryContainer::~cInventoryContainer()
{
  for (int ii=0;ii<GetNumItems();++ii)
  {
    delete mItems(ii);
  }
  mItems.Clear();
}

int           
cInventoryContainer::InitPropClass()
{

/* Add the class */

  AddSubClass( mCLASSID, 
               cInventory::mCLASSID,
               mCLASSID,
               "cInventoryContainer", 
               Creator, 
               mCLASSID, 
               0 ); 

  AddSubObjectPtrVectorProperty<cItem>(mCLASSID, 
                  0x1001, 
                  SyMemberOffset(cInventoryContainer,mItems), 
                  "Items");
  return 0;
}


SyPropObject* 
cInventoryContainer::Creator()
{
  SyPropObject *pObject;

  pObject = SyNew cInventoryContainer();
  if(pObject == NULL)
  {
    SyAssert(0);
    return(NULL);
  }

  return(pObject);
}

  // public interface
bool                 
cInventoryContainer::Drop(cItem *dropped_item)
{
  for (int ii=0;ii<GetNumItems();++ii)
  {
    if (mItems(ii)==dropped_item)
    {
      mItems(ii) = mItems(GetNumItems()-1);
      mItems.Erase();
    }
  
    cGameObjectRegistry *reg= mOwner->GetRegistry();
    tGameObjectID  item_id = reg->Create(cGameObject::OBJ_ITEM);
    cGameObject *item_obj = reg->Fetch(item_id);
    //player->SetLocation(SyVect3(3.0f,1.4f,-5.0f)); // Hells Kitch
    item_obj->SetLocation(mOwner->GetLocation());
    prop_cast<cStatsItem*>(item_obj->GetStats())->SetItem(dropped_item);

    float angle = SY_DEG_TO_RAD(mOwner->GetTitan()->Random(-180,180));

    static const float VEL_XZ = 2.0f;
    static const float VEL_Y = 2.0f;
    SyVect3 vel;
    vel.X = SY_SIN(angle) * VEL_XZ;
    vel.Y = VEL_Y;
    vel.Z = SY_COS(angle) * VEL_XZ;
    
    prop_cast<cPhysicsDynamic *>(item_obj->GetPhysics())->SetVelocity(vel);
    reg->InitObject(item_obj->GetID(),false);
    return true;
  }
  SyAssertf(0,"Asked to drop item i don't have.");
  return false;
}

void
cInventoryContainer::Init()
{
  cDatabaseSys *db = mOwner->GetTitan()->GetDatabaseSys();
  SyAssert(db);

  for (int ii=0;ii<GetNumItems();++ii)
  {
     mItems(ii)->Init(db);
  }
}

bool                 
cInventoryContainer::DropAll() // when a monster is killed, when a container is shattered
{
  while (GetNumItems() > 0)
  {
    if (!Drop(mItems(0)))
    {
      return false;
    }
  }

  return true;
}

cItem *                 
cInventoryContainer::Add(cItem *item)
{
  for (int ii=0;ii<GetNumItems();ii++)
  {
    if (mItems(ii)->Merge(item))
    {
      delete item; // item is merged, so we can get rid of it 
      return mItems(ii);
    }
  }
  mItems.Add(item);
  item->Unequip();
  return item;
}

cItem *                 
cInventoryContainer::Add(tGameID masterID,int quantity)
{
  cItem *item = new cItem();
  cDatabaseSys *db = mOwner->GetTitan()->GetDatabaseSys();
  SyAssert(db);

  const cStatsItemMaster *master= db->GetItemMaster(masterID);
  SyAssert(master); // todo: more graceful failure
  item->SetMaster(master);
  item->SetQuantity(quantity);
  return Add(item);
}


bool                 
cInventoryContainer::PickUp(cGameObject *obj) // object in world
{
  if (obj->GetType() != cGameObject::OBJ_ITEM)
  {
    SyAssertf(0,"Trying to pick up a non-item object???");
    return false;
  }
  cItem *item =((cStatsItem*)obj->GetStats())->TakeItem();
  SyAssertf(item != NULL,"Item not set in target object??");
  Add(item);
  return true;
}

int                  
cInventoryContainer::GetNumItems()
{
  return mItems.Size();
}

cItem *              
cInventoryContainer::GetItem(int index)
{
  SyAssertf(index < GetNumItems(),"Bad Item Index in inventory");
  return mItems(index);
}

//////////////////////////////////////////////////////////////////////////////////////////// cInventoryCharacter


cInventoryCharacter::cInventoryCharacter()
{
  InitPropObject( mCLASSID );
}

int           
cInventoryCharacter::InitPropClass()
{
/* Add the class */

  AddSubClass( mCLASSID, 
               cInventoryContainer::mCLASSID,
               mCLASSID,
               "cInventoryCharacter", 
               Creator, 
               mCLASSID, 
               0 ); 
  return 0;
}

SyPropObject* 
cInventoryCharacter::Creator()
{
  SyPropObject *pObject;

  pObject = SyNew cInventoryCharacter();
  if(pObject == NULL)
  {
    SyAssert(0);
    return(NULL);
  }

  return(pObject);
}


cItem*               
cInventoryCharacter::GetEquippedItem(eEquipSlot slot)
{
  if (slot == EQUIP_UNEQUIPPED)
  {
    SyAssertf(0,"What you talking 'bout, Willis? Bad parameter.");
    return NULL;  
  }
  for (int ii=0;ii<GetNumItems();ii++)
  {
    if (mItems(ii)->GetEquipSlot() == slot)
    {
      return mItems(ii);
    }
  }
  return NULL;
}

bool                 
cInventoryCharacter::Equip(cItem *item, eEquipSlot slot)
{
  // first, check to make sure the item can be equipped in that slot
  if (!item->CanEquip(slot))
  {
    return false;
  }

  // if the item is already in that slot, we're good...
  if (item->GetEquipSlot() == slot)
  {
    return true;
  }

  // if the item is currently equipped in a different slot, unequip it...
  if (item->GetEquipSlot() != EQUIP_UNEQUIPPED)
  {
    Unequip(item->GetEquipSlot());
  }

  if (slot != EQUIP_UNEQUIPPED)
  {
    Unequip(slot);
  }

  item->Equip(slot);
  ((cGraphicCharacter *)mOwner->GetGraphic())->SetDirty();
  return true;
}

void                 
cInventoryCharacter::Unequip(eEquipSlot slot)
{
  if (slot == EQUIP_UNEQUIPPED)
  {
    SyAssertf(0,"What you talking 'bout, Willis? Bad parameter.");
    return;
  }

  cItem *item = GetEquippedItem(slot);
  if (item != NULL)
  {
    item->Unequip();
    ((cGraphicCharacter *)mOwner->GetGraphic())->SetDirty();
  }
}

bool                 
cInventoryCharacter::Drop(cItem *item) 
{
  if (item->GetEquipSlot() != EQUIP_UNEQUIPPED)
  {
    Unequip(item->GetEquipSlot());
  }
  return cInventoryContainer::Drop(item);
  
}

void                  
cInventoryCharacter::ProcessRulePacket(cRulePacket *packet)
{
  for (int slot = EQUIP_MELEE;slot!= EQUIP_UNEQUIPPED;++slot)
  {
    cItem *item = GetEquippedItem((eEquipSlot)slot);
    if (item != NULL)
    {
      mOwner->GetTitan()->GetInventoryRules()->ProcessPacket(item,packet);
    }
  }
}

//////////////////////////////////////////////////////////////////////////////////////////// Global Funcs

void 
RegPropClasses_Inventory()
{
  cInventory::InitPropClass();
  cInventoryNone::InitPropClass();
  cInventoryContainer::InitPropClass();
  cInventoryCharacter::InitPropClass();

}
// EOF
