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

//-------------------------------------------------------- Includes
#include "item.h"
#include "gameobj.h"
#include "stats.h"
#include "database.h"
#include "spell.h"
#include "titan.h"
#include "t4.h"
#include "gameerror.h"
//---------------------------------------------- Class Declarations
//----------------------------------------- Functions Declarations
//------------------------------------ Member Functions Definitions

//------------------------------------- cStatsItemMaster 
cStatsItemMaster::cStatsItemMaster():
mType(IT_TRINKET),
mClass(IC_ANY),
mWorldModel(NULL),
mFileName(NULL),
mIcon(NULL),
mAttachModel(NULL),
mEmitterModel(NULL),
mProjectile(ID_NONE),
mAttachSkin(0),
mNameString(0),
mDescriptionString(0),
mValue(0),
mBaseDamageType(DT_NONE),
mBaseDamageBonus(0),
mMaxHP(0),
mMaxMana(0),
mAttackPower(0),
mSpellPower(0),
mPhysicalDefense(0),
mMagicalDefense(0)
{
  for (int ii=0;ii<MAX_NUM_ENHANCEMENTS;++ii)
  {
    mEnhancements[ii] = ID_NONE;
  }
}

cStatsItemMaster::~cStatsItemMaster()
{
  delete [] mWorldModel;
  delete [] mFileName;
  delete [] mIcon;
  delete [] mAttachModel;
  delete [] mEmitterModel;
}

//------------------------------------- cStatsEnhancementMaster
cStatsEnhancementMaster::cStatsEnhancementMaster()
: mID(0),
  mSpellID(ID_NONE),
  mSpellChance(100),
  mAttachFXID(ID_NONE),
  mDeliveryType(ENH_DELIVERY_EQUIP),
  mSlotType(ENH_SLOT_NUM_TYPES),
  mSlotUsageCost(0)
{
  for (int ii=0; ii<EQUIP_UNEQUIPPED; ++ii)
  {
    mEquipSlots[ii] = false;
  }
}

void 
cStatsEnhancementMaster::AppendPropertyString(cGameObject *pObj,SyString &str) const
{
  if (mSpellID != ID_NONE)
  {
    const cSpellMaster* pSpell;
    Titan *pTitan = pObj->GetTitan();
    pSpell = pTitan->GetDatabaseSys()->GetSpellMaster(mSpellID);

    // sgc: T4 still isn't ready (the content path is unclear.)
    // until that gets hammered out, i'm forced to use literals in game code.
    // TODO: swap out "setFinal" with "setContext" and use id from database

    switch (mDeliveryType)
    {
      case cStatsEnhancementMaster::ENH_DELIVERY_ATTACK:

        pTitan->T4SetFinal(0,"When Attacking");

        break;

      case cStatsEnhancementMaster::ENH_DELIVERY_DEFEND:

        pTitan->T4SetFinal(0,"When Defending");

        break;

      case cStatsEnhancementMaster::ENH_DELIVERY_EQUIP:
        pTitan->T4SetFinal(0,"When Equipped");
        break;

      default:
        SyAssertf(false, "Bad enhancement delivery type");
        break;
    }
    pSpell->AppendPropertyString(pObj,str);
  }
};

//------------------------------------- cItem

cItem::cItem() :
  mpMaster(NULL),
  mQuantity(1),
  mEquipSlot(EQUIP_UNEQUIPPED),
  mNoDrop(false)

{
  InitPropObject( ITEM_CLASSID );

  for (int ii=0;ii<cStatsEnhancementMaster::ENH_SLOT_NUM_TYPES;++ii)
  {
    mEnhancementSlots[ii] = ID_NONE;
  }
}

cItem::~cItem()
{

}


