/*
 * psserverchar.cpp
 *
 * Copyright (C) 2002 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.
 *
 * Communicates with the client side version of charmanager.
 */
#include <psconfig.h>

#include <iutil/databuff.h>
#include <iutil/object.h>
#include <csutil/csstring.h>

#include "util/serverconsole.h"
#include "util/psxmlparser.h"
#include "util/log.h"
#include "util/mathscript.h"
#include "util/psconst.h"
#include "util/eventmanager.h"

#include "net/message.h"
#include "net/messages.h"
#include "net/msghandler.h"
#include "net/charmessages.h"

#include "engine/celbase.h"
#include "engine/drmessage.h"

#include "bulkobjects/pscharacter.h"
#include "bulkobjects/pscharacterloader.h"
#include "bulkobjects/psitem.h"
#include "bulkobjects/psraceinfo.h"
#include "bulkobjects/pssectorinfo.h"
#include "bulkobjects/psmerchantinfo.h"
#include "bulkobjects/psactionlocationinfo.h"

#include "globals.h"

#include "psserverchar.h"
#include "client.h"
#include "playergroup.h"
#include "clients.h"
#include "entitymanager.h"
#include "util/psdatabase.h"
#include "psserver.h"
#include "chatmanager.h"
#include "groupmanager.h"
#include "spellmanager.h"
#include "workmanager.h"
#include "netmanager.h"
#include "cachemanager.h"
#include "progressionmanager.h"
#include "creationmanager.h"
#include "exchangemanager.h"
#include "actionmanager.h"
#include "serverstatus.h"

///This expresses in seconds how many days the char hasn't logon. 60 days, at the moment.
#define MAX_DAYS_NO_LOGON 5184000 

psServerCharManager::psServerCharManager()
{
    slotManager = NULL;
}

psServerCharManager::~psServerCharManager()
{
    if (psserver->GetEventManager())
    {
        psserver->GetEventManager()->Unsubscribe(this, MSGTYPE_CHAR_INFO);
        psserver->GetEventManager()->Unsubscribe(this, MSGTYPE_GUIINVENTORY);
        psserver->GetEventManager()->Unsubscribe(this, MSGTYPE_GUIMERCHANT);
        psserver->GetEventManager()->Unsubscribe(this, MSGTYPE_CHAR_CREATE_UPLOAD);
        psserver->GetEventManager()->Unsubscribe(this, MSGTYPE_VIEW_ITEM);
        psserver->GetEventManager()->Unsubscribe(this, MSGTYPE_CRAFT_INFO);

    }

    delete slotManager;
    slotManager = NULL;
}

bool psServerCharManager::Initialize( ClientConnectionSet* ccs)
{
    clients = ccs;

    psserver->GetEventManager()->Subscribe(this, MSGTYPE_CHAR_INFO,REQUIRE_READY_CLIENT);
    psserver->GetEventManager()->Subscribe(this, MSGTYPE_GUIINVENTORY,NO_VALIDATION);
    psserver->GetEventManager()->Subscribe(this, MSGTYPE_GUIMERCHANT,REQUIRE_READY_CLIENT|REQUIRE_ALIVE);
    psserver->GetEventManager()->Subscribe(this, MSGTYPE_CHAR_CREATE_UPLOAD,NO_VALIDATION);
    psserver->GetEventManager()->Subscribe(this, MSGTYPE_VIEW_ITEM,REQUIRE_READY_CLIENT);
    psserver->GetEventManager()->Subscribe(this, MSGTYPE_CRAFT_INFO, REQUIRE_READY_CLIENT);

    slotManager = new SlotManager;
    if ( !(slotManager && slotManager->Initialize()) )
        return false;

    return true;
}


void psServerCharManager::ViewItem( MsgEntry* me )
{
    psViewItemDescription mesg(me);
    Client* client = clients->Find(me->clientnum);
    ViewItem(client, mesg.containerID, mesg.slotID, mesg.parentContainerID);
}

void psServerCharManager::ViewItem(Client* client, int containerID, int slotID, int parentContainerID)
{
    psItem* item = slotManager->FindItem( client, containerID, slotID, parentContainerID );
        
    if ( !item )
    {
        psActionLocation *action = psserver->GetActionManager()->FindAction( containerID );
        if ( !action )
        {
            Error3( "No item/action : %d, %d", containerID, slotID );
            return;
        }
        else
        {
            if ( action->response.StartsWith( "<Examine>", false ) )
            {
                // load response into XML doc
                csRef<iDocument>     doc     = ParseString( action->response );
                if(!doc)
                {
                    Error1("Parse error in action response");
                    return;
                }

                csRef<iDocumentNode> root    = doc->GetRoot();
                if(!root)
                {
                    Error1("No XML root in action response");
                    return;
                }
                
                csRef<iDocumentNode> topNode = root->GetNode( "Examine" );
                if(!topNode)
                {
                    Error1("No <Examine> tag in action response");
                    return;
                }
                
                // Check for container
                csRef<iDocumentNode> containerNode, descriptionNode, gameboardNode;
                containerNode = topNode->GetNode( "Container" );
                if ( containerNode ) 
                {
                    uint32 instance_id = (uint32)containerNode->GetAttributeValueAsInt( "ID" );
                    gemItem* realItem = psserver->GetEntityManager()->GetGEM()->FindItemEntity( instance_id );

                    if (!realItem)
                    {
                        Error3("Invalid instance ID %u in action location %s", instance_id, action->name.GetDataSafe());
                        return;
                    }

                    item = realItem->GetItem();

                    if ( !item )
                    {
                        CPrintf (CON_ERROR, "Invalid ItemID in Action Location Response.\n");
                        return;
                    }
                    SendActionContents( client, action, topNode, item );
                }

                // Check for minigames
                else if ((gameboardNode = topNode->GetNode("GameBoard")))
                {
                    descriptionNode = topNode->GetNode("Description");
                    if (descriptionNode)
                    {
                        psViewItemDescription mesg(client->GetClientNum(), action->name,
                                descriptionNode->GetContentsValue(), csString(""), 0);
                        mesg.SendMessage();
                    }
                    else
                    {
                        Error2("Action location %s XML response is not a valid response", action->name.GetData());
                        return;
                    }

                }

                else if (item->GetBaseStats()->GetIllumination().Length() == 0)   // Not a container, show description or map
                {
                    descriptionNode = topNode->GetNode( "Description" );
                    if ( descriptionNode )
                    {
                        psViewItemDescription mesg( client->GetClientNum(), action->name, descriptionNode->GetContentsValue(), csString( "" ) , 0 );
                        mesg.SendMessage();
                    }
                    else
                    {
                        CPrintf (CON_ERROR, "Action Location XML response is not a valid response. \n");
                        return;
                    }
                }
                else // Illumination
                {
                    descriptionNode = topNode->GetNode( "Description" );
                    if ( descriptionNode )
                    {
                        psIlluminationMessage msg( client->GetClientNum(), item->GetBaseStats()->GetIllumination() );
                        msg.SendMessage();
                    }
                    else
                    {
                        CPrintf (CON_ERROR, "Action Location XML response is not a valid response. \n");
                        return;
                    }                    
                }
            }
            else
            {
                psViewItemDescription mesg( client->GetClientNum(), action->name, action->response, csString( "" ) , 0 );
                mesg.SendMessage();
            }
        }
    }
    else    
    {
        if ( item->GetIsContainer() )
        {
            SendItemContents( client, item, containerID, slotID );
        }
        //for now, we pretend that /examine reads.  When we implement /read, this will change            
        else if( item->GetBaseStats()->GetIsReadable() ) 
            SendBookText( client, item );
        else if (item->GetBaseStats()->GetIllumination().Length() == 0)   // Not a container, show description or map
            SendItemDescription( client, item );
        else // Illumination
        {
            psIlluminationMessage msg( client->GetClientNum(), item->GetBaseStats()->GetIllumination() );
            msg.SendMessage();
        }
    }
}

void psServerCharManager::HandleMessage( MsgEntry* me, Client *client )
{
    client = clients->FindAny(me->clientnum);

    if (!client)
    {
        CPrintf (CON_ERROR, "***Couldn't find clientnum in serverchar HandleMessage\n");
        return;
    }

    switch ( me->GetType() )
    {
        case MSGTYPE_GUIINVENTORY:
        {
            HandleInventoryMessage(me);
            return;
        }

        case MSGTYPE_VIEW_ITEM:
        {
            ViewItem( me );
            break;
        }

        //Not yet implemented, using /examine for now 
        case MSGTYPE_READ_BOOK:
        {
            ViewItem( me );
            break;
        }

        case MSGTYPE_CHAR_INFO:
        {
            HandleCharInfo( me, client );
            return;
        }
        case MSGTYPE_GUIMERCHANT:
        {
            HandleMerchantMessage(me, client);
            break;
        }

        case MSGTYPE_CHAR_CREATE_UPLOAD:
        {
            HandleUploadMessage( me, client );
            break;
        }
        
        case MSGTYPE_CRAFT_INFO:
        {
            HandleCraftInfo( me, client );
            break;
        }
    }
}


//---------------------------------------------------------------------------
/**
 * This handles all formats of inventory message.
 */
//---------------------------------------------------------------------------
bool psServerCharManager::HandleInventoryMessage( MsgEntry* me )
{
    if ( !me )
        return false;

    psGUIInventoryMessage incoming(me);
    int     fromClientNumber    = me->clientnum;

    switch ( incoming.command )
    {
        case psGUIInventoryMessage::REQUEST:
        {
            SendInventory(fromClientNumber);
            break;
        }
    }
    return true;
}

bool psServerCharManager::SendInventory( int clientNum )
{
    Client* client = clients->Find(clientNum);
    if (client==NULL)
        return false;

    int toClientNumber = clientNum;
    int itemCount;
    unsigned int z;

 
    psCharacter *chardata=client->GetCharacterData();
    if (chardata==NULL)
        return false;

    // count items that we are going to send
    itemCount = 0;
    for ( z = 0; z < PSCHARACTER_SLOT_COUNT; z++ )
        if (chardata->Inventory().GetEquipmentItem(z) != NULL)
            itemCount++;
    for (z=0;z<PSCHARACTER_BULK_COUNT; z++)
        if (chardata->Inventory().GetBulkItem(z) != NULL)
            itemCount++;

    psGUIInventoryMessage outgoing(toClientNumber,
                                   psGUIInventoryMessage::LIST,
                                   itemCount, chardata->Inventory().MaxWeight() );

    for ( z = 0; z < PSCHARACTER_SLOT_COUNT; z++ )
    {
        psItem *item = chardata->Inventory().GetEquipmentItem(z);
        if ( item )
        {
            csString name;
            outgoing.AddItem( item->GetName(),
                              CONTAINER_INVENTORY_EQUIPMENT,
                              z,
                              item->GetStackCount(),
                              item->GetSumWeight(),
                              item->GetSumSize(),
                              item->GetImageName(),
                              item->GetPurifyStatus());
        }
    }

    for (z=0;z<PSCHARACTER_BULK_COUNT;z++)
    {
        psItem *item=chardata->Inventory().GetBulkItem(z);
        if ( item )
        {
            csString name;
            outgoing.AddItem( item->GetName(),
                              CONTAINER_INVENTORY_BULK,
                              z,
                              item->GetStackCount(),
                              item->GetSumWeight(),
                              item->GetSumSize(),
                              item->GetImageName(),
                              item->GetPurifyStatus());
        }
    }
    outgoing.AddMoney(chardata->Money());
    if (outgoing.valid)
    {
        outgoing.msg->ClipToCurrentSize();
        psserver->GetEventManager()->SendMessage(outgoing.msg);
    }
    else
    {
        Bug2("Could not create valid psGUIInventoryMessage for client %u.\n",toClientNumber);
    }


    return true;
}

