/*
 * psitem.h
 *
 * Copyright (C) 2003 Atomic Blue (info@planeshift.it, http://www.atomicblue.org)
 *
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation (version 2 of the License)
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 */

#ifndef __PSITEM_H__
#define __PSITEM_H__

#include <csutil/array.h>
#include <csgeom/vector3.h>
#include "util/stringarray.h"
#include "util/psconst.h"

#include "../iserver/idal.h"

#include "util/poolallocator.h"
#include "psskills.h"
#include "psitemstats.h"
#include "util/scriptvar.h"
#include "util/gameevent.h"
#include "gem.h"

class csMutex;
class psString;

// Note: The following 2 options require a clean rebuild when changed.

/// Enable this to be notified of all item saving activity
#define SAVE_DEBUG 0

/// Enable this to keep track of the initial caller of Save() (when used with SAVE_DEBUG, will print full trace)
#define SAVE_TRACER 0


/// These two define how long an item will stay in the world before being auto-removed, in seconds.
#define REMOVAL_INTERVAL_RANGE     300
#define REMOVAL_INTERVAL_MINIMUM  1200

/// This indicates that the item has been deleted from the database and should not be updated
#define  ID_DONT_SAVE_ITEM 0xffffffff   

/// The crafter ID field contains the id of the character that made this item
#define PSITEM_FLAG_CRAFTER_ID_IS_VALID 0x00000001
/// The guild ID field contains the id of a guild that certified this item
#define PSITEM_FLAG_GUILD_ID_IS_VALID   0x00000002
/// This item uses its own unique psItemStats data (in the database), and modifications to the data are OK as they will reflect solely on this item
#define PSITEM_FLAG_UNIQUE_ITEM         0x00000004
/** This item is solely based on a basic item template.  It has no modifiers.
* When this flag is set and all effects on this item expire, if the current_stats is not the same as the base_stats pointer,
*  then the object referenced by current_stats is deleted and current_stats is set to point at the base_stats pointer.
*  This allows item stats for base items to use a single area in memory most of the time.  When effects are applied a new copy
*    of the base stats can be made and modified with the effect data.  When all effects fade the new copy can be released back to
*    the pool.
* This flag is not intended to be directly manipulated.  All objects starting from a basic item will have this set to begin with.
*   If a modifier is applied, or if the item is forced to take over unique status, this flag is cleared.
*/
#define PSITEM_FLAG_USES_BASIC_ITEM     0x00000008
/// Flags used for glyphs
#define PSITEM_FLAG_PURIFIED            0x00000010
#define PSITEM_FLAG_PURIFYING           0x00000020

/// Flags for locked status (as in a locked container)
#define PSITEM_FLAG_LOCKED              0x00000040

#define PSITEM_FLAG_LOCKABLE            0x00000080

/// Flag for an un-pickupable item (remains fixed)
#define PSITEM_FLAG_NOPICKUP            0x00000100

/// Flag defines any item as a key to a locked object
#define PSITEM_FLAG_KEY                 0x00000200

/// Transient items are removed from the world approx 3hrs after creation, if not picked up by someone
#define PSITEM_FLAG_TRANSIENT           0x00000400


#define KEY_SKELETON (unsigned int)-1

typedef unsigned int PSITEM_FLAGS;

/// The maximum number of modifiers that can be applied to a single instance of an item on top of the base item
#define PSITEM_MAX_MODIFIERS    10

class psContainerIterator;
class psItem;
class psCharacter;
class gemItem;
class psSectorInfo;
struct psItemCategory;
class psScheduledItem;
class psWorkGameEvent;



///** A list of the possible errors when placing an item in a container.
enum PS_CONTAINER_ERROR
{
    PS_CONT_OK = 0,
    PS_CONT_ERR_OUT_RANGE,
    PS_CONT_ERR_NO_ITEM,
    PS_CONT_ERR_SELF,
    PS_CONT_ERR_TO_BIG,
    PS_CONT_ERR_NOT_OWNER,
    PS_CONT_ERR_NO_STACK,
    PS_CONT_REMAINDER
};

enum ITEM_ORIGIN
{
   ITEM_DB_LOAD = 1, //loaded from DB
   ITEM_CLONE,   //created from a Copy(), as in purchasing something
   ITEM_CACHE,   //created by CacheManager
   ITEM_MONEY,   //created by psserverchar::MakeMoneyItem
   ITEM_BASIC   //created by psitemstats::instantiateBasicItem
};


/** This class iterates through sub objects in a container object.
*
*
*/
class psContainerIterator
{
public:
    psContainerIterator(psItem *container);
    ~psContainerIterator();
    bool HasNext();
    psItem *Next();

    void UseContainerItem(psItem *container);

private:
    psItem *current_container;
    unsigned int next_position;
};


/**
* This class holds a shared reference to an object.
* Any other class needing to hold a ptr to a psItem
* for >1 function should use a csRef of this and check for NULL
* before use.
*/
struct psItemSafe : public csRefCount
{
    psItem *item;

