/*
 * exchangemanager.cpp
 *
 * Copyright (C) 2001 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.
 *
 */

#include <psconfig.h>

#include "bulkobjects/pscharacterloader.h"
#include "bulkobjects/dictionary.h"
#include "bulkobjects/psitem.h"
#include "bulkobjects/psglyph.h"
#include "bulkobjects/psnpcdialog.h"
#include "util/log.h"
#include "util/psconst.h"
#include "util/psstring.h"
#include "util/eventmanager.h"
#include "globals.h"
#include "psserver.h"
#include "psserverchar.h"
#include "clients.h"
#include "gem.h"
#include "chatmanager.h"
#include "cachemanager.h"
#include "playergroup.h"

#include "exchangemanager.h"
#include "invitemanager.h"

/** Question client. Handles YesNo response of client for trade request. */
class PendingTradeInvite : public PendingInvite
{
public:
    PendingTradeInvite( Client *inviter, Client *invitee, 
        const char *question ) : PendingInvite( inviter, invitee, true, 
        question, "Accept", "Reject", 
        "You have offered %s to trade with you.", 
        "%s has offered you for a trade.", 
        "%s has accepted your trade offer.",
        "You have accepted the trade offer.",
        "%s has rejected your trade offer.",
        "You have rejected %s's trade offer.",
        psQuestionMessage::generalConfirm )
    {
        
    }

    void HandleAnswer(const csString & answer)
    {
        Client * invitedClient = psserver->GetConnections()->Find(clientnum);
        if ( !invitedClient )
            return;
       
        PendingInvite::HandleAnswer(answer);
                
        Client * inviterClient = psserver->GetConnections()->Find(inviterClientNum);
        if ( !inviterClient )
            return;
        
        psCharacter* invitedCharData = invitedClient->GetCharacterData();
        psCharacter* inviterCharData = inviterClient->GetCharacterData();
        const char* invitedCharFirstName = invitedCharData->GetCharName();
        //const char* inviterFirstName = inviterCharData->GetCharName();

        if ( answer == "no" )
        {
            // Inform proposer that the trade has been rejected
            csString message;
            message.Format( "%s has rejected your trade offer.", invitedCharFirstName );
            psserver->SendSystemError( inviterClient->GetClientNum(),
                message.GetData() );
                        
            return;
        }

        Exchange* exchange = new PlayerToPlayerExchange( inviterClient, invitedClient,
                                                         psserver->GetExchangeManager() );

        // Check range
        if ( !exchange->CheckRange(inviterClient->GetClientNum(), 
            inviterClient->GetActor(), invitedClient->GetActor()) )
        {
            psserver->SendSystemError(inviterClient->GetClientNum(), 
                "%s is too far away to trade.", invitedCharFirstName );
            delete exchange;
            return;
        }

        // Overwrite client trading state
        bool oldTradingStopped = inviterCharData->SetTradingStopped(false);
        if ( !inviterCharData->ReadyToExchange() )
        {
            psserver->SendSystemInfo( inviterClient->GetClientNum(), 
                "You are not ready to trade." );
            inviterCharData->SetTradingStopped( oldTradingStopped );
            delete exchange;
            return;
        }

        inviterCharData->SetTradingStopped( oldTradingStopped );

        if ( !invitedCharData->ReadyToExchange() )
        {
            psserver->SendSystemInfo( inviterClient->GetClientNum(), 
                "Target is not ready to trade." );
            delete exchange;
            return;
        }

        inviterClient->ExchangeID( exchange->GetID() );
        invitedClient->ExchangeID( exchange->GetID() );

        psserver->GetExchangeManager()->AddExchange( exchange );
    }
};


/** Sends text message describing change of offers to user 'clientNum'.
  * otherName - name of the other character (the person that the user exchanges with)
  * object - item that was moved (e.g. "2x sword")
  * actionOfClient - did the user that we send the message to do the operation ? (or the other character?)
  * movedToOffer - was the object moved to offered stuff ? (or removed from offered stuff and returned to inventory?)
  */