bool psServerCharManager::UpdateItemViews( int clientNum )
{
    Client* client = clients->Find(clientNum);

    // If inventory window is up, update it
    SendInventory( clientNum );

    // If glyph window is up, update it
    psserver->GetSpellManager()->SendGlyphs(client);

    return true;
}

bool psServerCharManager::IsBanned(const char* name)
{
    // Check if the name is banned
    csString nName = NormalizeCharacterName(name);
    for(int i = 0;i < (int)CacheManager::GetSingleton().GetBadNamesCount(); i++)
    {
        csString name = CacheManager::GetSingleton().GetBadName(i); // Name already normalized
        if(name == nName)
            return true;
    }
    return false;
}


int psServerCharManager::HasConnected( csString name )
{
    int secondsLastLogin;
    secondsLastLogin = 0;

    //Query to the db that calculates already the amount of seconds since the last login.
    Result result(db->Select("SELECT last_login, UNIX_TIMESTAMP() - UNIX_TIMESTAMP(last_login) as seconds_since_last_login FROM characters WHERE name = '%s'", name.GetData() ));
    //There is no character with such a name. 
    if (!result.IsValid() || result.Count() == 0)
    {
        return 1;
    }
    //We check when the char was last online.    
    secondsLastLogin = result[0].GetInt(1);

    if ( secondsLastLogin > MAX_DAYS_NO_LOGON )//the result is major than 2 month 
    { 
        return 2;
    }
    
    //The char has connected recently.
    return 0;
}
void psServerCharManager::HandleCraftInfo( MsgEntry* me, Client* client )
{
    psCharacter* chardata = client->GetCharacterData();
    if ( !chardata )
        return;
            
    psItem* mindPattern = chardata->Inventory().GetEquipmentItem(PSCHARACTER_SLOT_MIND);
    if ( !mindPattern )
        return;

    int patternID = mindPattern->GetPatternID();
    if ( patternID == -1 )
        return;

    psMsgCraftingInfo* craftMsg = CacheManager::GetSingleton().GetCraftInfo( patternID );
    if ( craftMsg )
    {        
        craftMsg->msg->clientnum = me->clientnum;
        craftMsg->SendMessage();
    }    
}


void psServerCharManager::HandleUploadMessage( MsgEntry* me, Client *client )
{
    Debug1( LOG_NEWCHAR, me->clientnum,"New Character is being created" );

    psCharUploadMessage upload(me);

    if (!upload.valid)
    {
        Debug2(LOG_NET,me->clientnum,"Received unparsable psUploadMessage from client %u.",me->clientnum);
        return;
    }

    int acctID = client->GetAccountID();
    if ( !acctID )
    {
        Error2( "Player account %d could not be located", acctID );

        psCharRejectedMessage reject(me->clientnum);

        psserver->GetEventManager()->Broadcast(reject.msg, NetBase::BC_FINALPACKET);
        psserver->RemovePlayer (me->clientnum,"Could not find your account.");
        return;
    }

    // Check to see if the player already has 4 accounts;
    csString query;
    query.Format( "Select id from characters where account_id=%d", acctID );
    Result result (db->Select( query ) );
    if ( result.IsValid() && result.Count() >= CHARACTERS_ALLOWED )
    {        
        psserver->RemovePlayer (me->clientnum,"At your character limit.");
        return;
    }
        
    csString playerName =  upload.name;
    csString lastName =  upload.lastname;

    playerName = NormalizeCharacterName(playerName);
    lastName = NormalizeCharacterName(lastName);

    // Check banned names
    if(IsBanned(playerName))
    {
        csString error;
        error.Format("The name %s is banned", playerName.GetData() );
        psCharRejectedMessage reject(me->clientnum,
                            psCharRejectedMessage::RESERVED_NAME,
                            (char*)error.GetData() );
        reject.SendMessage();
        return;
    }

    if(IsBanned(lastName))
    {
        csString error;
        error.Format("The lastname %s is banned", lastName.GetData() );
        psCharRejectedMessage reject(me->clientnum,
                            psCharRejectedMessage::RESERVED_NAME,
                            (char*)error.GetData() );
        reject.SendMessage();
        return;
    }

    Debug3( LOG_NEWCHAR, me->clientnum,"Got player firstname (%s) and lastname (%s)\n",playerName.GetData(), lastName.GetData() );

    ///////////////////////////////////////////////////////////////
    //  Check to see if the player name is valid
    ///////////////////////////////////////////////////////////////
    if ( playerName.Length() == 0 || !FilterName(playerName) )
    {
        psCharRejectedMessage reject(me->clientnum,
                                     psCharRejectedMessage::NON_LEGAL_NAME,
                                     "The name you specifed is not a legal player name." );

        psserver->GetEventManager()->SendMessage(reject.msg);
        return;
    }

    if ( lastName.Length() != 0 && !FilterName(lastName) )
    {
        psCharRejectedMessage reject(me->clientnum,
                                     psCharRejectedMessage::NON_LEGAL_NAME,
                                     "The name you specifed is not a legal lastname." );

        psserver->GetEventManager()->SendMessage(reject.msg);
        return;
    }

    Debug2( LOG_NEWCHAR, me->clientnum,"Checking player firstname '%s'..\n",playerName.GetData());
    ///////////////////////////////////////////////////////////////
    //  Check to see if the character name is unique in 'characters'.
    ///////////////////////////////////////////////////////////////
    if  ( !IsUnique( playerName ) )
    {
        psCharRejectedMessage reject(me->clientnum,
                                     psCharRejectedMessage::NON_UNIQUE_NAME,
                                     "The firstname you specifed is not unique." );

        psserver->GetEventManager()->SendMessage(reject.msg);
        return;
    }

    if (lastName.Length())
    {
        Debug2( LOG_NEWCHAR, me->clientnum,"Checking player lastname '%s'..\n",lastName.GetData());
        if  ( !IsLastNameUnique( lastName ) )
        {
            psCharRejectedMessage reject(me->clientnum,
                                        psCharRejectedMessage::NON_UNIQUE_NAME,
                                        "The lastname you specifed is not unique." );

            psserver->GetEventManager()->SendMessage(reject.msg);
            return;
        }
    }
    ///////////////////////////////////////////////////////////////
    //  Check to see if the character name is on the reserve list.
    ///////////////////////////////////////////////////////////////
    int reservedName = IsReserved( playerName, acctID );
    if ( reservedName == NAME_RESERVED )
    {
        csString error;
        error.Format("The name %s is reserved", playerName.GetData() );
        psCharRejectedMessage reject(me->clientnum,
                                     psCharRejectedMessage::RESERVED_NAME,
                                     (char*)error.GetData() );

        psserver->GetEventManager()->SendMessage(reject.msg);
        return;
    }


    csString error;
    if (  psserver->charCreationManager->Validate( upload, error) == false )
    {
        error.Append(", your creation choices are invalid." );
        psCharRejectedMessage reject(me->clientnum,
                                     psCharRejectedMessage::INVALID_CREATION,
                                     (char*)error.GetData() );

        reject.SendMessage();
        return;
    }        
    
    ///////////////////////////////////////////////////////////////
    //  Create the psCharacter structure for the player.
    ///////////////////////////////////////////////////////////////
    psCharacter *chardata=new psCharacter();
    chardata->SetCharType(PSCHARACTER_TYPE_PLAYER);
    chardata->SetFullName(playerName,lastName);
    chardata->SetDescription(upload.bio);

    psRaceInfo *raceinfo=CacheManager::GetSingleton().GetRaceInfoByNameGender( (PSCHARACTER_RACE)upload.race, (PSCHARACTER_GENDER)upload.gender);
    if (raceinfo==NULL)
    {
        Error3("Invalid race/gender combination on character creation:  Race='%d' Gender='%d'", upload.race, upload.gender );
        psCharRejectedMessage reject(me->clientnum);
        psserver->GetEventManager()->Broadcast(reject.msg, NetBase::BC_FINALPACKET);
        psserver->RemovePlayer (me->clientnum,"Player tried to create an invalid race/gender.");
        delete chardata;
        return;
    }
    chardata->SetRaceInfo(raceinfo);
    chardata->SetHitPoints(50.0);
    chardata->SetHitPointsMax(0.0);

    float x,y,z,yrot;
    const char *sectorname;

    raceinfo->GetStartingLocation(x,y,z,yrot,sectorname);

    if (sectorname==NULL)
    {
        Error1("Race has NULL starting sector" );
        psCharRejectedMessage reject(me->clientnum);
        psserver->GetEventManager()->Broadcast(reject.msg, NetBase::BC_FINALPACKET);
        psserver->RemovePlayer (me->clientnum,"No starting Sector.");
        delete chardata;
        return;
    }
    psSectorInfo *sectorinfo=CacheManager::GetSingleton().GetSectorInfoByName(sectorname);
    if (sectorinfo==NULL)
    {
        Error2("Unresolvable starting sector='%s'", sectorname );
        psCharRejectedMessage reject(me->clientnum);
        psserver->GetEventManager()->Broadcast(reject.msg, NetBase::BC_FINALPACKET);
        psserver->RemovePlayer (me->clientnum,"No starting Sector.");
        delete chardata;
        return;
    }
    chardata->SetLocationInWorld(sectorinfo,x,y,z,yrot);

    psTrait * trait;
    CPrintf(CON_DEBUG, "Trait: %d\n", upload.selectedFace );    
    trait = CacheManager::GetSingleton().GetTraitByID(upload.selectedFace);    
    if ( trait )
        chardata->SetTraitForLocation( trait->location, trait );
            
    trait = CacheManager::GetSingleton().GetTraitByID(upload.selectedHairStyle);    
    if ( trait )
        chardata->SetTraitForLocation( trait->location, trait );
    
    trait = CacheManager::GetSingleton().GetTraitByID(upload.selectedBeardStyle);    
    if ( trait )
        chardata->SetTraitForLocation( trait->location, trait );
    
    trait = CacheManager::GetSingleton().GetTraitByID(upload.selectedHairColour);    
    if ( trait )
        chardata->SetTraitForLocation( trait->location, trait );
    
    trait = CacheManager::GetSingleton().GetTraitByID(upload.selectedSkinColour);    
    if ( trait )
        chardata->SetTraitForLocation( trait->location, trait );
                    
    csString filename;
    filename.Format("/planeshift/models/%s/%s.cal3d",raceinfo->mesh_name,raceinfo->mesh_name);

    gemActor *actor = new gemActor( psserver->GetEntityManager()->GetGEM(),
                                    chardata,
                                    raceinfo->mesh_name,
                                    filename,
                                    psserver->GetEntityManager()->FindSector(sectorinfo->name),
                                    csVector3(x,y,z),yrot,
                                    client->GetClientNum(),chardata->GetCharacterID());

    actor->SetupCharData();

    if( !upload.verify )
    {
        if (!psServer::CharacterLoader.NewCharacterData(acctID,chardata))
        {
            Error1("Character could not be created.");
            psCharRejectedMessage reject(me->clientnum);
            psserver->GetEventManager()->Broadcast(reject.msg, NetBase::BC_FINALPACKET);
            psserver->RemovePlayer (me->clientnum,"Your character could not be created in the database.");
            delete chardata;
            return;
        }
    }

    // Check to see if a path name was set. If so we will use that to generate 
    // the character starting stats and skills.    
    if ( upload.path != "None" )
    {
        // Progression Event name is PATH_PathName
        csString name("PATH_");
        name.Append(upload.path);
        ProgressionEvent* event = psserver->GetProgressionManager()->FindEvent( name.GetData() );
        if ( event )
        {
            // The script uses the race base character points to calculate starting stats.
            event->SetValue("CharPoints",  (double)raceinfo->initialCP );                                                                                                 
            psserver->GetProgressionManager()->ProcessEvent( event, actor, actor );
        }            
    }
    else
    {
    //int cpUsage = psserver->charCreationManager->CalculateCPChoice( upload.choices ) +
    //              psserver->charCreationManager->CalculateCPLife(upload.lifeEvents );    
    for ( size_t ci = 0; ci < upload.choices.Length(); ci++ )
    {
        psCharCreationManager::CreationChoice* choice = psserver->charCreationManager->FindChoice( upload.choices[ci] );
        if ( choice )
        {
            csString script (choice->eventScript.GetData() );
            csString name( psserver->charCreationManager->FindChoice( upload.choices[ci] )->name.GetData() );
            Debug3( LOG_NEWCHAR, me->clientnum,"Choice: %s Creation Script: %s", name.GetData(), script.GetData() );

            if ( choice->choiceArea == FATHER_JOB || choice->choiceArea == MOTHER_JOB )
            {
                ProgressionEvent* event = psserver->GetProgressionManager()->FindEvent( script.GetData() );
                if ( event )
                {
                    int modifier = (choice->choiceArea == FATHER_JOB) ? upload.fatherMod : upload.motherMod;
                    if ( modifier > 3 || modifier < 1 )
                    {
                        modifier = 1;
                    }
                    
                    event->SetValue("ParentStatus", (double)modifier );                                                               
                }
            }                           
            psserver->GetProgressionManager()->ProcessEvent( script, actor, actor );
        }            
        else
        {
            Debug2( LOG_NEWCHAR, me->clientnum,"Character Choice %d not found\n", upload.choices[ci] );
        }
    }
    for ( size_t li = 0; li < upload.lifeEvents.Length(); li++ )
    {
        csString script(psserver->charCreationManager->FindLifeEvent( upload.lifeEvents[li] )->eventScript.GetData() );
        Debug2( LOG_NEWCHAR, me->clientnum,"LifeEvent Script: %s", script.GetData() );

        psserver->GetProgressionManager()->ProcessEvent( script, actor, actor );
    }
    }
    
    if ( !upload.verify )
    {

        if ( reservedName == NAME_RESERVED_FOR_YOU )
        {
            AssignScript( chardata );
        }
        // This function recalculates the Max HP, Mana and Stamina of the new character
        chardata->RecalculateStats();

        // Make sure the new player have HP, Mana and Samina that was calculated
        chardata->SetHitPoints(chardata->GetHitPointsMax());
        chardata->SetMana(chardata->GetManaMax());
        chardata->SetStamina(chardata->GetStaminaMax(true),true);
        chardata->SetStamina(chardata->GetStaminaMax(false),false);
        
                   
        psServer::CharacterLoader.SaveCharacterData( chardata, actor );
        Debug1(LOG_NEWCHAR,me->clientnum,"Player Creation Complete");

        // Remove cached objects to make sure that the client gets a fresh character
        // list from the database if it logs out and in within 2 minutes.
        iCachedObject *obj = CacheManager::GetSingleton().RemoveFromCache(CacheManager::GetSingleton().MakeCacheName("list",client->GetAccountID()));
        if (obj)
        {
            obj->ProcessCacheTimeout();
            obj->DeleteSelf();
        }
        obj = CacheManager::GetSingleton().RemoveFromCache(CacheManager::GetSingleton().MakeCacheName("auth",client->GetAccountID()));
        if (obj)
        {
            obj->ProcessCacheTimeout();
            obj->DeleteSelf();
        }

        // Here everything is ok
        client->SetPlayerID(chardata->GetCharacterID());
        client->SetName(playerName);

        psCharApprovedMessage app(me->clientnum);
        if (app.valid)
            psserver->GetEventManager()->SendMessage(app.msg);
        else
            Bug2("Could not create valid psCharApprovedMessage for client %u.\n",me->clientnum);                               
    }    
    else
    {
        psCharVerificationMesg mesg( me->clientnum );
        int z;
        for ( z = 0; z < PSITEMSTATS_STAT_COUNT; z++ )
        {
            int value = chardata->GetAttributes()->GetStat( (PSITEMSTATS_STAT)z);
            if ( value > 0 )
            {
                mesg.AddStat( value, CacheManager::GetSingleton().Attribute2String((PSITEMSTATS_STAT)z));                
            }                                    
        }
        for ( z = 0; z < PSSKILL_COUNT; z++ )
        {
            int rank = chardata->GetSkills()->GetSkillRank( (PSSKILL)z, false );
            
            psSkillInfo* info = CacheManager::GetSingleton().GetSkillByID(z);
            csString name("Not found");
            if ( info )
                name.Replace( info->name );
            
            if ( rank > 0 )
            {
                mesg.AddSkill(rank, name);                
            }                                            
        }                
        mesg.Construct();
        mesg.SendMessage();
    }
    
    delete actor;

    if (!upload.verify)
    {
        // Remove char data from the cache
        iCachedObject *obj = CacheManager::GetSingleton().RemoveFromCache(CacheManager::GetSingleton().MakeCacheName("char", chardata->GetCharacterID()));
        if (obj)
        {
            obj->ProcessCacheTimeout();
            obj->DeleteSelf();
        }
    }
}