    psItemSafe()             { item = NULL; }
    psItemSafe(psItem *init) { item = init; }
};

#define  ITEM_DECAY_FACTOR_PARRY   0.75F  // If the target parries the blow, how much is his weapon damaged by blocking the attacker
#define  ITEM_DECAY_FACTOR_BLOCKED 1.25F   // If the target parries the blow, how much is the attacker's weapon damaged by getting blocked

/** This class embodies item instances in the game.
* Every item that can be picked up, dropped, traded, equiped, bought or sold is a psitem.
* 
* This class has some specific design philosophy.  
*
* First, each item instance has a unique 32 bit identifier.  The database ensures that there can be only one entry
*  per item identifier.  This means if an item is truly duped it will not persist between world resets since the next
*  load from the database will find only one entry for that item.
* In order to make this mean anything, certain situations must be carefully coded to avoid bypassing this protection.
*  In any case where 1 item becomes multiple items (such as splitting a stack) the logic should be well defined and located
*  in just one place (preferably within the psItem class).  If a bug does arise, it should require a fix to a single location.
* Any instance where an item is legitimately duped will need to do more work to actually copy the item.  The code will be required
*  to basically reconstruct the item from the ground up.  The new item will be a truly new item.  Do not take this lightly.
*
*  ----The only time an item instance should be duplicated is for Game Master level functions.----
*  
* Items are built from statistic entries (item stats).  Most items will have a base item stat entry and 0 or more item stat
*  entries that act as modifiers.  Modifiers are permanent additions to an item instance.  For example, you may be able to buy
*  a normal 'short sword' from a merchant.  This would be the base stat of the item.  If you use a trade skill to sharpen the
*  short sword, that may add the 'sharpened' modifier to the sword.  The resulting item instance will still (and always) have
*  the base stat of 'short sword'.  In addition it will have the modifier of 'sharpened'.  It may gain or lose modifiers through
*  other actions.  Temporary effects such as spells, poisons, etc are NOT modifiers.  They should be handled differently.
* At this time there is no code to handle effects on items, but it should not be difficult to add once the parameters of effects
*  are defined.
* The other type of item instance is a unique item.  Unique items are truely unique.  The unique item has its own entry for its
*  stats in the database.  Only one item instance will ever refer to this entry.  If the item instance is destroyed, so is the entry.
*  In some situations the item stats may be altered - either through an extremely difficult quest, or GM intervention.  These alterations
*  to the stats will alter the entries of the unique item stats.  In addition, normal modifiers may also be applied.
*
* ----Unique items should be very difficult to obtain as they are a larger drain on server resources----
*
* An item instance has four persistant-state related operations:
* Load() - Translates data from a database item instance entry into a usable psItem
* Save() - Saves a psItem.  This creates a new Unique Identifier (UID) for the item instance if needed (if the UID is 0).
* DeleteItem() - (Called from psCharacterLoader) Destroys the item instance entry from the database.  The psItem entry in memory should also
*  be destroyed promptly.
*
* Hierarchy:
*  psItem uses a fairly straightforward hierarchy system.  psItems may contain other psItems.  The maximum amount of items a psItem may contain
*  is defined by the compile time constant PSITEM_MAX_CONTAINER_SLOTS.  Hierarchy linkage is stored in both the child and the parent in memory.
*  Only container items may store other items.  Container items MUST NOT be stackable.  The base stats (and modifiers) control wether an item instance
*  is a container or not.
*  The following functions should be used to add and remove items from containers, and ONLY these functions.  These functions should contain all logic
*   about ensuring hierarchy linkage and constraints (size, weight, etc) and updating data throughout the hierarchy (such as sum weight propagated to
*   parent containers)
*    bool TakeOutOfContainer();
*    bool AddItemToContainer(psItem *addme);
*    bool AddItemToContainer(psItem *addme,unsigned int slot);
*
* Weight Rules:
*   TODO: Containers will have a maximum carrying weight.
*   Weight of stacks is a the count of the stack multiplied by the weight of the base item stat.
*   
* Size Rules:
*   Size is single number (unsigned short) that relates to the length of the longest dimension measured in centimeters.
*   Stacks do not affect size.
*   Containers have a maximum content size.  Containers themselves have a size just like every other item as well, this tells wether containers can fit inside other containers.
*   65535 = infinite size. Items with 65535 cannot be put into any containers.  Containers with 65535 max content size can contain any item except items with 65535 size.
*
*  TODO: Add effect data - such as spell effects or poision or whatever.
*  TODO: Merge various mutually exclusive stats into unions to save space. The container stats and modifiers may be able to be merged (or maybe not).
*/
class psItem : public iScriptableVar, public iDeleteObjectCallback
{
public:

    /** Constructs a psItem.  Does not assign a UID. 
     * After construction the caller should set the location in the world or place the item into a container
     * with the parent's AddItemToContainer() member function.  It should then call Save() to save the instance
     * to the database.
     */
    psItem ();