/*************
void SendTextDescription(int clientNum, const csString & otherName, const csString & object, bool actionOfClient, bool movedToOffer)
{
    psString text;

    if (actionOfClient)
    {
        if (movedToOffer)
            text.AppendFmt("You have just offered %s to %s.", object.GetData(), otherName.GetData());
        else
            text.AppendFmt("You have just removed %s from offer.", object.GetData());
    }
    else
    {
        if (movedToOffer)
            text.AppendFmt("%s has just offered %s to you.", otherName.GetData(), object.GetData());
        else
            text.AppendFmt("%s has just removed %s from offer.", otherName.GetData(), object.GetData());
    }
    psserver->SendSystemInfo(clientNum, text);
}

csString MakeMoneyObject(int coin, int count)
{
    csString object;

    object = count;
    object += "x ";

    switch (coin)
    {
        case MONEY_TRIAS: object += "tria"; break;
        case MONEY_OCTAS: object += "octa"; break;
        case MONEY_HEXAS: object += "hexa"; break;
        case MONEY_CIRCLES: object += "circle"; break;
    }
    return object;
}

void SendTextDescriptionOfMoney(int clientNum, const csString & otherName, psMoney oldMoney, psMoney newMoney, bool actionOfClient)
{
    csString object;

    if (oldMoney.GetTrias() != newMoney.GetTrias())
        object = MakeMoneyObject(MONEY_TRIAS, abs(oldMoney.GetTrias() - newMoney.GetTrias()));
    else if (oldMoney.GetOctas() != newMoney.GetOctas())
        object = MakeMoneyObject(MONEY_OCTAS, abs(oldMoney.GetOctas() - newMoney.GetOctas()));
    else if (oldMoney.GetHexas() != newMoney.GetHexas())
        object = MakeMoneyObject(MONEY_HEXAS, abs(oldMoney.GetHexas() - newMoney.GetHexas()));
    else if (oldMoney.GetCircles() != newMoney.GetCircles())
        object = MakeMoneyObject(MONEY_CIRCLES, abs(oldMoney.GetCircles() - newMoney.GetCircles()));

    SendTextDescription(clientNum, otherName, object, actionOfClient, newMoney > oldMoney);
}
***************/




bool Exchange::CheckRange(int clientNum, gemObject * ourActor, gemObject * otherActor)
{
    if (ourActor->RangeTo(otherActor) > RANGE_TO_SELECT)
    {
        psserver->SendSystemInfo(clientNum, "You are not in range to trade with %s.", otherActor->GetName());
        return false;
    }
    return true;
}

psCharacter *GetClientCharacter(int clientNum)
{
    Client * client = psserver->GetConnections()->Find(clientNum);
    if (client == NULL)
        return NULL;

    return client->GetCharacterData();
}

/********
const char * GetClientName(int clientNum)
{
    Client * client = psserver->GetConnections()->Find(clientNum);
    if (client == NULL)
        return NULL;

    return client->GetName();
}
********/

/***********************************************************************************
*
*                              class ExchangingCharacter
*
***********************************************************************************/


ExchangingCharacter::ExchangingCharacter(int clientNum)
{
    int i;
    client = clientNum;
    for (i = 0; i < EXCHANGE_SLOT_COUNT; i++)
        offering[i] = NULL;
}



psItem * ExchangingCharacter::RemoveItem(int itemNum, int count, int& remain)
{
    if (itemNum<0  ||  itemNum>=EXCHANGE_SLOT_COUNT)
       return NULL;

    psItem* item = offering[itemNum];
    if (item == NULL)
        return NULL;

    if (count == -1)
        count = item->GetStackCount();

     if (item->GetStackCount() <= count)
    {
        offering[itemNum] = NULL;
        remain = 0;
        return item;
    }
    else
    {
        psItem* split = item->SplitStack(count, false);
        if (split == NULL)
            return NULL;

        remain = item->GetStackCount();
        return split;
    }
}


bool ExchangingCharacter::InsertItem(int itemNum, psItem *& item)
{
    if (itemNum<0  ||  itemNum>=EXCHANGE_SLOT_COUNT)
        return false;

    psItem* itemInSlot = offering[itemNum];
    if (itemInSlot == NULL)
        offering[itemNum] = item;
    else
    {
        if (itemInSlot->CombineStack(item, false) != NULL)
            return false;

        item = itemInSlot;  // Point to new location
    }
    return true;
}



void ExchangingCharacter::TransferMoney( int targetClientNum )
{
    psCharacter* target,*cclient;

    if ( targetClientNum == 0 )
        return;

    target =  GetClientCharacter(targetClientNum);
    cclient = GetClientCharacter(client);

    if (target == NULL)
        return;

    TransferMoney(target);
}

void ExchangingCharacter::TransferMoney( psCharacter *target )
{
    psMoney money;
    money.Adjust( MONEY_TRIAS, offeringMoney.Get(MONEY_TRIAS) );
    money.Adjust( MONEY_HEXAS, offeringMoney.Get(MONEY_HEXAS) );
    money.Adjust( MONEY_OCTAS, offeringMoney.Get(MONEY_OCTAS) );
    money.Adjust( MONEY_CIRCLES, offeringMoney.Get(MONEY_CIRCLES) );

    target->AdjustMoney( money );
    offeringMoney.Set(0, 0, 0, 0);
}

void ExchangingCharacter::TransferOffer(int targetClientNum)
{
    psCharacter * me = GetClientCharacter(client);
    psCharacter * target;
    psItem * item;

    if (targetClientNum != 0)
    {
        target = GetClientCharacter(targetClientNum);
        if (target == NULL) return;
    }
    else
        target = NULL;

    for (int i=0; i < EXCHANGE_SLOT_COUNT; i++)
    {
        item = offering[i];
        if (item != NULL)
        {
            if (target != NULL)
            {
                if ( target->MoveToInventory(item) )
                {
                    // If this is a purified glyph, we need to reset it for the new character
                    if ( item && item->GetPurifyStatus() && target != me )
                        dynamic_cast<psGlyph*>(item)->UnPurify();
                }
                else
                {
                    // Drop item on ground!!
                    target->DropItem( item );

                    csString buf;
                    buf.Format("%s, %s, %s, \"%s\", %u, %d",
                        me->GetCharFullName(),
                        target->GetCharFullName(),
                        "P2P - Item drop",
                        item->GetName(),
                        (unsigned int)item->GetStackCount(),
                        0
                        );

                    psserver->GetLogCSV()->Write(CSV_EXCHANGES, buf);
                }
            }
            else
                CacheManager::GetSingleton().RemoveInstance(item);
        }
        offering[i] = NULL;
    }

    // Money is handled in TransferMoney
}