int           
cItem::InitPropClass()
{
  SyPropClassID ClassID = ITEM_CLASSID;

/* Add the class */

  AddClass( ClassID, 
            "cItem", 
            Creator, 
            ClassID, 
            0 ); 

  AddStringProperty(ClassID,PropId_MasterName,SyMemberOffset(cItem,mMasterName),"mMasterName");

  AddInt32Property(ClassID,PropId_Quantity,SyMemberOffset(cItem,mQuantity),"mQuantity");

  SyPropEnum *propEnum;
  AddEnumProperty(ClassID,PropId_EquipSlot,SyMemberOffset(cItem,mEquipSlot),"mEquipSlot",&propEnum);
  propEnum->Add(EQUIP_MELEE,"MELEE");
  propEnum->Add(EQUIP_RANGED,"RANGED");
  propEnum->Add(EQUIP_HEAD,"HEAD");
  propEnum->Add(EQUIP_SHOULDERS,"SHOULDERS");
  propEnum->Add(EQUIP_FEET,"FEET");
  propEnum->Add(EQUIP_LEGS,"LEGS");
  propEnum->Add(EQUIP_BELT,"BELT");
  propEnum->Add(EQUIP_L_RING,"LRING");
  propEnum->Add(EQUIP_R_RING,"RRING");
  propEnum->Add(EQUIP_UNEQUIPPED,"UNEQUIPPED");

  return 0;
}

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

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

  return(pObject);
}

void
cItem::Init(cDatabaseSys *db)
{
  if (mMasterName.AsChar()[0] == '\0')
  {
    SyAssert(0);
    return;
  }

  tGameID mMasterID = SyHashResourceID(mMasterName.AsChar());
  GAME_ASSERT(ERROR_DESIGN, db->GetItemMaster(mMasterID)!=NULL, "Could not find item master '%s' to create item", mMasterName.AsChar());

  SetMaster(db->GetItemMaster(mMasterID));
}

void 
cItem::SetMaster(const cStatsItemMaster *master)
{
  if (master == NULL)
  {
    return;
  }

  mpMaster = master;
  mMasterName.Init(master->mID.GetName());
}


void 
cItem::SetQuantity(int quantity)
{
  SyAssertf(quantity > 0,"Bad quantity");
  mQuantity = quantity;
}

bool 
cItem::Merge(cItem *other)
{
  if (other->GetMaster() == GetMaster())
  {
    mQuantity += other->GetQuantity();
    return true;
  }
  return false;
}

void cItem::Unequip(cGameObject* pOwner)
{
  // if pOwner is NULL, we're being added to an inventory and 
  // don't need to remove spells, just initialize the equip slot

  // remove enhancement based spells

  const cStatsEnhancementMaster* pEnh;
  const cSpellMaster* pSpell;


  if (pOwner)
  {
    if (mpMaster)
    {
      for (int i=0; i<mpMaster->MAX_NUM_ENHANCEMENTS; ++i)
      {
        pEnh = pOwner->GetTitan()->GetDatabaseSys()->GetEnhancementMaster(mpMaster->mEnhancements[i]);

        if (pEnh && pEnh->mSpellID != ID_NONE)
        {
          switch (pEnh->mDeliveryType)
          {
            case cStatsEnhancementMaster::ENH_DELIVERY_ATTACK:
              pOwner->GetStats()->RemoveCondition("Delayed Attack Spell", pEnh->mSpellID, pOwner->GetID(), mpMaster->mID.GetID());
              break;

            case cStatsEnhancementMaster::ENH_DELIVERY_DEFEND:
              pOwner->GetStats()->RemoveCondition("Delayed Defend Spell", pEnh->mSpellID, pOwner->GetID(), mpMaster->mID.GetID());
              break;

            case cStatsEnhancementMaster::ENH_DELIVERY_EQUIP:
              pSpell = pOwner->GetTitan()->GetDatabaseSys()->GetSpellMaster(pEnh->mSpellID);
              SyAssert(pSpell != NULL);

              if (pSpell)
              {
                pSpell->EndSpellEffects(pOwner, NULL, mpMaster->mID.GetID());
              }
              break;

            default:
              SyAssertf(false, "Bad enhancement delivery type");
              break;
          }
        }
      }
    }

    for (int i=0; i<cStatsEnhancementMaster::ENH_SLOT_NUM_TYPES; ++i)
    {
      pEnh = pOwner->GetTitan()->GetDatabaseSys()->GetEnhancementMaster(mEnhancementSlots[i]);

      if (pEnh && pEnh->mSpellID != ID_NONE)
      {
        GAME_ASSERT(ERROR_DESIGN, EQUIP_MELEE == mEquipSlot, "Non weapon has slot-based enhancements"); 

        switch (pEnh->mDeliveryType)
        {
        case cStatsEnhancementMaster::ENH_DELIVERY_ATTACK:
          pOwner->GetStats()->RemoveCondition("Delayed Attack Spell", pEnh->mSpellID, pOwner->GetID(), mpMaster->mID.GetID());
          break;

        case cStatsEnhancementMaster::ENH_DELIVERY_DEFEND:
          pOwner->GetStats()->RemoveCondition("Delayed Defend Spell", pEnh->mSpellID, pOwner->GetID(), mpMaster->mID.GetID());
          break;

        case cStatsEnhancementMaster::ENH_DELIVERY_EQUIP:
          pSpell = pOwner->GetTitan()->GetDatabaseSys()->GetSpellMaster(pEnh->mSpellID);
          SyAssert(pSpell != NULL);

          if (pSpell)
          {
            pSpell->EndSpellEffects(pOwner, NULL, mpMaster->mID.GetID());
          }
          break;

        default:
          SyAssertf(false, "Bad enhancement delivery type");
          break;
        }
      }
    }
  }

  mEquipSlot = EQUIP_UNEQUIPPED;
}