    /** Destructs the psItem, and, if this is a container, all contained items. Does NOT remove any items from the database.
     */
    virtual ~psItem ();

    /** Return a string identifying this object as an Item
     */
    virtual const char * GetItemType() { return "Item"; }

    gemItem* GetGemObject() { return gItem; }
    void SetGemObject( gemItem* object );

    psItemSafe* GetSafeReference() { return self_reference; }

    uint32 GetPatternID() { return patternID; }
    psWorkGameEvent* GetTransformationEvent() { return transformationEvent; }
    void SetTransformationEvent(psWorkGameEvent* t) { transformationEvent = t; }
	/// Identify what methods were used to create this object for debugging
    /// rather religious looking
	void SetCreator(int origin){ creator=origin;}
	char * GetCreatorName();

    /// Handles deleted gem objects.
    virtual void DeleteObjectCallback(iDeleteNotificationObject * object);

private:
    /// The 32 bit Unique Identifier of this item.
    uint32 uid;
	/// Debug info, used to determine where this came from
    int creator;
    /// If set, this item is used as a pattern and this is the ID number of that pattern.
    int patternID;

    /// This flag is true when the item cannot be moved around in inventory, during repairs for example.
    bool item_in_use;

    /// If set, this is a pointer to the item that is currently being trade skill transformed.
    psWorkGameEvent* transformationEvent;

    /// Container specific data
    struct
    {
        /// The array of psItems contained in this psItem.  Only valid if this is a container.  NULL for slot empty.
        psItem *contained_item_ptr[PSITEM_MAX_CONTAINER_SLOTS];
    } container_data;

    /// Location in the game world. Only valid if this item is not being carried by a player (directly or indirectly)
    struct
    {
        psSectorInfo *loc_sectorinfo;
        float loc_x,loc_y,loc_z;
        float loc_yrot;
    } location;

    /// The total weight of this item and all contained items.  Adjusted during AddItemToContainer(), TakeOutOfContainer() and RecalculateFullWeight()
    float sum_weight;

    /// 0% means item does not reduce decay at all.  100% means item does not decay.
    float decay_resistance; // percent 3.2

    /// Current quality of item, value between 0 and 100.
    float item_quality;
    /// Original quality of item, used to detect save requirements
    float item_quality_original;

    /// The number of items in this stack if this is a stackable item.
    unsigned short stack_count;


protected:
    /** Flags for this item instance.  Flags are described at the top of this file.
     * Note that psItemStats also has flags that are stored separately.
     *  If you're looking for a flag that you think should be here, check psItemStats to be sure
     *  it's not a stat flag instead of an instance flag.
     *  Flags are indicated in the database as text, e.g. "NOPICKUP" in the flags column
     */
    PSITEM_FLAGS flags;
private:
    /** This field stores the UID of the character that crafted this item if it was crafted.
     * In this way we can add recognition to crafters as their items traverse the game.
     */
    unsigned int crafter_id;
    /** This field stores the guild UID of a guild that has CERTIFIED this item.
     * The concept of this is that a guild as an entity can certify certain works from any crafter.
     * This gives the ability to mark items after some kind of inspection through a method not based
     * on guild membership.  I.E. a crafter in a guild that crafts an item will not automatically have
     * his wares marked with guild id.  Rather someone of sufficient rank will need to mark the item.
     */
    unsigned int guild_id;

    /* Container Hierarchy Data
     *
     *  The following field allows objects-within-objects (containers) to be described.
     *
     */
    /// Points to the object that contains this object, or NULL if there is none.
    psItem *container_parent;


    /** Dual use.  Indicates either the slot within the parent item if contained, or the slot in the player's inventory/equipment/bulk if appropriate.
     * If the item is on the ground in the world and not inside of a container item this field should be ignored.
     */
    unsigned int loc_in_parent;


    /** Pointer to the psCharacter that holds this item
     * The character can hold the item either directly in their inventory/bank/equipment or in a container they hold.
     * NULL if this item is on the ground or in another item that is on the ground.
     *
     */
    psCharacter *owning_character;
    
    /** The owner of the item may not be infact online so have to store the ID since the
        above may be undefined in some cases. 
    */    
    unsigned int owningCharacterID;

    /** The basic stats of this item.
     * This can point to a common shared entry from the basic stats list, or a unique entry.  
     * Check for the PSITEM_FLAG_UNIQUE_ITEM flag to see which.
     */
    psItemStats *base_stats;
    /** The current stats of this item.  This value should be used for pretty much everything.
     * Initially this points to the same entry as the base_stats.  If modifiers or effects are applied
     * this will point to an allocated temporary stats entry that contains the result of all modifiers
     * and effects on top of the base.
     */
    psItemStats *current_stats;
    /** The list of modifiers for this item. 
     * We could keep just the base and current, but then we wouldn't be able to track what kinds of modifiers
     * had been applied to this item instance, and it would be difficult to find where to add the modifiers in
     * the database.
     */
    psItemStats *modifiers[PSITEM_MAX_MODIFIERS];