bool psServerCharManager::FilterName(const char* name)
{
    if (name == NULL)
        return false;

    size_t len = strlen(name);

    if ( (strspn(name,"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
                != len) )
        return false;

    if (((int)strspn(((const char*)name)+1,
                     (const char*)"abcdefghijklmnopqrstuvwxyz") != len - 1))
        return false;

    return true;
}



void psServerCharManager::HandleCharInfo( MsgEntry* me, Client *client )
{
    psCharInfoRequestMessage irmsg(me);

    psCharInfoRequestMessage::type
    command = psCharInfoRequestMessage::SKILL_REQUEST_ALL;

    if ( irmsg.command == command )
    {
        psCharacter *chardata=client->GetCharacterData();
        if (chardata==NULL)
            return;
        int i;
        csString outData("<SL>");
        csString tempBuffer;

        for (i=0;i<PSSKILL_COUNT;i++)
        {
            psSkillInfo *skillinfo=CacheManager::GetSingleton().GetSkillByID((PSSKILL)i);
            if (skillinfo!=NULL)
            {
        csString escpxml = EscpXML(skillinfo->name);
                tempBuffer.Format( "<SKILL NAME=\"%s\" VALUE=\"%5.2f\" />",
                                   escpxml.GetData(),
                                   chardata->GetSkills()->GetSkillRank((PSSKILL)i));
                outData.Append(tempBuffer);
            }
        }
        outData.Append("</SL>");

         psCharInfoRequestMessage msg(me->clientnum, command, outData);
         if (msg.valid)
            psserver->GetEventManager()->SendMessage(msg.msg);
        else
        {
            Bug2("Could not create valid psCharInfoRequestMessage for client %u.\n",me->clientnum);
        }

    }
}

void psServerCharManager::BeginTrading(Client * client, gemObject * target, const csString & type)
{
    psCharacter * merchant = NULL;
    int clientnum = client->GetClientNum();
    psCharacter* character = client->GetCharacterData();

    // Make sure that we are not busy with something else
    if (client->GetActor()->GetMode() != PSCHARACTER_MODE_PEACE)
    {
        psserver->SendSystemError(client->GetClientNum(), "You cannot trade because you are already busy.");
        return;
    }

    merchant = target->GetCharacterData();
    if(!merchant)
    {
        psserver->SendSystemInfo(client->GetClientNum(), "Merchant not found.");
        return;
    }

    if (client->GetActor()->RangeTo(target) > RANGE_TO_SELECT)
    {
        psserver->SendSystemInfo(client->GetClientNum(), "You are not in range to trade with %s.",merchant->GetCharName());
        return;
    }

    if (!target->IsAlive())
    {
        psserver->SendSystemInfo(client->GetClientNum(), "Can't trade with a dead merchant.");
        return;
    }

    if (!merchant->IsMerchant())
    {
        psserver->SendSystemInfo(client->GetClientNum(), "%s isn't a merchant.",merchant->GetCharName());
        return;
    }

    psserver->SendSystemInfo(client->GetClientNum(), "You started trading with %s.",merchant->GetCharName());

    if (type == "SELL")
    {
        csString commandData;
        commandData.Format("<MERCHANT ID=\"%d\" TRADE_CMD=\"%d\" />",
                merchant->GetCharacterID(),psGUIMerchantMessage::SELL);

        psGUIMerchantMessage msg1(clientnum,psGUIMerchantMessage::MERCHANT,commandData);
        msg1.SendMessage();
        character->SetTradingStatus(psCharacter::SELLING,merchant);
    }
    else
    {
        csString commandData;
        commandData.Format("<MERCHANT ID=\"%d\" TRADE_CMD=\"%d\" />",
                merchant->GetCharacterID(),psGUIMerchantMessage::BUY);
        psGUIMerchantMessage msg1(clientnum,psGUIMerchantMessage::MERCHANT,commandData);
        psserver->GetEventManager()->SendMessage(msg1.msg);
        character->SetTradingStatus(psCharacter::BUYING,merchant);
    }

    // Build category list
    csString categoryList("<L>");
    csString buff;

    psMerchantInfo * merchantInfo = merchant->GetMerchantInfo();

    for ( size_t z = 0; z < merchantInfo->categories.Length(); z++ )
    {
        psItemCategory * category = merchantInfo->categories[z];
    csString escpxml = EscpXML(category->name);
        buff.Format("<CATEGORY ID=\"%d\" "
                    "NAME=\"%s\" />",category->id,
                    escpxml.GetData());
        categoryList.Append(buff);
    }
    categoryList.Append("</L>");

    psGUIMerchantMessage msg2(clientnum,psGUIMerchantMessage::CATEGORIES,categoryList.GetData());
    if (msg2.valid)
        psserver->GetEventManager()->SendMessage(msg2.msg);
    else
    {
        Bug2("Could not create valid psGUIMerchantMessage for client %u.\n",clientnum);
    }


    SendPlayerMoney(client);
}

void psServerCharManager::HandleMerchantMessage( MsgEntry* me, Client *client )
{
    psCharacter* character = client->GetCharacterData();

    psGUIMerchantMessage msg(me);
    if (!msg.valid)
    {
        Debug2(LOG_NET,me->clientnum,"Received unparsable psGUIMerchantMessage from client %u.\n",me->clientnum);
        return;
    }

    //    CPrintf(CON_DEBUG, "psServerCharManager::HandleMerchantMessage (%s, %d,%s)\n",
    //           (const char*)client->GetName(),msg.command, (const char*)msg.commandData);

    switch (msg.command)
    {
        // This handles the initial request to buy or sell from a merchant.
        // A list of categories that this merchant handles is sent
        case psGUIMerchantMessage::REQUEST:
        {
            csRef<iDocumentNode> exchangeNode = ParseString(msg.commandData, "R");
            if (!exchangeNode)
                return;
        
            csRef<iDocumentAttribute> attr = exchangeNode->GetAttribute("TARGET");
            csString type = exchangeNode->GetAttributeValue("TYPE");

            gemObject * target = NULL;
            if (attr)
            {
                csString targetName = attr->GetValue();
                target = psserver->GetEntityManager()->GetGEM()->FindObject(targetName);
                if (!target)
                {
                    psserver->SendSystemInfo(client->GetClientNum(), "Merchant '%s' not found.", targetName.GetData());
                    return;
                }
            }
            else
            {
                target = client->GetTargetObject();
                if (!target)
                {
                    psserver->SendSystemInfo(client->GetClientNum(), "You have no target selected.");
                    return;
                }
            }

            BeginTrading(client, target, type);
            break;
        }
        //  This handles
        case psGUIMerchantMessage::CATEGORY:
        {
            csRef<iDocumentNode> merchantNode = ParseString (msg.commandData, "C");
            if (!merchantNode)
                return;

            psCharacter * merchant;
            psMerchantInfo * merchantInfo;

            if (VerifyTrade(client, character,&merchant,&merchantInfo,
                            "category","",merchantNode->GetAttributeValueAsInt("ID")))
            {
                csString category = merchantNode->GetAttributeValue("CATEGORY");
                psItemCategory * itemCategory = merchantInfo->FindCategory(category);
                if (!itemCategory)
                {
                    CPrintf(CON_DEBUG, "Player %s fails to get items in category %s. Unkown category!\n",
                            character->GetCharName(), (const char*)category);
                    return;
                }
                if (!merchant->GetActor()->IsAlive())
                {
                    psserver->SendSystemInfo(client->GetClientNum(), "You can't trade with a dead merchant.");
                    return;
                }

                // Send item list for given category
                if (character->GetTradingStatus() == psCharacter::BUYING)
                {
                    SendMerchantItems( client, merchant, itemCategory );
                }
                else
                {
                    SendPlayerItems( client, itemCategory );
                }
            }
            break;
        }
        case psGUIMerchantMessage::BUY:
        {
            csRef <iDocumentNode> merchantNode = ParseString(msg.commandData, "T");
            if (!merchantNode)
                return;

            csString itemName = merchantNode->GetAttributeValue("ITEM");
            int count = merchantNode->GetAttributeValueAsInt("COUNT");
            int merchantID = merchantNode->GetAttributeValueAsInt("ID");
            uint32 itemID = (uint32)merchantNode->GetAttributeValueAsInt("ITEM_ID");

            psCharacter * merchant;
            psMerchantInfo * merchantInfo;

            if (VerifyTrade(client, character,&merchant,&merchantInfo,
                            "buy",itemName,merchantID))
            {
                psItem * item = merchant->Inventory().FindInBulk(itemID);
                if (!item || count > item->GetStackCount())
                {
                    psserver->SendSystemError(client->GetClientNum(), "Merchant does not have %i %s.", count, itemName.GetData());
                    return;
                }
                if (!merchant->GetActor()->IsAlive())
                {
                    psserver->SendSystemError(client->GetClientNum(),"That merchant is dead");
                    return;
                }
                psMoney price = item->GetPrice();
                psMoney money = character->Money();

                if (price*count > money)
                {
                    psserver->SendSystemError(client->GetClientNum(),"You need more money");
                    return;
                }

                int canFit = (int)character->Inventory().CanFit(item); // count that actually fit into buyer's inventory

                if (count > canFit)
                {
                    count = canFit;
                    // Notify the buyer that their inventory is full.  (will purchase what fits)
                    psserver->SendSystemError(client->GetClientNum(),"Your inventory is full");
                    if (count <= 0) return;
                }

                psItem * newitem = item->Copy(count);
                if (newitem == NULL)
                {
                    Error2("Error: failed to create item %s.", itemName.GetData());
                    psserver->SendSystemError(client->GetClientNum(), "Error: failed to create item %s.", itemName.GetData());
                    return;
                }
                newitem->SetCreator(ITEM_CLONE);
                // If we managed to buy some items, we pay some money
                if ( character->Inventory().PutInBulk(newitem) )
                {
                    psMoney cost;
                    cost = (price * count).Normalized();

                    character->SetMoney(money - cost);
                   
                    psserver->SendSystemOK( client->GetClientNum(), "You bought %d %s for %s a total of %d Trias.",
                                       count, itemName.GetData(), cost.ToUserString().GetDataSafe(),cost.GetTotal());
                
                    psBuyEvent evt(
                        character->GetCharacterID(),
                        merchant->GetCharacterID(),
                        item->GetName(),
                        count,
                        (int)item->GetCurrentStats()->GetQuality(),
                        cost.GetTotal()
                        );
                    evt.FireEvent();
                }
                else
                {
                    // No empty or stackable slot in bulk or any container
                    psserver->SendSystemError(client->GetClientNum(),"You're carrying too many items");
                    CacheManager::GetSingleton().RemoveInstance(newitem);
                    return;
                }

                csString buf;
                buf.Format("%s, %s, %s, \"%s\", %d, %d", client->GetName(), merchant->name.GetData(), "Buy", 
                           itemName.GetData(), count, (price * count).GetTotal());
                psserver->GetLogCSV()->Write(CSV_EXCHANGES, buf);
                
                // Update client views
                SendPlayerMoney( client );
                SendMerchantItems( client, merchant, item->GetCategory() );

                // Update all client views
                UpdateItemViews( client->GetClientNum() );
            }
            break;
        }
        case psGUIMerchantMessage::SELL:
        {
            csRef <iDocumentNode> merchantNode = ParseString(msg.commandData, "T");
            if (!merchantNode)
                return;

            csString itemName = merchantNode->GetAttributeValue("ITEM");
            int count = merchantNode->GetAttributeValueAsInt("COUNT");
            int merchantID = merchantNode->GetAttributeValueAsInt("ID");

            psCharacter * merchant;
            psMerchantInfo * merchantInfo;

            if (VerifyTrade(client, character,&merchant,&merchantInfo,
                            "sell",itemName,merchantID))
            {
                uint32 itemID =(uint32) merchantNode->GetAttributeValueAsInt("ITEM_ID");
                psItem * item = character->Inventory().FindInBulk(itemID);
                if (!item)
                    return;
                if (!merchant->GetActor()->IsAlive())
                {
                    psserver->SendSystemError(client->GetClientNum(), "You can't trade with a dead merchant.");
                    return;
                }

                psMoney price = item->GetSellPrice();
                psMoney money = character->Money();

                count = MIN(count, item->GetStackCount());

                item = character->Inventory().RemoveBulk(character->Inventory().FindItemInTopLevelBulk(item), count);
                if (item == NULL)
                {
                    Error3("RemoveItemInBulk failed while selling to merchant %s %i", item->GetName(), count);
                    return;
                }

                psMoney cost;
                cost = (price * count).Normalized();

                character->SetMoney(money + cost);

                psserver->SendSystemOK( client->GetClientNum(), "You sold %d %s for %s a total of %d Trias.",
                                       count, itemName.GetData(), cost.ToUserString().GetDataSafe(),cost.GetTotal());
                
                // Record
                psSellEvent evt(
                        character->GetCharacterID(),
                        merchant->GetCharacterID(),
                        item->GetName(),
                        count,
                        (int)item->GetCurrentStats()->GetQuality(),
                        cost.GetTotal()
                        );
                evt.FireEvent();

                ServerStatus::sold_items += count;
                ServerStatus::sold_value += (price * count).GetTotal();

                csString buf;
                buf.Format("%s, %s, %s, \"%s\", %d, %d", client->GetName(), merchant->name.GetData(), "Sell", 
                           itemName.GetData(), count, (price * count).GetTotal());
                psserver->GetLogCSV()->Write(CSV_EXCHANGES, buf);

                // Update client views
                SendPlayerMoney( client );
                SendPlayerItems( client, item->GetCategory() );

                // items are not currently given to merchant, they are just destroyed
                CacheManager::GetSingleton().RemoveInstance(item);

                // Update all client views
                UpdateItemViews( client->GetClientNum() );
            }
            break;
        }
        case psGUIMerchantMessage::VIEW:
        {
            csRef <iDocumentNode> merchantNode = ParseString(msg.commandData, "V");
            if (!merchantNode)
                return;

            csString itemName = merchantNode->GetAttributeValue("ITEM");
            int merchantID = merchantNode->GetAttributeValueAsInt("ID");
            uint32 itemID = (uint32)merchantNode->GetAttributeValueAsInt("ITEM_ID");
            int tradeCommand = merchantNode->GetAttributeValueAsInt("TRADE_CMD");

            psCharacter * merchant;
            psMerchantInfo * merchantInfo;

            if (VerifyTrade(client, character,&merchant,&merchantInfo,
                            "view",itemName,merchantID))
            {
                if (!merchant->GetActor()->IsAlive())
                {
                    psserver->SendSystemInfo(client->GetClientNum(), "You can't trade with a dead merchant.");
                    return;
                }
                psItem * item;
                if (tradeCommand == psGUIMerchantMessage::SELL)
                    item = character->Inventory().FindInBulk(itemID);
                else
                    item = merchant->Inventory().FindInBulk(itemID);

                if (!item)
                {
                    CPrintf(CON_DEBUG, "Player %s failed to view item %s. No item!\n",
                            client->GetName(), (const char*)itemName);
                    return;
                }

                // check for container items
                if (item->GetIsContainer())
                {
                    SendItemContents(client, item);
                }
                else
                {
                    SendItemDescription(client, item);
                }
            }
            break;
        }
        case psGUIMerchantMessage::CANCEL:
        {
            character->SetTradingStatus(psCharacter::NOT_TRADING,0);
/*            psGUIMerchantMessage newmsg(me->clientnum,psGUIMerchantMessage::CANCEL,"");
            if (newmsg.valid)
            {
                psserver->GetEventManager()->SendMessage(newmsg.msg);
            }
            else
            {
                Bug2("Could not create valid psGUIMerchantMessage for client %u.\n",me->clientnum);
            }*/
            break;
        }

    }
}

bool psServerCharManager::VerifyTrade( Client * client, psCharacter * character, psCharacter ** merchant, psMerchantInfo ** info,
                                       const char * trade,const char * itemName, unsigned int merchantID)
{
    *merchant = character->GetMerchant();
    if (!*merchant)
    {
        CPrintf(CON_DEBUG, "Player %s failed to %s item %s. No merchant!\n",
                trade, character->GetCharName(), itemName);
        return false;
    }
    *info = (*merchant)->GetMerchantInfo();
    if (!*info)
    {
        CPrintf(CON_DEBUG, "Player %s failed to %s item %s. No merchant info!\n",
                trade, character->GetCharName(), itemName);
        return false;
    }
    // Check if player is trading with this merchant.
    if (character->GetTradingStatus() == psCharacter::NOT_TRADING)
    {
        CPrintf(CON_DEBUG, "Player %s failed to %s item %s. No trading status!\n",
                trade, character->GetCharName(), itemName);
        return false;
    }
    // Check if this is correct merchant
    if (merchantID != (*merchant)->GetCharacterID())
    {
        CPrintf(CON_DEBUG, "Player %s failed to %s item %s. Different merchant!\n",
                trade, character->GetCharName(), itemName);
        return false;
    }
    // Check range
    if (character->GetActor()->RangeTo((*merchant)->GetActor()) > RANGE_TO_SELECT)
    {
        psserver->SendSystemInfo(client->GetClientNum(),"Merchant is out of range.");
        CPrintf(CON_DEBUG, "Player %s failed to %s item %s. Out of range!\n",
                trade, character->GetCharName(), itemName);
        return false;
    }

    return true;
}

void psServerCharManager::SendOutPlaySoundMessage( int clientnum, const char* itemsound, const char* action )
{
    if (clientnum == 0 || itemsound == NULL || action == NULL)
        return;

    csString sound = itemsound;

    if (sound == "item.nosound")
        return;

    sound += ".";
    sound += action;
    
    Debug3(LOG_SOUND,clientnum,"Sending sound %s to client %d", sound.GetData(), clientnum);

    MsgEntry* msg = new MsgEntry( sound.Length() + 1 );
    msg->SetType(MSGTYPE_PLAYSOUND);
    msg->clientnum = clientnum;
    msg->Add(sound);

    psserver->GetEventManager()->SendMessage(msg);
    
// TODO: Sounds should really be multicasted, so others can hear them
//    psserver->GetEventManager()->Multicast(msg, fromClient->GetActor()->GetMulticastClients(), 0, range );
}


void psServerCharManager::SendOutEquipmentMessages( unsigned int client,
                                                    int slotName,
                                                    psItem* item,
                                                    int equiped )
{
    SendOutEquipmentMessages( clients->Find(client), slotName, item, equiped );
}                                                    


void psServerCharManager::SendOutEquipmentMessages( Client* fromClient,
                                                    int slotName,
                                                    psItem* item,
                                                    int equiped )
{
    if (fromClient == NULL || item == NULL)
        return;

    SendOutEquipmentMessages( fromClient->GetActor(), slotName, item, equiped );
}

void psServerCharManager::SendOutEquipmentMessages( gemActor* actor,
                                                    int slotName,
                                                    psItem* item,
                                                    int equiped )
{

    PS_ID eid = actor->GetEntity()->GetID();

    csString mesh = item->GetMeshName();
    csString part = item->GetPartName();
    csString texture = item->GetTextureName();
    csString partMesh = item->GetPartMeshName();

    /* Weild: mesh in a slot (no part or texture)
     * Wear: mesh for standalone; texture on a part when worn
     *
     * We'll send the info the client needs, and it figures the rest out.
     */
    if (part.Length() && texture.Length()) mesh.Clear();

    psEquipmentMessage msg( 0, eid, equiped, slotName, mesh, part, texture, partMesh );
    CS_ASSERT( msg.valid );
    
    psserver->GetEventManager()->Multicast( msg.msg,
                                            actor->GetMulticastClients(),
                                            0, // Multicast to all without exception
                                            PROX_LIST_ANY_RANGE );
}

void psServerCharManager::SendPlayerMoney( Client *client )
{
    csString buff;

    if (client->GetCharacterData()==NULL)
        return;

    psMoney money=client->GetCharacterData()->Money();

    csString money_str = money.ToString();
    buff.Format("<M MONEY=\"%s\" />",money_str.GetData());

    psGUIMerchantMessage msg(client->GetClientNum(),
                              psGUIMerchantMessage::MONEY,buff);
    psserver->GetEventManager()->SendMessage(msg.msg);
}



bool psServerCharManager::SendItemDescription( Client *client, psItem *item)
{
    if (item==NULL)
        return false;

    unsigned int stackCount = item->GetStackCount();
    csString itemInfo, weight, itemQuality, itemCategory;

    psItemStats* itemStats = item->GetCurrentStats();

    if(item->GetIsLocked())
    {
        itemInfo = "This item is locked\n\n";
    }

    itemCategory.Format( "Category: %s", itemStats->GetCategory()->name.GetData() );
    weight.Format("\nWeight: %.2f", item->GetSumWeight() );
    itemQuality.Format("\nQuality: %.1f/%1.0f", item->GetItemQuality(),item->GetMaxItemQuality() );
    itemInfo += itemCategory+weight+itemQuality;

    // Item was crafted
    if ( item->GetCrafterID() != 0 )
    {
        // Crafter and guild names
        csString crafterInfo;
        psCharacter* charData = psServer::CharacterLoader.LoadCharacterData( item->GetCrafterID(),false );
        if ( charData )
        {
            crafterInfo.Format( "\n\nCrafter: %s", charData->GetCharFullName());
            itemInfo += crafterInfo;
        }

        // Item was crafted by a guild member
        if ( item->GetGuildID() != 0 )
        {
            csString guildInfo;
            psGuildInfo* guild = CacheManager::GetSingleton().FindGuild( item->GetGuildID() );
            if ( guild )
            {
                guildInfo.Format( "\nGuild: %s", guild->GetName().GetData());
                itemInfo += guildInfo;
            }
        }
    }

    // Item is a weapon
    if ( item->GetCategory()->id == 1 )
    {
        csString speed, damage;
        // Weapon Speed
        speed.Format( "\n\nSpeed: %.2f", itemStats->Weapon().Latency() );
        
        // Weapon Damage Type
        damage = "\n\nDamage:";
        float dmgSlash, dmgBlunt, dmgPierce;
        dmgSlash = item->GetDamage(PSITEMSTATS_DAMAGETYPE_SLASH);
        dmgBlunt = item->GetDamage(PSITEMSTATS_DAMAGETYPE_BLUNT);
        dmgPierce = item->GetDamage(PSITEMSTATS_DAMAGETYPE_PIERCE);

        // Only worth printing if their value is not zero
        if ( dmgSlash )
            damage += csString().Format( "\n Slash: %.2f", dmgSlash );
        if ( dmgBlunt )
            damage += csString().Format( "\n Blunt: %.2f", dmgBlunt );
        if ( dmgPierce )
            damage += csString().Format( "\n Pierce: %.2f", dmgPierce );

        itemInfo+= speed + damage;
    }

    itemInfo += "\n\nDescription: ";
    itemInfo += item->GetDescription();
   
    psViewItemDescription outgoing( client->GetClientNum(), item->GetName(), itemInfo.GetData(), item->GetImageName(), stackCount );

    if ( outgoing.valid )
        psserver->GetEventManager()->SendMessage(outgoing.msg);
    else
    {
        Bug2("Could not create valid psViewItemDescription for client %u.\n",client->GetClientNum());
        return false;
    }

    return true;
}

bool psServerCharManager::SendItemContents( Client *client, psItem *item, int parentContainer, int containerID)
{
    if (item==NULL)
        return false;
    
    if (item->GetIsLocked())
        return SendItemDescription(client,item);

    csString name( item->GetName() );
    csString icon( item->GetImageName() );
    csString desc( item->GetDescription() );

    desc.AppendFmt("\n\nWeight: %.2f\nCapacity: %u/%u",
                    item->GetSumWeight(), 
                    item->GetContainedSize(),
                    item->GetContainerMaxSize() );

    psViewItemDescription outgoing( client->GetClientNum(),
                                    name,
                                    desc,
                                    icon,
                                    0,
                                    IS_CONTAINER,
                                    parentContainer );
                                          
    if (item->GetGemObject() != NULL )
        outgoing.containerID = item->GetGemObject()->GetEntity()->GetID();
    else
        outgoing.containerID = containerID;        

    FillContainerMsg( item, client, outgoing );
    
    outgoing.ConstructMsg();
    psserver->GetEventManager()->SendMessage(outgoing.msg);

    return true;
}

bool psServerCharManager::SendActionContents( Client *client, psActionLocation *action, csRef<iDocumentNode> topNode,
                                              psItem *item, int parentContainer, int containerID)
{
    if ( action == NULL )
        return false;

    if ( item == NULL )
        return false;
    
    //if(item->GetIsLocked())
    //    return SendItemDescription(client,item);

    csString name( action->name );
    csString desc( item->GetDescription() );
    csString icon( item->GetImageName() );

    csRef<iDocumentNode> description = topNode->GetNode( "Description" );
    if ( description )
    {
        desc = description->GetContentsValue();
    }

    bool isContainer = item->GetIsContainer();
        
    psViewItemDescription outgoing( client->GetClientNum(),
                                    name,
                                    desc,
                                    icon,
                                    0,
                                    isContainer,
                                    parentContainer );

    /* REMOVED: was probably there to avoid a crash, remove after some testing.
    if (action->GetGemObject() != NULL )
        outgoing.containerID = action->GetGemObject()->GetEntity()->GetID();
    else
        outgoing.containerID = containerID;
    */

    outgoing.containerID = item->GetGemObject()->GetEntity()->GetID();

    if ( isContainer )
    {           
        FillContainerMsg( item, client, outgoing );
        outgoing.ConstructMsg();    
    }
    
    outgoing.SendMessage();
    return true;
}


void psServerCharManager::FillContainerMsg( psItem* item, Client* client, psViewItemDescription& outgoing )
{
    psContainerIterator* it = new psContainerIterator(item);
    while (it->HasNext())
    {
        psItem* child = it->Next();
        // No owner... Add item visible to everybody.
        if ( child->GetOwningCharacterID() == 0  )
        {
            outgoing.AddContents( child->GetName(),  child->GetImageName(), child->GetLocInParent(), child->GetStackCount());
        }        
            
        // The owner of the item is the player. 
        else if ( child->GetOwningCharacterID() == client->GetCharacterData()->characterid )               
        {
            outgoing.AddContents( child->GetName(), child->GetImageName(), child->GetLocInParent(), child->GetStackCount());
        }                        
        // otherwise it's somebody else's stuff
        else
        {
            const char *icon = "/this/art/gui/slottaken_icon.png" ;
            outgoing.AddContents( child->GetName(), icon, child->GetLocInParent(), -1);
        }                
    }        
    delete it;
}


bool psServerCharManager::SendBookText( Client *client, psItem *item)
{
    if (item==NULL)
        return false;

    csString name( item->GetName() );
    csString desc( item->GetDescription() );

    if(item->GetIsLocked())
    {
        desc = "This item is locked\n";
    }

    psReadBookTextMessage outgoing( client->GetClientNum(), name, desc );
    if (outgoing.valid)
    {
        psserver->GetEventManager()->SendMessage(outgoing.msg);
    }
    else
    {
        Bug2("Could not create valid psReadBookText for client %u.\n",client->GetClientNum());
        return false;
    }

    return true;
}


bool psServerCharManager::SendMerchantItems( Client *client, psCharacter* merchant, psItemCategory* category)
{
    csArray<psItem*> items = merchant->Inventory().GetItemsInCategory(category);

    // Build item list
    csString buff("<L>");
    csString item;

    for ( size_t z = 0; z < items.Length(); z++ )
    {
        csString escpxml_name = EscpXML(items[z]->GetName());
        csString escpxml_imagename = EscpXML(items[z]->GetImageName());
        item.Format("<ITEM ID=\"%u\" "
                    "NAME=\"%s\" "
                    "IMG=\"%s\" "
                    "PRICE=\"%i\" "
                    "COUNT=\"%i\" />",
                    items[z]->GetUID(),
                    escpxml_name.GetData(),
                    escpxml_imagename.GetData(),
                    items[z]->GetPrice().GetTotal(),
                    items[z]->GetStackCount());

        buff.Append(item);
    }
    buff.Append("</L>");

    psGUIMerchantMessage msg4(client->GetClientNum(),psGUIMerchantMessage::ITEMS,buff.GetData());
    if (msg4.valid)
        psserver->GetEventManager()->SendMessage(msg4.msg);
    else
    {
        Bug2("Could not create valid psGUIMerchantMessage for client %u.\n",client->GetClientNum());
    }


    return true;
}

bool psServerCharManager::SendPlayerItems( Client *client, psItemCategory* category)
{
    csArray<psItem*> items = client->GetCharacterData()->Inventory().GetItemsInCategory(category);

    // Build item list
    csString buff("<L>");
    csString item;
    csString itemID;

    for ( size_t z = 0; z < items.Length(); z++ )
    {
        itemID.Format("%u",items[z]->GetUID());
        csString purified;

        if (items[z]->GetPurifyStatus() == 2)
            purified = "yes";
        else
            purified = "no";

        csString escpxml_name = EscpXML(items[z]->GetName());
        csString escpxml_imagename = EscpXML(items[z]->GetImageName());
        item.Format("<ITEM ID=\"%s\" "
                    "NAME=\"%s\" "
                    "IMG=\"%s\" "
                    "PRICE=\"%i\" "
                    "COUNT=\"%i\" " 
                    "PURIFIED=\"%s\" />",
                    itemID.GetDataSafe(),
                    escpxml_name.GetData(),
                    escpxml_imagename.GetData(),
                    items[z]->GetSellPrice().GetTotal(),
                    items[z]->GetStackCount(),
                    purified.GetData());

        buff.Append(item);
    }
    buff.Append("</L>");

    psGUIMerchantMessage msg4(client->GetClientNum(),psGUIMerchantMessage::ITEMS,buff.GetData());
    if (msg4.valid)
        psserver->GetEventManager()->SendMessage(msg4.msg);
    else
    {
        Bug2("Could not create valid psGUIMerchantMessage for client %u.\n",client->GetClientNum());
    }


    return true;
}

bool psServerCharManager::VerifyGoal(Client* client, psCharacter* character, psItem* goal)
{
    // glyph items can't be goals
    if (goal->GetCurrentStats()->GetIsGlyph())
        return false;

    return true;
}


bool psServerCharManager::IsUnique( const char* name )
{
    csString escape;
    db->Escape( escape, name );
    // Check to see if name already exists in character database.
    csString query;
    query.Format( "Select id from characters where name='%s'", escape.GetData() );
    Result result (db->Select( query ) );
    if ( result.IsValid() && result.Count() >= 1 )
        return false;
    else
        return true;
}

bool psServerCharManager::IsLastNameUnique( const char* lastname )
{
    if (lastname && strlen(lastname))
    {
        // Check to see if name already exists in character database.
        csString query;
        csString escape;
        db->Escape(escape, lastname);
        query.Format( "Select id from characters where lastname='%s'", escape.GetData() );
        Result result (db->Select( query ) );
        if ( result.IsValid() && result.Count() >= 1 )
            return false;
        else
            return true;
    }
    else
        return true;  // blank last name is now allowed.
}


int psServerCharManager::IsReserved( const char* name, int acctID )
{
    // Check to see if this name is reserved.  Does this check by comparing
    // the email address of the account with that stored in the migration table.
    csString query;
    csString escape;
    db->Escape( escape, name );
    query.Format( "SELECT m.email FROM migration m WHERE m.username='%s'", escape.GetData() );
    Result result (db->Select( query ) );

    if ( result.IsValid() && result.Count() == 1 )
    {
        csString savedEmail( result[0][0] );

        query.Format( "SELECT username FROM accounts WHERE id=%d\n", acctID );

        Result result2(db->Select( query ) );
        if ( result2.IsValid() && result2.Count() == 1 )
        {
            csString email( result2[0][0] );
            if ( savedEmail.CompareNoCase(email) )
                return NAME_RESERVED_FOR_YOU;
            else
                return NAME_RESERVED;
        }
    }

    return NAME_AVAILABLE;
}


void psServerCharManager::AssignScript( psCharacter* character )
{
    csString query;
    csString escape;
    db->Escape( escape, character->GetCharName() );
    query.Format( "SELECT script FROM migration where username='%s' AND done !='Y'", escape.GetData());
    Result result (db->Select( query ) );

    if ( result.IsValid() && result.Count() == 1 )
    {
        csString script(result[0][0]);
        character->AppendProgressionScript( script );
        printf("Doing it: %s\n",character->progressionScript.GetData());
    }
    
    query.Format("UPDATE migration SET done='Y' WHERE username='%s'",escape.GetData());
    db->Command(query);
}


    


//------------------------------------------------------------------------------

SlotManager::SlotManager()
{
    holdingContainer = NULL;
    holdingItem = NULL;
}

SlotManager::~SlotManager()
{
    psserver->GetEventManager()->Unsubscribe( this, MSGTYPE_SLOT_MOVEMENT );
}

bool SlotManager::Initialize()
{
    psserver->GetEventManager()->Subscribe( this, MSGTYPE_SLOT_MOVEMENT, REQUIRE_READY_CLIENT );
    return true;
}


void SlotManager::HandleMessage( MsgEntry* me, Client *fromClient )
{
    this->fromClient = fromClient;
    // This is the only type of message we should handle
    if ( me->GetType() != MSGTYPE_SLOT_MOVEMENT )
    {
        return;
    }

    psSlotMovementMsg mesg( me );

    // If stacks are less than 1 then we should not do anything server wise.
    // Infact we should probably log as some client is doing something wrong.
    if ( mesg.stackCount < 1 )
    {
        Error2("Got a slot movement message with stack count 0 from client %s.",fromClient->GetName());
        return;
    }

    clientNum = me->clientnum;
    charData = fromClient->GetCharacterData();
    if ( !charData )
    {
        Error2("Could not find Character Data for client: %s", fromClient->GetName() );
        return;
    }

    holdingContainer    = NULL;
    holdingSlotID       = mesg.fromSlot;
    holdingContainerID  = mesg.fromContainer;
    holdingParentContainerID = mesg.fromParentContainer;

    holdingItem = FindHoldingItem( fromClient, mesg );

    // Did we actually find an item to move?
    if ( holdingItem == NULL )
    {
        Error2("Could not find item in specified slot for client: %s", fromClient->GetName() );
        return;
    }

    printf("Holding item is a %s with owner %s, count %d.\n",
           holdingItem->GetName(),
           (holdingItem->GetOwningCharacter()) ? holdingItem->GetOwningCharacter()->GetCharName() : "None",
           holdingItem->GetStackCount() );

    if (holdingItem->IsInUse() )
    {
        psserver->SendSystemError(me->clientnum, "You cannot do anything with that item while you are using it.");
        return;
    }
    
    if (mesg.fromContainer == CONTAINER_INVENTORY_EQUIPMENT)
        UnequipHolding(mesg);

    if ( !fromClient->IsAlive() )
    {       
        psserver->SendSystemError(clientNum,"Cannot do that when you are dead!");
    }
    else if (mesg.toParentContainer != CONTAINER_INVENTORY_BULK && mesg.toContainer == CONTAINER_EXCHANGE_OFFERING)
    {
        PlaceItemInExchange(mesg);
    }
    else
    {
        PlaceHoldingItem(mesg);
    }

    if(holdingItem)
    {
        if(holdingContainerID == CONTAINER_EXCHANGE_OFFERING)
        {
            Exchange* exchange = psserver->exchangemanager->GetExchange( fromClient->ExchangeID() );
            if (exchange->AddItem( fromClient, holdingItem, mesg.fromSlot))
            {
                holdingItem = NULL;
            }
        }
        else
            ReplaceHolding();
    }

    psserver->GetCharManager()->SendInventory(me->clientnum);
}


void SlotManager::ReplaceHolding()
{    
    switch ( holdingContainerID )
    {
        case CONTAINER_INVENTORY_BULK:
        {
            charData->Inventory().PutInBulk( holdingItem, holdingSlotID );
            psserver->GetCharManager()->UpdateItemViews( fromClient->GetClientNum() );
            break;
        }
       
        case CONTAINER_INVENTORY_MONEY:
        {
            psMoney money = charData->Money();             
            money.Adjust( holdingSlotID, holdingItem->GetStackCount() );
            charData->SetMoney( money );

            /// We need to remove this item since it no longer exists.
            CacheManager::GetSingleton().RemoveInstance(holdingItem);
            break;
        }

        case CONTAINER_INVENTORY_EQUIPMENT:
        {
            if ( charData->Inventory().EquipIn( holdingSlotID, holdingItem ) )
                psserver->GetCharManager()->SendOutEquipmentMessages( fromClient, holdingSlotID, holdingItem, psEquipmentMessage::EQUIP);

            break;
        }

        default:
        {
            if ( holdingContainer )
            {
                holdingContainer->PutItemsInContainer( holdingSlotID, holdingItem, charData );
                //psserver->GetCharManager()->SendItemContents( fromClient, holdingContainer );
                psserver->GetCharManager()->ViewItem( fromClient,holdingContainerID, 0 , 0 );
            }
            break;
        }
    }
    
    holdingItem = NULL;
}

psItem* SlotManager::FindItem( Client* client, int containerID, int slotID, int parentID )
{
    // Container inside something
    if ( parentID != 0 )
    {
        psItem* container = FindItem(client,parentID,containerID);
        if ( container )
            return container->GetItemInSlot(slotID);
        else
            Error3("Could not find container %d inside parent %d", containerID, parentID);
    }

    switch ( containerID )
    {
        case CONTAINER_INVENTORY_BULK:
        {
            return client->GetCharacterData()->Inventory().GetBulkItem( slotID );
        }
        case CONTAINER_INVENTORY_EQUIPMENT:
        {
            return client->GetCharacterData()->Inventory().GetEquipmentItem( slotID );
        }

        case CONTAINER_EXCHANGE_OFFERING:
        {
            Exchange* exchange = psserver->GetExchangeManager()->GetExchange( client->ExchangeID() );
            if (!exchange) return NULL;

            // Container is different from each perspective
            if (exchange->GetStarterClient() == client)
                return exchange->GetStarterOffer(slotID);
            else
                return exchange->GetTargetOffer(slotID);
        }
        case CONTAINER_EXCHANGE_RECEIVING:
        {
            Exchange* exchange = psserver->GetExchangeManager()->GetExchange( client->ExchangeID() );
            if (!exchange) return NULL;

            // Container is different from each perspective
            if (exchange->GetStarterClient() == client)
                return exchange->GetTargetOffer(slotID);
            else
                return exchange->GetStarterOffer(slotID);
        }

        default:  // Find container in world
        {
            gemObject* object = psserver->GetEntityManager()->GetGEM()->FindObject( containerID );
            if ( object )
                return object->GetItem();
            else
            {
                Error2("No Object %d found", containerID );
                return NULL;
            }
        }
    }
}

psItem* SlotManager::FindContainer( Client* client,  int containerID, int parentContainerID  )
{
    // Is this container in another container?
    if ( parentContainerID != 0 )
    {
        psItem* container = FindItem(client,parentContainerID,containerID);
        if (container)
            return container;
        else
            Error3("Could not find container %d inside parent %d", containerID, parentContainerID);
    }

    // Find container in world
    gemObject* object = psserver->GetEntityManager()->GetGEM()->FindObject( containerID );
    psItem* container = NULL;
    if ( !object )
    {
        Error2("No Object %d found", containerID );
        return NULL;
    }
    else
    {
        container = object->GetItem();
        if ( !container )
        {
            psActionLocation *action = psserver->GetActionManager()->FindAction( containerID );
            if ( !action )
            {
                Error2( "No item/action : %d", containerID );
                return NULL;
            }
            else
            {
                if ( action->response.StartsWith( "<Examine>", false ) )
                {
                    // load response into XML doc
                    csRef<iDocument>     doc     = ParseString( action->response );    
                    if(!doc)
                    {
                        Error1("Parse error in action response");
                        return NULL;
                    }
                    csRef<iDocumentNode> root    = doc->GetRoot();
                    if(!root)
                    {
                        Error1("No XML root in action response");
                        return NULL;
                    }
                    csRef<iDocumentNode> topNode = root->GetNode( "Examine" );
                    if(!topNode)
                    {
                        Error1("No <examine> tag in action response");
                        return NULL;
                    }
                    csRef<iDocumentNode> containerNode, descriptionNode;

                    containerNode = topNode->GetNode( "Container" );
                    if ( containerNode ) 
                    {
                        //item = slotManager->FindItem( client, containerNode->GetAttributeValueAsInt( "ID" ), 0, 0 );

                        // if it's an 'Item'
                        // Get psItem and check uid
                        // set item = currentGem;

                        // Get all entities (gemObjects)
                        csHash<gemObject *>& gems = psserver->GetEntityManager()->GetGEM()->GetAllGEMS();

                        csHash<gemObject *>::GlobalIterator i(gems.GetIterator());
                        gemObject* obj;
                        int id = containerNode->GetAttributeValueAsInt( "ID" );

                        // Iterate through each one
                        while ( i.HasNext() )
                        {
                            obj = i.Next();
                            if (obj)
                            {
                                gemItem *gItem = dynamic_cast<gemItem*>(obj);
                                if (gItem) {

                                    psItem *pItem = gItem->GetItem();

                                    if ( pItem->GetUID() == id )
                                    {
                                        container = pItem;
                                        break;
                                    }
                                }
                            }
                        }

                        if ( !container )
                        {
                            Error1("Container PS item could not be found");
                            return NULL;
                        }
                        else
                        {
                            return container;
                        }
                    }
                }
            }
        }
        else
        {
            if ( !container->GetIsContainer() )
            {
                Error1("This thing is not a container");
                return NULL;
            }
            else
            {
                return container;
            }
        }
    }

    return NULL;
}


psItem* SlotManager::FindHoldingItem( Client* fromClient, psSlotMovementMsg& mesg )
{
//    psItem* holdingItem=NULL;
/*    // Check to see if this is from a container in the inventory.
    if ( mesg.fromParentContainer != 0 )
    {
        holdingContainer = FindContainer( fromClient, mesg.fromContainer, mesg.fromParentContainer );
        if ( holdingContainer )
            holdingItem = holdingContainer->RemoveItemsInContainer( mesg.fromSlot, mesg.stackCount );
        else
            Error3("Could not find container %d inside parent %d", mesg.fromContainer, mesg.fromParentContainer);
    }
    // The item came from elsewhere so figure out where.
    else
    { */
        switch ( mesg.fromContainer )
        {   
            case CONTAINER_INVENTORY_MONEY:
            {               
                psMoney money = charData->Money(); 
                int count  = money.Get( mesg.fromSlot ) - mesg.stackCount;
                if ( count < 0 ) 
                {
                    Error2("Client %s is trying to cheat by grabbing too much money", charData->GetCharName() );
                    break;
                }                
                money.Adjust( mesg.fromSlot, -mesg.stackCount);
                charData->SetMoney( money );

                holdingItem = MakeMoneyItem( mesg.fromSlot, mesg.stackCount );
                break;
            }    
            
            case CONTAINER_INVENTORY_BULK:
            {
                holdingItem = charData->Inventory().RemoveBulk( mesg.fromSlot, mesg.stackCount );
                break;
            }            
            
            case CONTAINER_INVENTORY_EQUIPMENT:
            {
                holdingItem = charData->Inventory().RemoveEquipment( mesg.fromSlot );

                // stop any work being done on item removed from equip slot
                psserver->GetWorkManager()->StopWork(fromClient, holdingItem);
                break;
            }

            case CONTAINER_EXCHANGE_OFFERING:
            {
                int dummy;
                Exchange* exchange = psserver->exchangemanager->GetExchange( fromClient->ExchangeID() );            
                if ( !exchange )
                {
                    break;
                }
                holdingItem = exchange->RemoveItem( fromClient,  mesg.fromSlot, mesg.stackCount, dummy );
                break;
            }

            case CONTAINER_OFFERING_MONEY:
            {
                Exchange* exchange = psserver->exchangemanager->GetExchange( fromClient->ExchangeID() );
                if ( !exchange )
                    break;
                    
                int n = exchange->GetMoneyCount(fromClient, mesg.fromSlot);
                if (n < mesg.stackCount)
                {
                    Error2("Client %s is trying to cheat by grabbing too much money from exchange", charData->GetCharName() );
                    break;
                }
                exchange->AdjustMoney(fromClient, mesg.fromSlot, -mesg.stackCount);
                holdingItem = MakeMoneyItem( mesg.fromSlot, mesg.stackCount );
                break;
            }

            default:
            {
                holdingContainer = FindContainer( fromClient, mesg.fromContainer, mesg.fromParentContainer );

                if ( holdingContainer && holdingContainer->GetGemObject() )  // Container exists in world
                {
                    // Check range ignoring Y co-ordinate
                    if ( fromClient->GetActor()->RangeTo(holdingContainer->GetGemObject(), true) > RANGE_TO_USE )
                    {
                        psserver->SendSystemInfo(clientNum,"You are not in range to remove from %s.",holdingContainer->GetName());            
                        psserver->GetCharManager()->ViewItem(fromClient, mesg.fromContainer, mesg.fromSlot, mesg.fromParentContainer);
                        break;
                    }

                    holdingItem = holdingContainer->RemoveItemsInContainer( mesg.fromSlot, mesg.stackCount );

                    // stop any work being done on item removed from container
                    psserver->GetWorkManager()->StopWork(fromClient, holdingItem);

                    // If items left in the slot do some work. 
                    psItem * item = holdingContainer->GetItemInSlot(mesg.fromSlot);
                    if ( item )
                    {
                        // Stop any work going on with existing item.
                        psserver->GetWorkManager()->StopWork(fromClient, item);

                        // Try to autotransform what is left. 
                        psserver->GetWorkManager()->StartAutoWork(fromClient, holdingContainer, item, item->GetStackCount());
                    }
                }
                else if (holdingContainer)
                {
                    // Containers in character's inventory
                    holdingItem = holdingContainer->RemoveItemsInContainer(mesg.fromSlot, mesg.stackCount);
                }
                break;
            }
        }        
//    }    
    
    return holdingItem;
}


psItem* SlotManager::MakeMoneyItem( int slot, int stackCount )
{                
    psItem* holdingItem =  new psItem();
    holdingItem->SetCreator(ITEM_MONEY);
    csString type("Tria");
            
    switch( slot )
    {
        case MONEY_TRIAS: type="Tria"; break;
        case MONEY_HEXAS: type="Hexa"; break;
        case MONEY_OCTAS: type="Octa"; break;
        case MONEY_CIRCLES: type="Circle"; break;
        default:
            Error2("Unknown fromSlot: %d", slot );
            break;
    }
            
    psItemStats * stat = CacheManager::GetSingleton().GetBasicItemStatsByName(type);
    if ( !stat )
    {
        Error2("Could not load basic item stat for %s", type.GetData() );
    }        
    else
    {   
        holdingItem->SetBaseStats(stat);
        holdingItem->SetStackCount( stackCount );
    }
    
    holdingItem->SetLoaded();  // Loaded and ready

    return holdingItem;                                                            
}


void SlotManager::PlaceItemInEquipment( psSlotMovementMsg& mesg )
{
    csArray<int> fitsIn;
                
    // -1 means add to any slot or consume
    if ( mesg.toSlot == -1 )
    {
        if ( holdingItem->GetBaseStats()->GetIsConsumable() )
        {
            if ( Consume(holdingItem) )
            {
                holdingItem = NULL;
                return;
            }
        }
                    
        //If not consumable then get the list of equipment
        //slots this item can go in and iterate over them all
        //to find the first ( if any ) empty one.                    
        fitsIn = holdingItem->GetBaseStats()->GetSlots();                    
    }
    else
    {
        // See if we're trying to drop an item into an equipped container
        if ( charData->Inventory().PutInEquipped(mesg.toSlot, holdingItem) )
        {
            holdingItem = NULL;
            return;
        }

        // Otherwise, just push in the slot that was picked.
        fitsIn.Push( mesg.toSlot );
    }

    // Only ammo or equiped stackable can be stacked inside an inventory slot.
    if ( holdingItem->GetStackCount() > 1 &&
        !(holdingItem->GetBaseStats()->GetFlags() & PSITEMSTATS_FLAG_IS_EQUIP_STACKABLE))
    {
        psserver->SendSystemError(charData->GetActor()->GetClientID(),"Cannot equip %d %s's in a slot!", holdingItem->GetStackCount(), holdingItem->GetName() );        
        return;
    }
    
    for ( size_t idx = 0; idx < fitsIn.Length(); idx++ )
    {   
        int toSlot = fitsIn[idx];                                 
        psItem* toItem = charData->Inventory().GetEquipmentItem( toSlot );
        // If there is an item already here then this is not allowed for equipment.
        // The better thing would be to do a swap but that is more complex and
        // will be a future item.
        if ( !toItem )
        {
            if ( holdingItem->FitsInSlots( CacheManager::GetSingleton().slotMap[toSlot]) )
            {
                if ( charData->Inventory().EquipIn(toSlot, holdingItem) )
                {
                    psserver->GetCharManager()->SendOutEquipmentMessages( fromClient, toSlot, holdingItem, psEquipmentMessage::EQUIP);

                    psserver->GetCharManager()->SendOutPlaySoundMessage(fromClient->GetClientNum(), holdingItem->GetSound(), "equip" );

                    csString script = holdingItem->GetBaseStats()->GetProgressionEventEquip();                                    
                    if ( script.Length() > 0 )
                    {
                        gemActor *actor = charData->GetActor();
                        psserver->GetProgressionManager()->ProcessEvent(script,  actor );
                    }
                    holdingItem = NULL;
                    return;
                }                                
            }
        }
    }
}                



void SlotManager::PlaceHoldingItem( psSlotMovementMsg& mesg )
{   
/*  This code prevents the default condition of the below switch to be called for free standing containers.
    It needs to be fixed to support equipped containers.
    if ( mesg.toParentContainer != 0 )
    {        
        psItem* container = FindContainer( fromClient, mesg.toContainer, mesg.toParentContainer );
        if ( container )
        {           
            PS_CONTAINER_ERROR result = container->PutItemsInContainer( mesg.toSlot, holdingItem, charData);
            if ( result == PS_CONT_OK )
            {
                holdingItem = NULL;
                psserver->GetCharManager()->SendItemContents( fromClient, container, mesg.toParentContainer, mesg.toContainer );
            }
            else
            {
                SendContainerError( result, fromClient );
            }
        }
        else
            Error3("Could not find container %d inside parent %d", mesg.fromContainer, mesg.fromParentContainer);        
    }
    else
*/    {
        switch ( mesg.toContainer )
        {   
            case CONTAINER_INVENTORY_MONEY:
            {   
                if ( mesg.fromContainer == CONTAINER_INVENTORY_MONEY || 
                     mesg.fromContainer == CONTAINER_OFFERING_MONEY )
                {
                    psMoney money = charData->Money(); 
                    money.Adjust( mesg.fromSlot, mesg.stackCount);
                    charData->SetMoney( money );
                    psserver->GetCharManager()->UpdateItemViews(clientNum);
                    
                    /// We need to remove this item since it no longer exists.
                    CacheManager::GetSingleton().RemoveInstance(holdingItem);
                }
                break;
            }
        
            case CONTAINER_OFFERING_MONEY:
            {
                if ( mesg.fromContainer == CONTAINER_INVENTORY_MONEY ||
                    mesg.fromContainer == CONTAINER_OFFERING_MONEY )
                {
                    Exchange* exchange = psserver->exchangemanager->GetExchange( fromClient->ExchangeID() );
                    if ( !exchange )
                    {
                        Error1( "Client tried to drop to non existant exchange" );
                    }
                    else
                    {
                        exchange->AdjustMoney(fromClient, mesg.fromSlot, mesg.stackCount); 

                        /// Delete the money item since it has been placed in the exchange area.
                        CacheManager::GetSingleton().RemoveInstance(holdingItem);
                    }                
                }
                break;
            }
                
            case CONTAINER_WORLD:
            {
                // If you are holding it, the world now owns it
                holdingItem->SetOwningCharacter(NULL, true);

                // Drop item in front of the character
                charData->DropItem( holdingItem,
                                    holdingItem->IsTransient(),
                                    charData->GetActor()->GetAngle() + PI /* + PI/4 * (mesg.toSlot ? -1 : 1) */
                                    );

                // Record
                psSellEvent evt(
                        charData->GetCharacterID(),
                        0,
                        holdingItem->GetName(),
                        holdingItem->GetStackCount(),
                        (int)holdingItem->GetCurrentStats()->GetQuality(),
                        0 // 0 = Dropped, i.e "gave" it away
                        );
                evt.FireEvent();

                csString buf;
                buf.Format("%s, %s, %s, \"%s\", %d, %d", fromClient->GetName(), "World", "Drop", holdingItem->GetName(), 0, 0);
                psserver->GetLogCSV()->Write(CSV_EXCHANGES, buf);

                // Update all client views
                psserver->GetCharManager()->UpdateItemViews(clientNum);

                holdingItem = NULL;  // Not deleted because item now exists in world on its own.
                break;
            }

            case CONTAINER_INVENTORY_BULK:
            {                
                if ( charData->Inventory().PutInBulk(holdingItem, mesg.toSlot) ||
                     charData->Inventory().PutInBulk(holdingItem, ANY_BULK_SLOT) )
                {
                    // PutInBulk consumes the psItem* or merges it, so we just clear it here
                    holdingItem = NULL;
                }
                else if (mesg.fromContainer == CONTAINER_EXCHANGE_OFFERING)
                {
                    Exchange* exchange = psserver->exchangemanager->GetExchange( fromClient->ExchangeID() );
                    if ( !exchange )
                    {
                        Error1( "Client tried to drop to non existing exchange" );
                        return;
                    }   
                    else
                    {
                        if (exchange->AddItem( fromClient, holdingItem, mesg.toSlot))
                        {
                            holdingItem = NULL;
                        }
                        else
                        {
                            Error1("Client tried to replace an item in the exchange window, but failed!");
                            return;
                        }
                    }
                }
                else 
                {
                    psserver->SendSystemInfo(clientNum, "You cannot place this object in your inventory!" );
                    psserver->SendSystemError(clientNum, charData->Inventory().lastError.GetData() ); 

                    if (mesg.fromContainer == CONTAINER_INVENTORY_BULK)
                    {
                        mesg.toContainer = CONTAINER_WORLD; // Can't put it back; drop it
                        holdingItem->SetOwningCharacter(NULL, true);  // Now clear ownership in world.
                    }
                    else
                        mesg.toContainer = mesg.fromContainer; // Put it back

                    PlaceHoldingItem(mesg);
                }                    
                break;
            }

            
            case CONTAINER_INVENTORY_EQUIPMENT:
            {            
                // Stick item into equipment.
                PlaceItemInEquipment(mesg);
                break;
            }

            // We assume that the incomming ID is for a generic container and try to
            // put an item in that.
            default:
            {
                // Prevents money from being placed in an external container. 
                if ( mesg.fromContainer == CONTAINER_INVENTORY_MONEY || 
                     mesg.fromContainer == CONTAINER_OFFERING_MONEY )
                {
                    break;
                }                     
                
                psItem* toContainer = FindContainer( fromClient, mesg.toContainer, mesg.toParentContainer );
    
                if ( toContainer )
                {
                    // Check range ignoring Y co-ordinate
                    if ( toContainer->GetGemObject() &&
                         fromClient->GetActor()->RangeTo(toContainer->GetGemObject(), true) > RANGE_TO_USE )
                    {
                        psserver->SendSystemError(clientNum,"You are not in range to use %s.",toContainer->GetName());            
                        break;
                    }
                        
                    // Update the container and resend contents to player.
                    PS_CONTAINER_ERROR result = toContainer->PutItemsInContainer( mesg.toSlot, holdingItem, charData );
                    if ( result == PS_CONT_OK )
                    {
                        //psserver->GetCharManager()->SendItemContents( fromClient, toContainer );

                        if (!toContainer->GetGemObject())
                        {
                            // Containers in character's inventory need different code to handle them
                            psserver->GetCharManager()->ViewItem( fromClient, -1, mesg.toContainer, 0);
                        }
                        else
                        {
                            // Detect if this is an actionlocation container entity id's will not match
                            if ( toContainer->GetGemObject()->GetEntity()->GetID() != mesg.toContainer )
                            {
                                psserver->GetCharManager()->ViewItem( fromClient, mesg.toContainer, 0 , 0 );
                            }
                            else
                            {
                                psserver->GetCharManager()->ViewItem( fromClient, toContainer->GetGemObject()->GetEntity()->GetID(), 0 , 0 );
                            }

                            // check to see if this container will start an auto transform of item(s)
                            psItem *item = toContainer->GetItemInSlot(mesg.toSlot);
                            psserver->GetWorkManager()->StartAutoWork(
                                    fromClient, toContainer, item, item->GetStackCount());
                        }

                        holdingItem = NULL;
                    }
                    else
                    {
                        SendContainerError( result, fromClient );                        
                    }
                }
                
                break;
            }
        }    
    }            
}

void SlotManager::PlaceItemInExchange( psSlotMovementMsg & mesg)
{
    if ( mesg.fromContainer != CONTAINER_INVENTORY_MONEY && 
        mesg.fromContainer != CONTAINER_OFFERING_MONEY )
    {                     
        Exchange* exchange = psserver->exchangemanager->GetExchange( fromClient->ExchangeID() );
        if ( !exchange )
        {
            Error1( "Client tried to drop to non existing exchange" );
            return;
        }   
        else
        {
            if (exchange->AddItem( fromClient, holdingItem, mesg.toSlot))
            {
                holdingItem = NULL;
            }
            else
            {
                //The item should be back in his container
                if (mesg.fromContainer == CONTAINER_EXCHANGE_OFFERING)
                {
                    if (exchange->AddItem(fromClient, holdingItem, mesg.fromSlot))
                        holdingItem = NULL;
                }
                //The item was taken from the inventory slots (small or big)
                else if (mesg.fromContainer == CONTAINER_INVENTORY_BULK)
                {
                    if ( charData->Inventory().PutInBulk( holdingItem, mesg.fromSlot ) )
                        holdingItem = NULL;
                }
                else 
                    /*The item can be taken also from the ground or from the equipment, in this 
                    case we place it in the first free slot available in the inventory */
                {
                    if ( !charData->Inventory().PutInBulk(holdingItem) )
                    {
                        charData->DropItem(holdingItem); // Can't fit; drop it
                        holdingItem = NULL;
                    }
                }

            }
        }
    }
    else
    { /*It means we are trying to place money in the wrong place! 
      We place money then in the right slots*/
        Exchange* exchange = psserver->exchangemanager->GetExchange( fromClient->ExchangeID() );
        if ( !exchange )
        {
            Error1( "Client tried to drop to non existing exchange" );
            return;
        } 
        else
        {
            exchange->AdjustMoney(fromClient, mesg.fromSlot, mesg.stackCount);                     

            /// Delete the money item since it has been placed in the exchange area.
            CacheManager::GetSingleton().RemoveInstance(holdingItem);
        }
    }
}

void SlotManager::UnequipHolding( psSlotMovementMsg & mesg)
{
    psserver->GetCharManager()->SendOutPlaySoundMessage(fromClient->GetClientNum(), holdingItem->GetSound(), "unequip" );
    psserver->GetCharManager()->SendOutEquipmentMessages(fromClient, mesg.fromSlot, holdingItem, psEquipmentMessage::DEEQUIP);

    csString script = holdingItem->GetBaseStats()->GetProgressionEventUnEquip();
    csString equip_script = holdingItem->GetBaseStats()->GetProgressionEventEquip();
    if (script.IsEmpty() && !equip_script.IsEmpty())
    {
        Notify2(LOG_SCRIPT, "Item \"%s\" has no prg_evt_unequip script. Using \"undo_equip\"", holdingItem->GetName());
        script = "undo_equip";
    }

    gemActor *actor = charData->GetActor();
    if ( script == "undo_equip" )
    {
        csString equip = holdingItem->GetBaseStats()->GetProgressionEventEquip();
        if ( !equip.IsEmpty() )
        {
            ProgressionEvent *ev = psserver->GetProgressionManager()->FindEvent(equip);
            psserver->GetProgressionManager()->ProcessEvent(ev,  actor, NULL, true);
        }
    }
    else if (!script.IsEmpty())
    {
        psserver->GetProgressionManager()->ProcessEvent(script,  actor );
    }
}

bool SlotManager::Consume( psItem* item )
{
    if ( item )
    {
        csString script = item->GetBaseStats()->GetProgressionEventEquip();
            
        if ( !script.IsEmpty() )
        {
            gemActor *actor = charData->GetActor();
            psserver->GetProgressionManager()->ProcessEvent(script,  actor );
            
            int count = item->GetStackCount();

            // Place the rest of the stack back to where it came from.
            if ( item->GetStackCount()  > 1 )
                item->SetStackCount( count-1, false );  // don't save this because it is not in any inventory slot
            // Destroy the item object if the last of the stack was consumed
            else if ( count == 1 )
            {
                // Make sure the item is no longer in the database.
                item->Delete();
                delete item;
                return true;
            }                        
        }                            
    }

    return false;
}

void SlotManager::SendContainerError( int result, Client* fromClient )
{
    csString errorMessage = "No Error";
    
    switch ( result )
    {
        case PS_CONT_ERR_OUT_RANGE:
        {
            errorMessage = "Not a valid slot inside!";
            break;        
        }
        
        case PS_CONT_ERR_NO_ITEM:
        {
            errorMessage = "No item was given";
            break;
        }
        
        case  PS_CONT_ERR_SELF:
        {
            errorMessage = "Cannot place item inside itself!";
            break;
        }
        
        case PS_CONT_ERR_TO_BIG:
        {
            errorMessage = "That item is too big to fit inside";
            break;
        }
        
        case PS_CONT_ERR_NOT_OWNER:
        {
            errorMessage = "You don't own that!";
            break;
        }
        
        case PS_CONT_ERR_NO_STACK:
        {
            errorMessage = "Cannot stack that item here!";
            break;
        }
        
        case PS_CONT_REMAINDER:
        {
            errorMessage = "You will have items left over!";
            break;
        }       
    }   
    psserver->SendSystemError(fromClient->GetClientNum(), errorMessage );        
}