bool 
cItem::Unequip(cGameObject* pOwner, eEquipSlot slot) // if we're in the equip slot, equip it
{
  if (mEquipSlot == slot)
  {
    Unequip(pOwner);
    return true;
  }

  return false;
}

bool
cItem::CanEquip(eEquipSlot slot)
{
  SyAssertf(mpMaster!=NULL,"Master Not Set");

  if (slot == EQUIP_UNEQUIPPED)
  {
    return true;
  }
  if ((int)mpMaster->mType == (int)slot)
  {
    return true;
  }
  return false;
}

bool 
cItem::Equip(cGameObject* pOwner, eEquipSlot slot)
{
  // up to inventory to make sure there aren't two items in the same slot
  if (!CanEquip(slot))
  {
    return false;
  }

  mEquipSlot = slot;

  SyAssert(pOwner != NULL && mpMaster!=NULL);

  // cast enhancement based spells
  const cStatsEnhancementMaster* pEnh;
  const cSpellMaster* pSpell;

  if (pOwner)
  {
    if (mpMaster)
    {
      for (int i=0; i<mpMaster->MAX_NUM_ENHANCEMENTS; ++i)
      {
        pEnh = pOwner->GetTitan()->GetDatabaseSys()->GetEnhancementMaster(mpMaster->mEnhancements[i]);
        
        if (pEnh && pEnh->mSpellID != ID_NONE)
        {
          GAME_ASSERT(ERROR_DESIGN, pOwner->GetTitan()->GetDatabaseSys()->GetSpellMaster(pEnh->mSpellID)!=NULL, "Could not find spell for enhancement %s", pEnh->mID.GetName());

          switch (pEnh->mDeliveryType)
          {
            case cStatsEnhancementMaster::ENH_DELIVERY_ATTACK:
              if (pOwner->GetTitan()->Random(1, 100) <= pEnh->mSpellChance)
              {
                pOwner->GetStats()->AddCondition("Delayed Attack Spell", pEnh->mSpellID, pOwner->GetID(), mpMaster->mID.GetID(), 0.0f, ID_NONE, pEnh->mSpellID);
              }
              break;

            case cStatsEnhancementMaster::ENH_DELIVERY_DEFEND:
              if (pOwner->GetTitan()->Random(1, 100) <= pEnh->mSpellChance)
              {
                pOwner->GetStats()->AddCondition("Delayed Defend Spell", pEnh->mSpellID, pOwner->GetID(), mpMaster->mID.GetID(), 0.0f, ID_NONE, pEnh->mSpellID);
              }
              break;

            case cStatsEnhancementMaster::ENH_DELIVERY_EQUIP:
              GAME_ASSERT(ERROR_DESIGN, pEnh->mSpellChance == 100, "Enhancement using equip delivery type has less than 100 percent spell chance");
              pSpell = pOwner->GetTitan()->GetDatabaseSys()->GetSpellMaster(pEnh->mSpellID);
              SyAssert(pSpell != NULL);

              if (pSpell)
              {
                pSpell->CastSpell(pOwner, pOwner, NULL, NUM_COMBOS, mpMaster->mID.GetID());
              }
              break;

            default:
              SyAssertf(false, "Bad enhancement delivery type");
              break;
          }
        }
      }
    }

    for (int i=0; i<cStatsEnhancementMaster::ENH_SLOT_NUM_TYPES; ++i)
    {
      pEnh = pOwner->GetTitan()->GetDatabaseSys()->GetEnhancementMaster(mEnhancementSlots[i]);

      if (pEnh && pEnh->mSpellID != ID_NONE)
      {
        GAME_ASSERT(ERROR_DESIGN, EQUIP_MELEE == mEquipSlot, "Non weapon has slot-based enhancements"); 
        GAME_ASSERT(ERROR_DESIGN, pOwner->GetTitan()->GetDatabaseSys()->GetSpellMaster(pEnh->mSpellID)!=NULL, "Could not find spell for enhancement %s", pEnh->mID.GetName());

        switch (pEnh->mDeliveryType)
        {
        case cStatsEnhancementMaster::ENH_DELIVERY_ATTACK:
          if (pOwner->GetTitan()->Random(1, 100) <= pEnh->mSpellChance)
          {
            pOwner->GetStats()->AddCondition("Delayed Attack Spell", pEnh->mSpellID, pOwner->GetID(), mpMaster->mID.GetID(), 0.0f, ID_NONE, pEnh->mSpellID);
          }
          break;

        case cStatsEnhancementMaster::ENH_DELIVERY_DEFEND:
          if (pOwner->GetTitan()->Random(1, 100) <= pEnh->mSpellChance)
          {
            pOwner->GetStats()->AddCondition("Delayed Defend Spell", pEnh->mSpellID, pOwner->GetID(), mpMaster->mID.GetID(), 0.0f, ID_NONE, pEnh->mSpellID);
          }
          break;

        case cStatsEnhancementMaster::ENH_DELIVERY_EQUIP:
          GAME_ASSERT(ERROR_DESIGN, pEnh->mSpellChance == 100, "Enhancement using equip delivery type has less than 100 percent spell chance");
          pSpell = pOwner->GetTitan()->GetDatabaseSys()->GetSpellMaster(pEnh->mSpellID);
          SyAssert(pSpell != NULL);

          if (pSpell)
          {
            pSpell->CastSpell(pOwner, pOwner, NULL, NUM_COMBOS, mpMaster->mID.GetID());
          }
          break;

        default:
          SyAssertf(false, "Bad enhancement delivery type");
          break;
        }
      }
    }
  }

  return true;
}