    // Lock stuff
    unsigned int lockStrength;
    PSSKILL lockpickSkill;

    // A key can open multiple locks
    csArray<unsigned int> openableLocks;

    psScheduledItem* schedule;

    /** Saves this item to the database. Generates an item ID if necessary.
     * This creates a new UID for the item and creates appropriate database entries if necessary.
     * Otherwise it updates the existing entry.
     * This calls a database function directly.
     */
    void Commit();

    /**
     * ItemQuality for items that decay changes so often, writing every change to the db
     * is overly onerous.  Also, any loss of data due to server crashes benefits the player
     * rather than hurting him, so it isn't too bad if it is never updated.  This function
     * is called by the dtor to make sure quality is only updated minimal times.
     */
    void UpdateItemQuality(uint32 id, float qual);

    /// Commit() is called primarily from psDelaySave::Trigger()
    friend class psDelaySave;

public:

    /** Loads data from a database row into the current psItem.
     * This is used only by the character loader.
     *  On return parentid is set to the UID of the parent item.  UID 0 is reserved for no parent.
     */
    virtual bool Load(iResultRow& row,uint32 &parentid);

    /** Queues this item to be saved to the database.  (DB action will be executed after 500ms)
     *  Call this after EVERY change of a persistant property, and the system
     *  will ensure that any sequence of changes will result in only one DB hit.
     */
    void Save();

    /// Saves this item now if it hasn't been saved before, to force the generation of a UID
    void ForceSaveIfNew();

    /** Flags that this item has been fully loaded, and is allowed to be saved on changes.
     *  This MUST be called after this item is constructed AND it's initial
     *  properties are set.  (includes normal loading, InstantiateBasicItem(), etc.)
     */
    void SetLoaded() { loaded = true; }

    /** Check to see if the character meets the requirements for this item.
     */
    bool CheckRequirements( psCharacter* character, csString& resp );

    /** Database assist function.  Fills the passed psStringArray with values for the fields in the database.
     * Helper function for saving and updating.
     */
    void GetFieldArray(psStringArray& fields);

    /** Returns the UID of this item instance.
     * 0 is reserved for no UID assigned yet.
     * Items are initially created with a UID of 0.  Save() generates a unique item id.
     */
    uint32 GetUID() { return uid; }

    /** Sets the Unique ID of this item. DO NOT CALL THIS.
     * This should ONLY be called internally or from psCharacterLoader.
     */
    void SetUID(uint32 v);

    /// Returns true if the crafter character ID is valid
    bool GetIsCrafterIDValid();
    /// Set wether the crafter ID is valid.  SetCrafterID() sets this to true for you.
    void SetIsCrafterIDValid(bool v);

    /// Returns true if the certifying guild ID is valid
    bool GetIsGuildIDValid();
    /// Set wether the guild ID is valid.  SetGuildID() sets this to true for you.
    void SetIsGuildIDValid(bool v);
    /// Returns the UID for the crafter of this item.  Be sure to check GetIsCrafterIDValid()!

    unsigned int GetCrafterID() { return crafter_id; }
    /// Sets the UID for the cracter of this item.  Generally used immediately after completing the crafting work.
    void SetCrafterID(unsigned int v,bool save);

    /// Returns the UID for the guild who has certified this item.  Be sure to check GetIsGuildIDValid()!
    unsigned int GetGuildID() { return guild_id; }
    /// Sets the UID for the guild certifying this item.
    void SetGuildID(unsigned int v,bool save);

    /// Gets the maximum possible quality of the item by reading the item_stat.
    float GetMaxItemQuality();
    /// Sets the maximum allowed quality of the item by changing the item_stat.
    void SetMaxItemQuality(float v);

    /// Gets the quality of the item.
    float GetItemQuality();
    /// Sets the quality of the item.
    void SetItemQuality(float v,bool save);

    /// Returns item_stats id of repair tool required to fix this item, or 0.
    int GetRequiredRepairTool();
    /// Returns the is_consumed flag of repair tool required to fix this item.
    bool GetRequiredRepairToolConsumed();

    /// Set the decay resistance percentage for the item
    void SetDecayResistance(float v,bool flag);
    /// Set the item decay factor
    void SetItemDecayRate(float v);
    /// Get the item decay factor
    float GetItemDecayRate() { return base_stats->GetDecayRate(); }
    /// Adjust the item quality by an amount of decay
    float AddDecay(float severityFactor);

    bool IsInUse()  { return item_in_use; }
    void SetInUse(bool flag) { item_in_use = flag; }

    /** Returns true if this item is based off of unique statistics.
     * Items with unique statistics will be especially rare.  Some special operations are
     * possible on unique items
     */
    bool GetIsUnique();