void ExchangingCharacter::TransferOffer(psCharacter *npc)
{
    for (int i=0; i < EXCHANGE_SLOT_COUNT; i++)
    {
        psItem *item = offering[i];
        if (item != NULL)
        {

#ifdef GIVE_GOES_TO_NPC_INVENTORY

            if ( !npc->Inventory().PutInBulk(item) )
            {
                // Drop item on ground!!
                npc->DropItem(item);
            }
#else
            item->Destroy();
#endif
        }
        offering[i] = NULL;
    }
}

int ExchangingCharacter::GetMoneyCount( int moneyType )
{
    return offeringMoney.Get(moneyType);
}

int ExchangingCharacter::GetTotalMoneyCount()    //added RMS
{
    return offeringMoney.GetTotal();
}

void ExchangingCharacter::AdjustMoney(int type, int delta)
{
    offeringMoney.Adjust( type, delta );
}

void ExchangingCharacter::UpdateReceivingMoney( psMoney& money )
{
    psExchangeMoneyMsg message( client, CONTAINER_RECEIVING_MONEY,
                             money.Get(MONEY_TRIAS),
                             money.Get(MONEY_HEXAS),
                             money.Get(MONEY_CIRCLES),
                             money.Get(MONEY_OCTAS) );
    message.SendMessage();
}

void ExchangingCharacter::UpdateOfferingMoney()
{
    psExchangeMoneyMsg message( client, CONTAINER_OFFERING_MONEY,
                             offeringMoney.Get(MONEY_TRIAS),
                             offeringMoney.Get(MONEY_HEXAS),
                             offeringMoney.Get(MONEY_CIRCLES),
                             offeringMoney.Get(MONEY_OCTAS) );
    message.SendMessage();
}