tGameID 
cItem::GetBaseEnhancement(int index) const
{
  if (index < 0 ||
      index >= cStatsItemMaster::MAX_NUM_ENHANCEMENTS ||
      !mpMaster)
  {
    return ID_NONE;
  }

  return mpMaster->mEnhancements[index];
}

tGameID 
cItem::GetEnhancementInSlot(cStatsEnhancementMaster::SlotType slot) const
{
  if (slot >= cStatsEnhancementMaster::ENH_SLOT_NUM_TYPES)
  {
    return ID_NONE;
  }

  return mEnhancementSlots[slot];
}

bool    
cItem::SetEnhancementInSlot(cStatsEnhancementMaster::SlotType slot, tGameID id)
{
  if (slot >= cStatsEnhancementMaster::ENH_SLOT_NUM_TYPES)
  {
    return false;
  }

  mEnhancementSlots[slot] = id;
  return true;
}

tGameID 
cItem::GetAttachFX(cGameObject* pOwner) const
{
  if (!mpMaster)
  {
    return ID_NONE;
  }

  tGameID fxID = ID_NONE;

  if (ID_NONE != mEnhancementSlots[cStatsEnhancementMaster::ENH_SLOT_PROC])
  {
    const cStatsEnhancementMaster* pEnh;

    pEnh = pOwner->GetTitan()->GetDatabaseSys()->GetEnhancementMaster(mEnhancementSlots[cStatsEnhancementMaster::ENH_SLOT_PROC]);

    if (pEnh)
    {
      fxID = pEnh->mAttachFXID;
    }
  }

  return fxID;
}