    /** Returns true if this item has this modifier in its modifier list.
     * This can be used to make sure an item doesn't get the same modifier multiple times or if an
     * item needs to have a specific modifier to be used for something.
     *
     * TODO: In the future we may expand this to functions that check for "similar" modifiers to a given
     *       modifier.
     */
    bool HasModifier(psItemStats *modifier);
    
    /** Adds a modifier to this item instance if there is space available.
     * Current stats are automaticaly updated.
     *
     * TODO: A DeleteModifier() function may be warranted in the future.
     */
    bool AddModifier(psItemStats *modifier);
    
    /** Returns the modifier at a specific index 0 through PSITEM_MAX_MODIFIERS-1
     *
     */
    psItemStats *GetModifier(int index);

    /** Creates a new blank instance of it's class (i.e. psItem or its subclass) */
    virtual psItem *CreateNew() { return new psItem(); }

    /** Duplicates an item instance.
     *  The parameter is the stack count of the new item.
     *  The return value must be checked for NULL.
     *  The returned item will not have a valid location nor a valid UID.  The location in the world or
     *  on an owning player must be set first, and then Save() called to generate a UID and save to the database.
     */
    virtual psItem *Copy(unsigned short newstackcount);

    /** Copies values of its attributes to item 'target'.
      */
    virtual void Copy(psItem * target);
    
    /** Splits an item instance representing a stack into two smaller stacks.
     *  The parameter is the size of the new stack.
     *  The return value must be checked for NULL.
     *  The returned item will not have a valid location nor a valid UID.  The location in the world or
     *  on an owning player must be set first, and then Save() called to generate a UID and save to the database.
     */
    psItem* SplitStack(unsigned short newstackcount, bool save=true);


    /** Attempts to stack 'stackme' with this item.
     *
     *  If 'stackme' has been fully merged into another stack, returns NULL.
     *  Otherwise returns the part that remained unstacked.
     */
    psItem* CombineStack(psItem *& stackme, bool save=true);


    /** TODO:  Comment me with something more than "Gets the attack animation ID"
     *
     */
    int GetAttackAnimID(psCharacter *pschar,iCelEntity *entity);

    /// Returns the decay value of the item.  See the decay member for a description of what this is.
    float GetDecay();
    /// Sets the decay value of the item.  See the decay member for a description of what this is.
    void SetDecay(float v);

    /// Returns the stack count.  Be sure to call GetIsStackable() first!
    unsigned short GetStackCount() { return stack_count; }
    /** Sets the stack count.  Be sure to call GetIsStackable() first!
     *  Don't call this to try and combine or split stacks!  That logic is done in CombineStack() 
     *   ,FindStackableAndStack() and SplitStack().
     */
    void SetStackCount(unsigned short v, bool save=true);


    /**  Returns a pointer to the character who is holding this item directly or indirectly (including containers and bank slots) or NULL if none.
     * Returns NULL if on the ground or in a container on the ground.
     *
     * This should always be valid since items should be destroyed when the character logs off
     */
    psCharacter *GetOwningCharacter() { return owning_character; }
    /** Get the ID of the owning character.  This is required in cases where the owning 
      * character may not be online and the above pointer is undefined.
      */
    unsigned int GetOwningCharacterID() { return owningCharacterID; }

    /** Alters the owning character of this item and all contained items (if this is a container).  DO NOT USE!
     * This is automatically called from the functions that remove items from inventory (or add items).
     * There shouldn't be any reason to call this from anywhere else, and it can really mess things up if you do!
     */
    virtual void SetOwningCharacter(psCharacter *owner, bool save);

    /// Returns true if this is not a container or if it's a container that's empty.
    bool GetIsContainerEmpty();
    
    /// Returns the total size of the items within a container.
    unsigned short GetContainedSize();

    /// Returns the item that contains this item, or NULL if it's not contained by another item.
    psItem *GetParentItem() { return container_parent; }
    void SetParent( psItem* item );

    /** Returns the UID of the item that contains this item. DO NOT USE!
     * There's no danger in using this, but you probably want to use GetParentItem() instead.
     * The UID isn't really useful outside of the database and maybe sending to clients as an ID.
     */
    uint32 GetParentID() { if (container_parent!=NULL) return container_parent->GetUID(); return 0; }
    
    /** Returns the location of this item in it's parent item or in the players equipment, bulk or bank as appropriate.
     *
     */
    unsigned int GetLocInParent() { return loc_in_parent; }

    /** DO NOT USE!  DO NOT USE! DO NOT USE!
     * This is used at load time and as a helper function for setting the location within the parent AFTER all checks
     * have been performed. It does NOT do two way linkage.
     * Look at TakeOutOfContainer() and AddItemToContainer() to do what you want to do.
     */
    void SetLocInParent(unsigned int location, bool save);
    
    /** Returns the item in the specific slot of this container.
     * Returns NULL if this is not a container or if there is no item in the requested slot.
     */
    psItem *GetItemInSlot(unsigned int slot);