void ExchangingCharacter::GetOffering(csString& buff)
{
    csString moneystr = offeringMoney.ToString();
    buff.Format("<L MONEY=\"%s\">", moneystr.GetData() );
    unsigned int z;

    for ( z = 0; z < EXCHANGE_SLOT_COUNT; z++ )
    {
        psItem *item = offering[z];
        if (item)
        {
            csString itemStr;
            itemStr.Format(
                        "<ITEM N=\"%s\" "
                        "SLT_POS=\"%d\" "
                        "C=\"%d\" "
                        "WT=\"%.2f\" "
                        "IMG=\"%s\"/>",
                        item->GetName(),
                        z,
                        item->GetStackCount(),
                        item->GetSumWeight(),
                        item->GetImageName());

                buff.Append(itemStr);
        }
    }

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

void ExchangingCharacter::GetSimpleOffering(csString& buff, bool exact)
{
    csString moneystr = offeringMoney.ToString();
    if (exact)
    {
        buff.Format("<L MONEY=\"%s\">", moneystr.GetData() );
    }
    else
    {
        buff.Format("<L MONEY=\"0,0,0,%d\">", offeringMoney.GetTotal());        
    }
    
    
    unsigned int z;

    for ( z = 0; z < EXCHANGE_SLOT_COUNT; z++ )
    {
        psItem *item = offering[z];
        if (item)
        {
            csString itemStr;
            itemStr.Format("<ITEM N=\"%s\" "
                           "C=\"%d\" />",
                           item->GetName(),
                           item->GetStackCount());

            buff.Append(itemStr);
        }
    }

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

float ExchangingCharacter::GetSumSize()
{
    float size = 0.0;
    
    psItem *item = NULL;
    
    for (int i=0; i < EXCHANGE_SLOT_COUNT; i++)
    {
        item = offering[i];
        if (item != NULL)    
        {
            size += item->GetSumSize();        
        }
    }        
    return size;                        
}


float ExchangingCharacter::GetSumWeight()
{
    float weight = 0.0;    
    psItem *item = NULL;
    
    for (int i=0; i < EXCHANGE_SLOT_COUNT; i++)
    {
        item = offering[i];
        if (item != NULL)    
        {
            weight += item->GetSumWeight();        
        }
    }        
    return weight;                        
}


/***********************************************************************************
*
*                              class Exchange
*
***********************************************************************************/

/**
 * Common base for different kinds of exchanges
 */

Exchange::Exchange(Client* starter, csRef<ExchangeManager> manager)
        : playerChar( starter->GetClientNum() )
{
    id = next_id++;
    exchangeEnded = false;
    exchangeSuccess = false;
    exchangeMgr = manager;
    starterClient = starter;
    this->player = starter->GetClientNum();

    starter->GetActor()->RegisterCallback( this );
}

Exchange::~Exchange()
{
    if ( !exchangeSuccess )
    {
        playerChar.TransferOffer(player);
        playerChar.TransferMoney(player);
        psserver->GetCharManager()->SendInventory(player);
    }

    SendEnd( player );

    starterClient->ExchangeID(0);
}

bool Exchange::AddItem(Client* client, psItem *& item, int toSlot)
{
    if ( client == starterClient )
    {
        if (playerChar.InsertItem( toSlot, item))
        {
            UpdateOffering( starterClient, item, toSlot );
            return true;
        }
        return false;
    }

    return false;
}

int Exchange::GetMoneyCount( Client* client, int moneyType )
{
    if ( client == starterClient )
        return playerChar.GetMoneyCount(moneyType);
    return 0;
}

bool Exchange::AdjustMoney( Client* client, int moneyType, int newMoney )
{
    if ( client == starterClient )
    {
        playerChar.AdjustMoney( moneyType, newMoney);
        playerChar.UpdateOfferingMoney();
        return true;
    }
    return false;
}

psItem* Exchange::RemoveItem(Client* client, int fromSlot, int stackCount, int &remain )
{
    if ( client == starterClient )
    {
        psItem* item = playerChar.RemoveItem(fromSlot, stackCount, remain);


        RemoveOffering( starterClient, fromSlot, remain );
        return item;
    }
    return NULL;
}


void Exchange::SendEnd(int clientNum)
{
    psExchangeEndMsg msg(clientNum);
    psserver->GetEventManager()->SendMessage(msg.msg);
}

void Exchange::RemoveOffering( Client* client, int slot, int remain )
{
    psExchangeRemoveItemMsg msg( client->GetClientNum(), CONTAINER_EXCHANGE_OFFERING, slot, remain );
    psserver->GetEventManager()->SendMessage(msg.msg);
}

void Exchange::RemoveReceiving( Client* client, int slot, int remain )
{
    psExchangeRemoveItemMsg msg( client->GetClientNum(), CONTAINER_EXCHANGE_RECEIVING, slot, remain );
    psserver->GetEventManager()->SendMessage(msg.msg);
}


void Exchange::UpdateOffering( Client* client, psItem* item, int toSlot )
{
    csString name( item->GetName() );
    csString icon( item->GetImageName() );
   
    psExchangeAddItemMsg msg(    client->GetClientNum(),
                                 name,
                                 CONTAINER_EXCHANGE_OFFERING,
                                 toSlot,
                                 item->GetStackCount(),
                                 icon );

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


void Exchange::UpdateReceiving( Client* client, psItem* item, int toSlot )
{
    csString name( item->GetName() );
    csString icon( item->GetImageName() );

    psExchangeAddItemMsg msg( client->GetClientNum(),
                              name,
                              CONTAINER_EXCHANGE_RECEIVING,
                              toSlot,
                              item->GetStackCount(),
                              icon );

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

psItem* Exchange::GetStarterOffer(int slot)
{
    if (slot < 0 || slot >= EXCHANGE_SLOT_COUNT)
        return NULL;

    psItem **offering = playerChar.GetOffering();
    return offering[slot];
}


int Exchange::next_id = 1;

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

PlayerToPlayerExchange::PlayerToPlayerExchange(Client* player, Client* target, csRef<ExchangeManager> manager)
                       :Exchange(player, manager), targetChar(target->GetClientNum())
{
    targetClient = target;
    this->target = target->GetClientNum();

    playerAccepted = targetAccepted = false;

    SendRequestToBoth();

    targetClient->GetActor()->RegisterCallback( this );
}

void PlayerToPlayerExchange::SendRequestToBoth()
{
    csString targetName( targetClient->GetName() );
    psExchangeRequestMsg one( starterClient->GetClientNum(), targetName, true );
    psserver->GetEventManager()->SendMessage( one.msg );

    csString clientName( starterClient->GetName() );
    psExchangeRequestMsg two( targetClient->GetClientNum(), clientName, true );
    psserver->GetEventManager()->SendMessage( two.msg );
}

void PlayerToPlayerExchange::DeleteObjectCallback(iDeleteNotificationObject * object)
{
    // Make sure both actors are disconnected.
    starterClient->GetActor()->UnregisterCallback( this );
    targetClient->GetActor()->UnregisterCallback( this );

    HandleEnd( starterClient );
    exchangeMgr->DeleteExchange( this );
}


PlayerToPlayerExchange::~PlayerToPlayerExchange()
{
    if  ( !exchangeSuccess )
    {
        targetChar.TransferOffer(target);
        targetChar.TransferMoney(target);
        psserver->GetCharManager()->SendInventory(target);
    }

    SendEnd( target );

    targetClient->ExchangeID(0);

    starterClient->GetActor()->UnregisterCallback( this );
    targetClient->GetActor()->UnregisterCallback( this );
}


bool PlayerToPlayerExchange::AddItem(Client* client, psItem *& item, int toSlot )
{
    // If the client is the starter then update the targets receive list
    if ( Exchange::AddItem( client, item, toSlot ) )
    {
        UpdateReceiving( targetClient, item, toSlot );
    }
    else if ( client == targetClient )
    {
        targetChar.InsertItem( toSlot, item);
        UpdateOffering( targetClient, item, toSlot );
        UpdateReceiving( starterClient, item, toSlot );
    }
    else
        return false;

    //Remove accepted state
    playerAccepted = false;
    targetAccepted = false;
    SendExchangeToBoth();

    return true;
}

int PlayerToPlayerExchange::GetMoneyCount( Client *client, int moneyType)
{
    if ( client == starterClient )
        return playerChar.GetMoneyCount(moneyType);
    if ( client == targetClient )
        return targetChar.GetMoneyCount(moneyType);
    return 0;
}

bool PlayerToPlayerExchange::AdjustMoney( Client* client, int moneyType, int newMoney )
{
    if ( Exchange::AdjustMoney( client, moneyType, newMoney ) )
    {
        targetChar.UpdateReceivingMoney( playerChar.offeringMoney );
    }
    else if (client == targetClient )
    {
        targetChar.AdjustMoney( moneyType, newMoney);
        targetChar.UpdateOfferingMoney();
        playerChar.UpdateReceivingMoney( targetChar.offeringMoney );
    }

    //Remove accepted state
    playerAccepted = false;
    targetAccepted = false;
    SendExchangeToBoth();

    return true;
}



psItem* PlayerToPlayerExchange::RemoveItem( Client* client, int fromSlot, int stackCount, int &remain )
{
    psItem* item = 0;
    int left;
    item = Exchange::RemoveItem( client, fromSlot, stackCount, left );
    if ( item )
    {
        RemoveReceiving( targetClient, fromSlot, left );
    }
    else if ( client == targetClient )
    {
        item = targetChar.RemoveItem( fromSlot, stackCount, left );
        RemoveOffering( targetClient, fromSlot, left );
        RemoveReceiving( starterClient, fromSlot, left );
    }

    //Remove accepted state
    playerAccepted = false;
    targetAccepted = false;
    SendExchangeToBoth();

    return item;
}


bool PlayerToPlayerExchange::CheckExchange(int clientNum, bool checkRange)
{
    ClientConnectionSet * clients = psserver->GetConnections();

    Client * playerClient = clients->Find(player);
    if (playerClient == NULL)
    {
        exchangeEnded = true;
        return false;
    }

    Client * targetClient = clients->Find(target);
    if (targetClient == NULL)
    {
        exchangeEnded = true;
        return false;
    }

    if (checkRange)
    {
        if (clientNum == player)
        {
            if ( ! CheckRange(player, playerClient->GetActor(), targetClient->GetActor()) )
            {
                psserver->GetCharManager()->SendInventory(clientNum);
                return false;
            }
        }
        else
        {
            if ( ! CheckRange(target, targetClient->GetActor(), playerClient->GetActor()) )
            {
                psserver->GetCharManager()->SendInventory(clientNum);
                return false;
            }
        }
    }

    return true;
}


bool PlayerToPlayerExchange::HandleAccept(Client * client)
{
    if ( ! CheckExchange(client->GetClientNum(), true))
        return exchangeEnded;
        
    
    if (client->GetClientNum() == player)
    {                
        float capacityBalance = targetChar.GetSumSize()- playerChar.GetSumSize();
        float weightBalance   = targetChar.GetSumWeight() - playerChar.GetSumWeight();
        if ( starterClient->GetCharacterData()->Inventory().HasSpace( capacityBalance ) && 
             starterClient->GetCharacterData()->Inventory().HasWeight( weightBalance ) )
        {             
            playerAccepted = true;
        }            
        else
        {
            psserver->SendSystemError( starterClient->GetClientNum(), "This is too much for you to carry!");                     
        }            
    }        
    else
    {
        float capacityBalance = playerChar.GetSumSize()- targetChar.GetSumSize();
        float weightBalance   = playerChar.GetSumWeight() - targetChar.GetSumWeight();
        if ( targetClient->GetCharacterData()->Inventory().HasSpace( capacityBalance ) && 
             targetClient->GetCharacterData()->Inventory().HasWeight( weightBalance ) )    
        {             
            targetAccepted = true;
        }            
        else
        {
            psserver->SendSystemError( targetClient->GetClientNum(), "This is too much for you to carry!");                     
        }            
    }        

    if (playerAccepted && targetAccepted)
    {
        Debug1(LOG_EXCHANGES,client->GetClientNum(),"Both Exchanged\n");
        Debug2(LOG_EXCHANGES,client->GetClientNum(),"Exchange %d have been accepted by both clients\n",id);

        ClientConnectionSet * clients = psserver->GetConnections();

        const char* playerName;
        const char* targetName;
        if (player !=0)
            playerName = clients->Find(player)->GetName();
        else
            playerName = "None";
        if (target !=0)
            targetName = clients->Find(target)->GetName();
        else
            targetName = "None";

        csString buf;
        csString items;

        psItem **offering = playerChar.GetOffering();
        for (int i=0; i < EXCHANGE_SLOT_COUNT; i++)
        {
            psItem *item = offering[i];
            if (item != NULL)
            {
                buf.Format("%s, ", item->GetName());
                items.Append(buf);
            }
        }
        if (!items.IsEmpty())
        {
            items.Truncate(items.Length() - 2);

            csString buf;
            buf.Format("%s, %s, %s, \"%s\", %d, %d", playerName, targetName, "P2P Exchange", items.GetData(), 0, 0);
            psserver->GetLogCSV()->Write(CSV_EXCHANGES, buf);
        }

        items.Clear();

        offering = targetChar.GetOffering();
        for (int i=0; i < EXCHANGE_SLOT_COUNT; i++)
        {
            psItem *item = offering[i];
            if (item != NULL)
            {
                buf.Format("%s, ", item->GetName());
                items.Append(buf);
            }
        }

        if (!items.IsEmpty())
        {
            items.Truncate(items.Length() - 2);

            csString buf;
            buf.Format("%s, %s, %s, \"%s\", %d, %d", targetName, playerName, "P2P Exchange", items.GetData(), 0, 0);
            psserver->GetLogCSV()->Write(CSV_EXCHANGES, buf);
        }

        csString itemsOffered;

        // RMS: Output items start client gave target client
        if (ExchangedItems(playerChar, itemsOffered))
        {
            psserver->SendSystemBaseInfo( targetClient->GetClientNum(), "%s gave %s %s.", starterClient->GetActor()->GetName(), targetClient->GetActor()->GetName(), itemsOffered.GetData());                             // RMS: Trade cancelled without items being offered
            psserver->SendSystemBaseInfo( starterClient->GetClientNum(), "%s gave %s %s.", starterClient->GetActor()->GetName(), targetClient->GetActor()->GetName(), itemsOffered.GetData());                            // RMS: Trade cancelled without items being offered
        }

        // RMS: Output items target client gave start client
        if (ExchangedItems(targetChar, itemsOffered))
        {
            psserver->SendSystemBaseInfo( targetClient->GetClientNum(), "%s gave %s %s.", targetClient->GetActor()->GetName(), starterClient->GetActor()->GetName(), itemsOffered.GetData());                             // RMS: Trade cancelled without items being offered
            psserver->SendSystemBaseInfo( starterClient->GetClientNum(), "%s gave %s %s.", targetClient->GetActor()->GetName(), starterClient->GetActor()->GetName(), itemsOffered.GetData());                            // RMS: Trade cancelled without items being offered
        }

        playerChar.TransferOffer(target);
        playerChar.TransferMoney(target);

        targetChar.TransferOffer(player);
        targetChar.TransferMoney(player);

        psserver->SendSystemOK(targetClient->GetClientNum(),"Trade complete");
        psserver->SendSystemOK(starterClient->GetClientNum(),"Trade complete");

        psserver->GetCharManager()->UpdateItemViews(player);
        psserver->GetCharManager()->UpdateItemViews(target);

        SendEnd(player);
        SendEnd(target);

        exchangeSuccess = true;
        return true;
    }
    else
        SendExchangeToBoth();
    return false;
}



void PlayerToPlayerExchange::HandleEnd(Client * client)
{
    csString itemsOffered;

    // RMS: Target client
    if (ExchangedItems(playerChar, itemsOffered))
        psserver->SendSystemResult( targetClient->GetClientNum(), "Trade declined" );  // RMS: Trade cancelled without items being offered
    else
        psserver->SendSystemResult( targetClient->GetClientNum(), "Trade cancelled");  // RMS: Starter client

    if (ExchangedItems(targetChar, itemsOffered))
        psserver->SendSystemResult( starterClient->GetClientNum(), "Trade declined" ); // RMS: Trade cancelled without items being offered
    else
        psserver->SendSystemResult( starterClient->GetClientNum(), "Trade cancelled");


    if (client->GetClientNum() == player)
        SendEnd(target);
    else
        SendEnd(player);
}

bool PlayerToPlayerExchange::ExchangedItems(ExchangingCharacter givingChar, csString& text)
{
    psItem **offering = givingChar.GetOffering();

    text.Clear();

    // RMS: Loop through all items offered
    for (int i=0; i < EXCHANGE_SLOT_COUNT; i++)
    {
        psItem *item = offering[i];
        if (item != NULL)
        {
            text += item->GetQuantityName();
            text += ", ";
        }
    }
    // RMS: Add the money count
    if (givingChar.GetTotalMoneyCount() > 0)
    {
        text.AppendFmt("%i Trias, ", givingChar.GetTotalMoneyCount());
    }
    if (!text.IsEmpty())
    {
        text.Truncate(text.Length() - 2);
        return true;
    }

    return false;
}

bool PlayerToPlayerExchange::SendExchange(int clientNum)
{
    if ( starterClient->GetClientNum() == clientNum )
    {
        psExchangeStatusMsg mesg( clientNum, playerAccepted, targetAccepted );
        psserver->GetEventManager()->SendMessage( mesg.msg );
    }
    else
    {
        psExchangeStatusMsg mesg( clientNum, targetAccepted, playerAccepted );
        psserver->GetEventManager()->SendMessage( mesg.msg );
    }

    return true;
}

void PlayerToPlayerExchange::SendExchangeToBoth()
{
    SendExchange(player);
    SendExchange(target);
}

bool PlayerToPlayerExchange::Involves(psCharacter * character)
{
    return (GetClientCharacter(player) == character) || (GetClientCharacter(target) == character);
}

psItem* PlayerToPlayerExchange::GetTargetOffer(int slot)
{
    if (slot < 0 || slot >= EXCHANGE_SLOT_COUNT)
        return NULL;

    psItem **offering = targetChar.GetOffering();
    return offering[slot];
}


/***********************************************************************************
*
*                              class NPCExchange
*
***********************************************************************************/

PlayerToNPCExchange::PlayerToNPCExchange(Client* player, gemObject* target, csRef<ExchangeManager> manager)
                    : Exchange(player, manager)
{
    this->target = target;

    csString targetName( target->GetName() );
    psExchangeRequestMsg one( starterClient->GetClientNum(), targetName, false );
    psserver->GetEventManager()->SendMessage( one.msg );

    target->RegisterCallback( this );

}

PlayerToNPCExchange::~PlayerToNPCExchange()
{
    starterClient->GetActor()->UnregisterCallback( this );
    target->UnregisterCallback( this );
}

gemObject * PlayerToNPCExchange::GetTargetGEM()
{
    return target;
}

bool PlayerToNPCExchange::CheckExchange(int clientNum, bool checkRange)
{
    ClientConnectionSet * clients = psserver->GetConnections();

    Client * playerClient = clients->Find(player);
    if (playerClient == NULL)
    {
        exchangeEnded = true;
        return false;
    }

    gemObject * targetGEM = GetTargetGEM();
    if (targetGEM == NULL)
    {
        exchangeEnded = true;
        return false;
    }

    if (checkRange)
        if ( ! CheckRange(clientNum, playerClient->GetActor(), targetGEM) )
        {
            psserver->GetCharManager()->SendInventory(clientNum);
            return false;
        }

    return true;
}



void PlayerToNPCExchange::HandleEnd(Client * client)
{
    return;
}

bool PlayerToNPCExchange::CheckXMLResponse(Client * client, psNPCDialog *dlg, csString trigger)
{
    NpcResponse *resp = dlg->FindXMLResponse(client, trigger);
    if (resp && resp->type != NpcResponse::ERROR_RESPONSE)
    {
        if (resp->ExecuteScript(client, (gemNPC *)target))
        {
            // Give offered items to npc
            playerChar.TransferOffer(target->GetCharacterData());
            playerChar.TransferMoney(target->GetCharacterData());
            exchangeSuccess = true;
            client->SetLastResponse(resp->id);
            return true;
        }
    }
    return false;
}
    

bool PlayerToNPCExchange::HandleAccept(Client * client)
{
    if ( ! CheckExchange(client->GetClientNum(), true))
        return exchangeEnded;


    // Send to the npc as an event trigger
    psNPCDialog *dlg = target->GetNPCDialogPtr();
    if (dlg == NULL)
        psserver->SendSystemError(client->GetClientNum(), "%s does not respond to gifts.", target->GetName());
    else
    {
        csString trigger;

        // Check if NPC require the exact among of money
        playerChar.GetSimpleOffering(trigger,true);
        if (!CheckXMLResponse(client,dlg,trigger))
        {
            // Check if NPC is OK with all the money total in trias
            playerChar.GetSimpleOffering(trigger,false);
            if (!CheckXMLResponse(client,dlg,trigger))
            {
                psserver->SendSystemError(client->GetClientNum(), "%s does not need that.", target->GetName());
            }
        }

    }

    psserver->SendSystemOK(client->GetClientNum(),"Trade complete");

    psserver->GetCharManager()->SendInventory(player);
    SendEnd(player);
    return true;
}

bool PlayerToNPCExchange::Involves(psCharacter * character)
{
    gemObject * gem = GetTargetGEM();
    if (gem!=NULL  &&  gem->GetCharacterData()==character)
        return true;

    return (GetClientCharacter(player) == character);
}


void PlayerToNPCExchange::DeleteObjectCallback(iDeleteNotificationObject * object)
{
    starterClient->GetActor()->UnregisterCallback( this );
    target->UnregisterCallback( this );

    HandleEnd( starterClient );
    exchangeMgr->DeleteExchange( this );
}

/***********************************************************************************
*
*                              class ExchangeManager
*
***********************************************************************************/

ExchangeManager::ExchangeManager(ClientConnectionSet *pClnts,
                                 EntityManager *celserv)
{
    clients      = pClnts;
    entitymanager    = celserv;

    psserver->GetEventManager()->Subscribe(this, MSGTYPE_GUIEXCHANGE, REQUIRE_READY_CLIENT|REQUIRE_ALIVE);
    psserver->GetEventManager()->Subscribe(this, MSGTYPE_EXCHANGE_REQUEST, REQUIRE_READY_CLIENT|REQUIRE_ALIVE|REQUIRE_TARGETACTOR);
    psserver->GetEventManager()->Subscribe(this, MSGTYPE_EXCHANGE_ACCEPT, REQUIRE_READY_CLIENT|REQUIRE_ALIVE);
    psserver->GetEventManager()->Subscribe(this, MSGTYPE_EXCHANGE_END, REQUIRE_READY_CLIENT|REQUIRE_ALIVE);
}

ExchangeManager::~ExchangeManager()
{
    if (psserver->GetEventManager())
    {
        psserver->GetEventManager()->Unsubscribe(this, MSGTYPE_GUIEXCHANGE);
        psserver->GetEventManager()->Unsubscribe(this, MSGTYPE_EXCHANGE_REQUEST);
        psserver->GetEventManager()->Unsubscribe(this, MSGTYPE_EXCHANGE_ACCEPT);
        psserver->GetEventManager()->Unsubscribe(this, MSGTYPE_EXCHANGE_END);
    }
}
void ExchangeManager::StartExchange( Client* client, bool withPlayer )
{
    // Make sure we are in a peaceful mode before trading
    if ( client->GetActor()->GetMode() != PSCHARACTER_MODE_PEACE )
    {
        psserver->SendSystemInfo(client->GetClientNum(), "Can only trade when not engaged in combat");
        return;
    }

    if ( client->ExchangeID() )
    {
        psserver->SendSystemError(client->GetClientNum(), "You are already busy with another trade" );
        return;
    }

    Client * targetClient = 0;
    gemObject * target;

    target = client->GetTargetObject();

    if ( target->IsAlive() == false )
    {
        psserver->SendSystemError(client->GetClientNum(), "Cannot give items to dead things!");
        return;
    }

    // if the command was "/give":
    if (!withPlayer)
    {
        if ( !target->GetNPCPtr() )
        {
            psserver->SendSystemError(client->GetClientNum(), "You can give items to NPC only");
            return;
        }

        Exchange* exchange = new PlayerToNPCExchange(client, target, this);
        client->ExchangeID( exchange->GetID() );
        exchanges.Push (exchange);
    }

    // if the command was "/trade":
    else
    {
        if ( target->GetNPCPtr() )
        {
            psserver->SendSystemError(client->GetClientNum(), "You can trade with other players only");
            return;
        }

        targetClient = clients->FindPlayer(target->GetPlayerID());
        if (targetClient == NULL)
        {
            psserver->SendSystemError(client->GetClientNum(), "Cannot find your target!");
            return;
        }

        if ( client == targetClient )
        {
            psserver->SendSystemError(client->GetClientNum(),"You can not trade with yourself");
            return;
        }

        if ( targetClient->ExchangeID() )
        {
            psserver->SendSystemError(client->GetClientNum(), "%s is busy with another trade", targetClient->GetName() );
            return;
        }

        // Must ask the other player before starting the trade
        csString question;
        question.Format( "Do you want to trade with %s ?", client->GetCharacterData()->GetCharName() );
        PendingQuestion *invite = new PendingTradeInvite(
                    client, targetClient, question.GetData() );
        psserver->questionmanager->SendQuestion(invite);
    }
}

void ExchangeManager::HandleMessage(MsgEntry *me,Client *client)
{
    switch ( me->GetType() )
    {
        case MSGTYPE_EXCHANGE_END:
        {
            Notify2( LOG_TRADE, "Trade %d Rejected", client->ExchangeID() );

            Exchange* exchange = GetExchange( client->ExchangeID() );
            if ( exchange )
            {
                exchange->HandleEnd(client);
                DeleteExchange(exchange);
            }
            else
            {
                Warning2( LOG_TRADE, "Trade %d Not located", client->ExchangeID() );
            }

            break;
        }

        case MSGTYPE_EXCHANGE_REQUEST:
        {
            Notify2( LOG_TRADE, "Trade Requested from %u", client->GetClientNum() );
            psExchangeRequestMsg msg(me);
            StartExchange( client, msg.withPlayer );
            break;
        }

        case MSGTYPE_EXCHANGE_ACCEPT:
        {
            Notify3( LOG_TRADE, "Exchange %d Accept from %d", client->ExchangeID(), client->GetClientNum() );

            Exchange* exchange = GetExchange( client->ExchangeID() );
            if ( exchange )
            {
                if (exchange->HandleAccept(client))
                {
                    DeleteExchange(exchange);
                }
            }
            break;
        }
    }
}



Exchange * ExchangeManager::GetExchange(int id)
{
    // TODO:  Make this a hashmap
    for (size_t n = 0; n < exchanges.Length(); n++){
        if (exchanges[n]->GetID() == id) return exchanges[n];
    }
    Error2("Can't find exchange: %d !!!",id);
    return NULL;
}

void ExchangeManager::DeleteExchange(Exchange * exchange)
{
    exchanges.Delete(exchange); // deletes the exchange object
}