void
cItem::GetAttachModelFilename(cGameObject* pOwner, SyString* pName, SyString* pLeftName) const
{
  *pName = "";

  if (pLeftName)
  {
    *pLeftName = "";
  }

  if (pOwner->GetType() == cGameObject::OBJ_PLAYER)
  {
    const SyVector<cStatsEnhancementComboModel*>& models = pOwner->GetTitan()->GetDatabaseSys()->GetEnhancementComboModels();
    cStatsEnhancementComboModel* pCombo;
    bool bMatch;

    for (int i=0; i<models.Size(); ++i)
    {
      pCombo = models(i);

      bMatch = true;

      for (int j=0; j < cStatsEnhancementMaster::ENH_SLOT_NUM_TYPES; ++j)
      {
        bMatch = bMatch && (ID_NONE == pCombo->mSlots[j] || pCombo->mSlots[j] == mEnhancementSlots[j]);
      }

      if (bMatch)
      {
        cStatsCharacter* pOwnerStats = static_cast<cStatsCharacter*>(pOwner->GetStats());
        tGameID modelDataID = ID_NONE;
        tGameID leftModelDataID = ID_NONE;

        if (strcmp(pOwnerStats->GetMasterName(), "Brute") == 0)
        {
          modelDataID = pCombo->mModelDataIDs[0];
        }
        else if (strcmp(pOwnerStats->GetMasterName(), "Mage") == 0)
        {
          modelDataID = pCombo->mModelDataIDs[1];
        }
        else if (strcmp(pOwnerStats->GetMasterName(), "Scout") == 0)
        {
          modelDataID = pCombo->mModelDataIDs[2];
          leftModelDataID = pCombo->mModelDataIDs[3];
        }
  
        const cStatsModelData* pModelData = pOwner->GetTitan()->GetDatabaseSys()->GetModelData(modelDataID);
        const cStatsModelData* pLeftModelData = pOwner->GetTitan()->GetDatabaseSys()->GetModelData(leftModelDataID);

        if (pModelData)
        {
          *pName = pModelData->mFileName;
        }

        if (pLeftModelData && pLeftName)
        {
          *pLeftName = pLeftModelData->mFileName;
        }
        return;
      }
    }
  }

  if (mpMaster)
  {
    *pName = mpMaster->mAttachModel;
  }
}

void
cItem::GetEmitterModelFilename(cGameObject* pOwner, SyString* pName, SyString* pLeftName) const
{
  SyAssert(pName!=NULL);

  *pName = "";

  if (pLeftName)
  {
    *pLeftName = "";
  }

  GetAttachModelFilename(pOwner, pName, pLeftName);

  SyPathname path;
  SyString emitterName = path.Base();
  emitterName += "_emitter";
  path.Base(emitterName);
  *pName = path.Full();

  if (pLeftName)
  {
    path.Full(*pLeftName);
    emitterName = path.Base();
    emitterName += "_emitter";
    path.Base(emitterName);
    *pLeftName = path.Full();
  }
}