    /** Takes this item out of any container it's in.  
     * This function adjusts the two way linkage, the owning player, and properties such as sum_weight of the parents.
     * Returns false if this item is not in a container.
     */
    bool TakeOutOfContainer();

    /** Put an item into this container item in any spot where it will fit, or any container within this container where it will fit.  
     * This function adjusts the two way linkage, the owning player, and properties such as sum_weight of the parents.
     * Returns false if the item will not fit or this is not a container.
     *
     *  @param addme The item to add into the container.
     *  @param useOwner If NULL then use the owner of this container. Otherwise use the owner passed in. 
     *  NOTE:  This does NOT stack items of the same type.  addme is guaranteed to be valid when this function completes.
     */
    bool AddItemToContainer(psItem *addme, psCharacter* useOwner = NULL);
    
    /** Put an item into this container item in a specified slot.  
     * This function adjusts the two way linkage, the owning player, and properties such as sum_weight of the parents.
     * Returns false if the item will not fit or this is not a container.
     *
     *  NOTE:  This does NOT stack items of the same type.  addme is guaranteed to be valid when this function completes.
     */
    bool AddItemToContainer(psItem *addme,unsigned int slot);

    /** Swaps, moves, removes, and puts based on count in a container.
     *  For drag and drop mostly.  Different from and take and add in that you can split counts.
     */
    bool SwapItemsInContainer(unsigned int fromSlot, unsigned int toSlot);
    bool MoveItemsInContainer(unsigned int fromSlot, unsigned int toSlot, int count);
    psItem *RemoveItemsInContainer(unsigned int fromSlot, int count);

    PS_CONTAINER_ERROR PutItemsInContainer(unsigned int toSlot, psItem *item, psCharacter* owner);

    /** Same as CombineStack() except that if our item is container, it will try to stack 'stackme' with content of this container
     */
    psItem * FindStackableAndStack(psItem * stackme);

    /// Returns the sum weight of this item and all contained items
    float GetSumWeight() { return sum_weight; }

    /** DO NOT USE! HELPER FUNCTION!
     * Item weights are stored as a property of the basic item stats.  If weight reduction items are desired they should be
     * described within the item stats and the logic for that weight reduction should be implemented in the various weight
     * adjusting functions here in psItem.
     *  This function is used to help recursively adjust the weight of this item and all parent items.
     */
    float AdjustSumWeight(float delta);
    
    /** Recalculates the sum weight of this item from the top down.
     * Starts at this item and sets its own weight, recursively calls itself for each contained item.
     * Adds the sum weight of the children to its own weight for its own sum.
     * Returns the sum weight calculated.
     */
    float RecalculateFullWeight();

    /** Used to set the UNIQUE flag in addition to setting base stats pointer
     * TODO: This is pretty much untested at this point.
     *
     */
    void SetUniqueStats(psItemStats *statptr);

    /** Alters the base stats of this item instance.
     * If called on a unique item it revokes the unique item status of this item!
     * If called on a unique item the unique item stats should be destroyed elsewhere.
     *
     */
    void SetBaseStats(psItemStats *statptr);

    /** Returns the base stats for this item instance.  You may want to use this only to see this item instance is a specific type of item
     * Do not use for retrieval of values for calculations (such as damage).
     */
    psItemStats *GetBaseStats() { return base_stats; }

    /** Sets the current item stats.  DO NOT USE! 
     * This is used mostly internally.  The current stats either point to the same entry as the base stats or a stats entry that
     * is the sum of the base stats plus modifiers and effects.
     */
    void SetCurrentStats(psItemStats *statptr);

    /** Gets a pointer to the current stats.  You probably dont want to use this.
     * Use the functions below which may apply additional logic to determine what value to return.
     *
     */
    psItemStats *GetCurrentStats() { return current_stats; }

    /** In some cases some modifiers or effects may be difficult to reverse (for example percent bonuses applied and removed in different orders).
     * In these cases it should be safe to call RecalcCurrentStats() which takes a bit longer, but builds the current stats back up from the base.
     */
    void RecalcCurrentStats();

    PSITEM_FLAGS GetFlags() { return flags; }
    void SetFlags(int f) { flags = f; }

    virtual int GetPurifyStatus() { return 0; }

    /*  These next two functions provide the means for unique item id numbering.
     *  The first will be used constantly as items are created.
     *  The second should be used rarely since unique items will be rare, but a guaranteed unique number is still needed
     *
     */

    /** Returns a unique ID that can be assigned to an item instance being created.
     *    
     *   This should be called during setup of initial item data.  This function guarantees that the number returned will
     *    be unique among all item instances in memory and the database.
     *
     *  @return A 32 bit unsigned unique ID number for items.  0 on failure (lock failed).
     */
    static uint32 GetNextItemUID();
    /** Returns a unique ID that can be assigned to a new Unique ItemStats entry being created.
     *    
     *   This should be called during setup of unique item stats.  This function guarantees that the number returned will
     *    be unique among all unique item stats entries in memory and the database.
     *
     *  @return A 32 bit unsigned unique ID number for items.  0 on failure (lock failed).
     */
    static uint32 GetNextUniqueItemStatsUID();

