/******************************************************************
  
  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);

      prop_cast<cGraphicActor *>(item_obj->GetGraphic())->AddEffect(0, SyHashResourceID("fx_item_pickup"));

      return true;
    }
  }
  SyAssertf(0,"Asked to drop item i don't have.");
  return false;
}

// public interface
bool                 
cInventoryContainer::Remove(cItem *pItemToRemove)
{
  for (int ii=0;ii<GetNumItems();++ii)
  {
    if (mItems(ii)==pItemToRemove)
    {
      mItems(ii) = mItems(GetNumItems()-1);
      mItems.Erase();

      return true;
    }
  }

  SyAssertf(0,"Asked to remove 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(NULL); // unequip w/no owner because we don't need to remove enchantments on holder, just initialize, 
  return item;
}

void                
cInventoryContainer::Clear()
{
  for (int ii=0;ii<GetNumItems();ii++)
  {
    delete mItems(ii); // item is merged, so we can get rid of it 
  }
  mItems.Clear();
}

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

  const cStatsItemMaster *master= db->GetItemMaster(masterID);
  SyAssertf(master!=NULL, "Could not find item 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)
{
  SyAssertf(slot != EQUIP_UNEQUIPPED, "cInventoryCharacter::GetEquippedItem() needs valid equip slot");

  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(mOwner, 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(mOwner);
    ((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)
{
  cInventoryRuleContainer* pInventoryRules = mOwner->GetTitan()->GetInventoryRules();
  for (int i=0; i<GetNumItems(); i++)
  {
    if (mItems(i)->GetEquipSlot() != EQUIP_UNEQUIPPED)
    {
      pInventoryRules->ProcessPacket(mItems(i), packet);
    }
  }
}

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

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

}
// EOF