/*
void    
cItem::CalcEffectiveEnhancements(cDatabaseSys *db)
{
  // these are the enhancements already on the item

  for (int index=0;index<cStatsItemMaster::MAX_NUM_ENHANCEMENTS;++index)
  {
    if (mpMaster->mEnhancements[index] != ID_NONE)
    {
      mEffectiveEnhancements[index] = mpMaster->mEnhancements[index];
    }
    else
    {
      mEffectiveEnhancements[index] = mEnhancements[index];
    }
  }
  const SyMap<int, cStatsEnhancementMaster *> &map = 
                  db->GetPrioritizedEnhancementMap();

  // go backwards through enhancements to see if there is a morph
  for (int index = map.Begin(); index !=map.End();index = map.Next(index))
  {
    cStatsEnhancementMaster *master = map(index);
    MorphEnhancement(master);
  }
}

void
cItem::MorphEnhancement(cStatsEnhancementMaster *master)
{
  // make sure this one can be morphered

  if (master->mMorphPrereq[0] == 0 && master->mMorphPrereq[1] == 0 && master->mMorphPrereq[2]==0)
  {
    return;
  }

  tGameID  TestEnhancements[cStatsItemMaster::MAX_NUM_ENHANCEMENTS]; // after morphing

  for (int ii=0;ii<cStatsItemMaster::MAX_NUM_ENHANCEMENTS;++ii)
  {
    TestEnhancements[ii] = mEffectiveEnhancements[ii];
  }

  for (int ii=0;ii<cStatsEnhancementMaster::NUM_MORPH_PREREQS;++ii)
  {
    if (master->mMorphPrereq[ii] != NULL)
    {
      bool found = false;
      for (int jj=0;jj<cStatsItemMaster::MAX_NUM_ENHANCEMENTS;++jj)
      {
        if (TestEnhancements[jj] == master->mMorphPrereq[ii])
        {
          TestEnhancements[ii] = 0;
          found = true;
          break;
        }
      }
      if (!found) return;
    }
  }
  
  // all prereqs found, convert it over
  for (int ii=0;ii<cStatsItemMaster::MAX_NUM_ENHANCEMENTS;++ii)
  {
    mEffectiveEnhancements[ii] = TestEnhancements[ii];
  }
  
  for (int ii=0;ii<cStatsItemMaster::MAX_NUM_ENHANCEMENTS;++ii)
  {
    if (mEffectiveEnhancements[ii] == 0)
    {
      mEffectiveEnhancements[ii] = master->mID.GetID();
      return;
    }
  }
}
*/

bool  
cItem::GetPropertyString(cGameObject *pOwner,SyString &ans) const
{
  const cStatsEnhancementMaster* pEnh;

  if (mpMaster)
  {
    for (int index=0;index<cStatsItemMaster::MAX_NUM_ENHANCEMENTS;++index)
    {
      if (ID_NONE != mpMaster->mEnhancements[index])
      {
        pEnh = pOwner->GetTitan()->GetDatabaseSys()->GetEnhancementMaster(mpMaster->mEnhancements[index]);
        GAME_ASSERT(ERROR_DESIGN, pEnh != NULL, "Could not find enhancement for item master '%s'", mpMaster->mID.GetName());

        if (pEnh)
        {
          pEnh->AppendPropertyString(pOwner,ans); 
        }
      }
    }
  }

  for (int index=0;index<cStatsEnhancementMaster::ENH_SLOT_NUM_TYPES;++index)
  {
    if (ID_NONE != mEnhancementSlots[index])
    {
      pEnh = pOwner->GetTitan()->GetDatabaseSys()->GetEnhancementMaster(mEnhancementSlots[index]);
      GAME_ASSERT(ERROR_DESIGN, pEnh != NULL, "Could not find enhancement in slot %d for item with master '%s'", index, mpMaster->mID.GetName());

      if (pEnh)
      {
        pEnh->AppendPropertyString(pOwner,ans); 
      }
    }
  }

  return true;
}

void   
cItem::T4_SetContext_Name(cGameObject* pOwner,char8 slot)
{
  uint32 id = GetMaster()->mNameString;
  if (id == 0)
  {
    static const int unknown = SyHashResourceID("ITEM_UNKNOWN");
    pOwner->GetTitan()->T4SetContext( slot,unknown);
    return;
  }
  pOwner->GetTitan()->T4SetContext( slot,id,T4::UNKNOWN_SEX,GetQuantity());
};

void   
cItem::T4_SetContext_Description(cGameObject* pOwner,char8 slot)
{
  uint32 id = GetMaster()->mDescriptionString;
  if (id == 0)
  {
    static const int unknown = SyHashResourceID("ITEM_NO_DESCRIPTION");
    pOwner->GetTitan()->T4SetContext( slot,unknown);
    return;
  }
  pOwner->GetTitan()->T4SetContext( slot,id,T4::UNKNOWN_SEX,GetQuantity());
}

cStatsEnhancementComboModel::cStatsEnhancementComboModel()
{
  for (int i=0; i<cStatsEnhancementMaster::ENH_SLOT_NUM_TYPES; ++i)
  {
    mSlots[i] = ID_NONE;
  }

  for (int i=0; i<NUM_MODEL_IDS; ++i)
  {
    mModelDataIDs[i] = ID_NONE;
  }
}


//----------------------------------------- Functions Declarations
void 
RegPropClasses_Item()
{
  cItem::InitPropClass();
}
// EOF