    // Interface to itemstats

    /*  The following functions mostly call into the current stats to return the requested values.
     *  In the future additional logic about value modification may be added to these functions.
     *  For this reason these functions should ALWAYS be used rather than calling GetCurrentStats()->Get*()
     *   unless you REALLY know what you're doing and REALLY mean to bypass the intended value of the item instance.
     */

    bool GetIsMeleeWeapon();
    bool GetIsRangeWeapon();
    bool GetIsAmmo();
    bool GetIsShield();
    bool GetIsContainer();
    bool GetCanTransform();
    bool GetUsesAmmo();
    bool GetIsStackable();

    const char *GetName();
    const char *GetDescription();

    PSITEMSTATS_WEAPONTYPE GetWeaponType();
    PSSKILL GetWeaponSkill(PSITEMSTATS_WEAPONSKILL_INDEX index);
    float GetLatency();
    float GetDamage(PSITEMSTATS_DAMAGETYPE dmgtype);
    float GetExtraDamagePercent(PSITEMSTATS_DAMAGETYPE dmgtype);
    PSITEMSTATS_AMMOTYPE GetAmmoType();

    float GetPenetration();
    float GetUntargetedBlockValue();
    float GetTargetedBlockValue();
    float GetCounterBlockValue();
    PSITEMSTATS_ARMORTYPE GetArmorType();
    float GetDamageProtection(PSITEMSTATS_DAMAGETYPE dmgtype);
    float GetHardness();
    float GetWeaponAttributeBonus(PSITEMSTATS_STAT stat);
    PSITEMSTATS_STAT GetWeaponAttributeBonusType(PSITEMSTATS_STAT_BONUS_INDEX index);
    float GetWeaponAttributeBonusMax(PSITEMSTATS_STAT_BONUS_INDEX index);

    float GetWeight();
    unsigned short GetSize();
    /// Gets the total size of the items in the stack.
    float GetSumSize() { return (float)GetSize()*stack_count; }
    
    unsigned short GetContainerMaxSize();
    PSITEMSTATS_SLOTLIST GetValidSlots();
    bool FitsInSlots(PSITEMSTATS_SLOTLIST slotmask);
    float GetDecayResistance();
    psMoney& GetPrice();
    psMoney GetSellPrice();    ///< Merchants want a percentage
    psItemCategory* GetCategory();

    float GetVisibleDistance();

    void GetLocationInWorld(psSectorInfo **sectorinfo,float &loc_x,float &loc_y,float &loc_z,float &loc_yrot);
    void SetLocationInWorld(psSectorInfo *sectorinfo,float loc_x,float loc_y,float loc_z,float loc_yrot);

    /** Get the Mesh Name for the item.
     *
     *  Used for standalone or weilded mesh.
     */
    const char *GetMeshName();
    
    /** Get the Texture Name for the item.
     *
     *  Used when worn and attached to the mesh given by part name.
     */
    const char *GetTextureName();
    
    /** Get the Part Name for the item.
     *
     *  This is the name of the part that the texture should be attached to
     *  if no change of mesh.
     */
    const char *GetPartName();
    
    /** Get the Part Mesh Name for the item.
     *
     *  This is the new mesh to be attached to the location given
     *  by the pattern Part Name.
     */
    const char *GetPartMeshName();

    /** Get the Image Name for the item.
     *
     *  Used in inventory and other location where item has to be presented
     *  by a 2D image.
     */
    const char *GetImageName();

    csString GetQuantityName(); // returns the quantity and plural name in a string
    static csString GetQuantityName(psString name, int stack_count);

    // For future use, see notes for mesh_index variables, etc.
    /*
    unsigned int GetMeshIndex();
    unsigned int GetTextureIndex();
    unsigned int GetTexturePartIndex();
    unsigned int GetImageIndex();
    */


    bool CheckStackableWith(psItem *otheritem);

    const char *GetSound();

    /// This is used by the math scripting engine to get various values.
    double GetProperty(const char *ptr);

    bool GetIsLocked() { return ((flags & PSITEM_FLAG_LOCKED)? true : false); }
    void SetIsLocked(bool v);

    bool GetIsLockable() { return ((flags & PSITEM_FLAG_LOCKABLE)? true : false); }
    void SetIsLockable(bool v);

    bool GetIsKey() { return ((flags & PSITEM_FLAG_KEY)? true : false); }
    void SetIsKey(bool v);

    bool IsTransient() { return ((flags & PSITEM_FLAG_TRANSIENT) ? true : false); }
    void SetIsTransient(bool v);
    void SetIsPickupable(bool v);

    PSSKILL GetLockpickSkill() { return lockpickSkill; }
    void SetLockpickSkill(PSSKILL v);

    unsigned int GetLockStrength() { return lockStrength; }
    void SetLockStrength(unsigned int v);

    bool CanOpenLock(uint32 id);
    void AddOpenableLock(uint32 v);
    void RemoveOpenableLock(uint32 v);
    void ClearOpenableLocks();
    void MakeSkeleton(bool b);
    bool GetIsSkeleton();

    psScheduledItem* GetScheduledItem() { return schedule; }
    void SetScheduledItem(psScheduledItem* item) { schedule = item; }
    void ScheduleRespawn();

    void ItemAboutToMove(); ///< This is called before item is moved to another place

    /// Checks if the 'item' parameter is direct or indirect parent of our item
    bool IsIndirectParent(psItem * item);

    /// Gets the reduction of this weapon against the armor given
    float GetArmorVSWeaponResistance(psItemStats* armor);

    /** Called when an item is completely destroyed from the persistant world
     *
     *  Persistant refers to the item existing through a reset of the server.  This definition can affect when an item
     *  should be "destroyed" (removed from the database).
     *  For example, if items dropped on the ground will be forever lost if the server goes down, then when an item
     *  is dropped on the group, DestroyItem() should be used instead of UpdateItem().  When the item is picked up
     *  again, CreateItem() should be called.
     *
     */
    bool Destroy();

    /**
     * This function handles creating an event manager event at the right time
     * to get transient objects removed from the world automatically.
     */
    void ScheduleRemoval();

    bool Delete();

    /*  Objects of this class utilize a pool allocator.  The pool allocator can be found in poolallocator.h in the psutil library.
     *  The pool allocator allows us to avoid allocating memory from and releasing memory to the heap over and over for things that are
     *  constantly shifting in the number of allocations (such as items in the game).  It also reduces memory usage overhead slightly.
     *
     *  It should be possible to debug memory leaks from a specific pool easier as well since you can analyze allocations solely from that pool
     *  without getting flooded with allocations from everywhere else.
     */

    ///  The new operator is overriden to call PoolAllocator template functions
    void *operator new(size_t);
    ///  The delete operator is overriden to call PoolAllocator template functions
    void operator delete(void *);

private:
    /// Static reference to the pool for all psItem objects
    static PoolAllocator<psItem> itempool;

    friend class psContainerIterator;

    /// Mutex to restrict access to the global itemid
    static csRef<csMutex> global_itemid_mutex;
    /// global item id counter.  This value is always at least 1 higher than the highest itemid assigned to an item.
    static uint32 global_itemid;

    /// Mutex to restrict access to the global incrementing id of unique itemstats
    static csRef<csMutex> global_uniqueitemid_mutex;
    /// global unique itemstats id counter.  This value is always at least 1 higher than the highest id assigned to a unique itemstats entry.
    static uint32 global_uniqueitemid;

    gemItem* gItem;

    bool loaded;
    bool pendingsave;

    csRef<psItemSafe> self_reference;

#if SAVE_TRACER
private:
    csString last_save_queued_from;
#endif
};

class psScheduledItem
{
public:
    psScheduledItem(int spawnID,uint32 itemID,csVector3& position, psSectorInfo* sector, int interval,int maxrnd);

    psItem* CreateItem();
    uint32 GetItemID() { return itemID;}

    psSectorInfo* GetSector() { return sector; }
    csVector3& GetPosition() { return pos; }
    int MakeInterval(); ///< This will return a randomized interval
    int GetInterval() { return interval; }
    int GetMaxModifier() { return maxrnd; }

    csTicks GetLastSpawn() { return lastSpawn; }
    void UpdatePosition(csVector3& positon, const char *sector);
    void ChangeIntervals(int newint, int newrand);
    void Remove(); ///< Deletes from the DB and everything

    bool WantToDie() { return wantToDie; }

private:
    bool wantToDie;
    int spawnID;           ///< database id
    uint32 itemID;         ///< Item
    csVector3 pos;         ///< Position
    psSectorInfo* sector;  ///< Sector
    int interval;          ///< Interval in msecs
    int maxrnd;            ///< Maximum random interval modifier in msecs
    csTicks lastSpawn;     ///< When we last spawned it, good for something perhaps? :)
};

class psItemSet
{
protected:
    csArray<psItem*> set;
    csArray<uint32> parents;

public:
    ~psItemSet();

    /// Adds a psitem to this set.  The parentid must be specified since this function is used at load time.
    void Add(psItem *item,uint32 parentid);
    bool ResolveAllParents();
    void Release();
    psItem* Get(size_t n);
    size_t GetSize() { return set.Length(); }
};

class psDelaySave : public psGameEvent
{
protected:
    csRef<psItemSafe> item;

public:
    psDelaySave(int length, psItemSafe* item);
    virtual void Trigger();

#if SAVE_DEBUG
private:
    csString type, name;
#endif
};

#endif
