/*
 * exchangemanager.h
 *
 * 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.
 *
 */
#ifndef __EXCHANGEMANAGER_H__
#define __EXCHANGEMANAGER_H__

#include <csutil/ref.h>
#include <csutil/refarr.h>

#include "net/messages.h"           // Exchange Messages definitions
#include "client.h"                 // Client, and ClientConnectionSet classes
#include "msgmanager.h"             // Parent class
#include "entitymanager.h"
#include "gem.h"

class psSpawnManager;
class psServer;
class Exchange;


/** ExchangingCharacter holds relevant information about character that is
  * participating in exchange of items with another character.
  */
class ExchangingCharacter
{
public:
    ExchangingCharacter( int client );

    /** Removes items from position 'itemNum'. Can return NULL if there is no item.
     * Returned item instance now belongs to caller.
     * @param Count=-1 means take everything.
     *  @param  remain [CHANGES]  The number of items left in the slot ( if any )
     */
    psItem * RemoveItem(int itemNum, int count, int &remain);

    /** Inserts item to position 'itemNum'.
     * If there is another item at this position already, it tries to stack them.
     *
     * @return true on success.
     */
    bool InsertItem(int itemNum, psItem *& item);

    /** Transfers offered items and money to character 'target'.
     *
     * Target can be 0 - in this case everything is just destroyed.
     */
    void TransferOffer(int targetClientNum);

    /** Transfers offered items and money to an NPC 'npc'.
     *
     */
    void TransferOffer(psCharacter *npc);

    /** Transfers the trias from this exchanging player to a different player.
     *  @param targetClientNum The client id of the client that should be
     *                         receiving this characters offering money.
     */
    void TransferMoney( int targetClientNum );

    /** Transfers the trias from this exchanging player to another character.
     */
    void TransferMoney( psCharacter *target );

    /** Get a count of money this character is offering in the exchange.
      * @param moneyType is one of the 4 coin types.
      */
    int GetMoneyCount( int moneyType );

    /** RMS: Get a total count of money this character is offering in the exchange.
     */
    int GetTotalMoneyCount();

    /** Change the amount of money this character is offering in the exchange.
      * @param coin Is one of the 4 coin types.  Defined in common/rpgrules/psmoney.h
      * @param delta The amount to change by. Can be +/-
      */
    void AdjustMoney(int coin, int delta);

    psItem **GetOffering() { return &(offering[0]); }

    /** Constructs an XML list of the items that are being offered.  The format of
      * the xml is:
      * <L MONEY=psMoney.ToString>
      *     <ITEM N=name SLT_POS=slotposition C=count WT=weight IMG=icon image>
      *      ...
      * </L>
      *
      * @param buff [CHANGES] Is where the resulting xml message is built.
      */
    void GetOffering(csString& buff);

    /** Constructs a simple XML list of the items that are being offered.  The format of
      * the xml is when exact:
      * <L MONEY="psMoney.ToString"><ITEM N=name C=count />...</L>
      * else:
      * <L MONEY="0,0,0,psMoney.GetTotal"><ITEM N=name C=count />...</L>
      *
      * @param buff [CHANGES] Is where the resulting xml message is built.
      * @param exact 
      */
    void GetSimpleOffering(csString& buff, bool exact = true);

    /** Update the money box for money that will be received by this character.
    */
    void UpdateReceivingMoney( psMoney& money);

    /** Update the money box for money that is being offered by this character.
    */
    void UpdateOfferingMoney();

    psMoney offeringMoney;

    /** Gets the total size of all the items that are offered. */
    float GetSumSize();
    
    /** Gets the total weight of all the items that are offered. */
    float GetSumWeight();
    
protected:

    /**
     * Stuff that was removed from character inventory and offered to the other character
     */
    psItem *offering[EXCHANGE_SLOT_COUNT];

    int client;
};

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

/** A basic exchange.  This shares the qualities of any exchange and stores the
 *   players involved as well as functionality to start/stop the exchange.
 */
class Exchange : public iDeleteObjectCallback
{
public:
    Exchange( Client* starter, csRef<ExchangeManager> manager);
    virtual ~Exchange();

    /** One of the clients has ended this exchange. This will close the exchange
      * on both the clients without transfer of items.
      * @param client The client that has ended the exchange.
      */
    virtual void HandleEnd(Client * client) = 0;

    /** One of the client has accepted the exchange. If both clients have accepted
      * the exchange will end and the items will be exchanged.
      * @param client The client that has accepted the exchange.
      * @return true If the exchange has ended.
      */
    virtual bool HandleAccept(Client * client) = 0;
    virtual bool Involves(psCharacter * character) = 0;

    /** Add a new item to the exchange. Will add to the offering slot of the person that
      * offered it an the receiving slot of the other person. Sends out the relevant
      * network messages.
      *
      * @param fromClient The client that this item came from.
      * @param item The item that is being offered.
      * @param toSlot Where in the exchange the item is placed.
      * @param transaction The transaction item is currently in.
      */
    virtual bool AddItem( Client* fromClient, psItem *& item, int toSlot);

    /* Removes an item from the exchange.  Sends out the network messages to update
     * the clients' views.
     *
     *  @param fromClient The client that has removed an item.
     *  @param slot The slot that the items were removed from.
     *  @param stackCount The amount to remove from that slot.
     *  @param remain [CHANGES] The number of items left in the slot.
     *
     *  @param A new psItem for the items removed.
     */
    virtual psItem* RemoveItem( Client* fromClient, int slot, int stackCount, int &remain );

    virtual int GetMoneyCount( Client* client, int moneyType );
    virtual bool AdjustMoney( Client* client, int moneyType, int newMoney );
    int GetID() const { return id; }
    bool CheckRange(int clientNum, gemObject * ourActor, gemObject * otherActor);

    Client* GetStarterClient() { return starterClient; }

    psItem* GetStarterOffer(int slot);
    virtual psItem* GetTargetOffer(int slot) { return NULL; }

protected:
    /// Adds an item to the offering panel on a client.
    virtual void UpdateOffering( Client* client, psItem* item, int toSlot );

    /// Adds an item to the  receiving panel on a client.
    virtual void UpdateReceiving( Client* client, psItem* item, int toSlot );

    /// Removes an item from the offering panel on a client.
    virtual void RemoveOffering( Client* client, int toSlot, int remain );

    /// Removes an item from the receiving panel on a client.
    virtual void RemoveReceiving( Client* client,int toSlot, int remain);


    void SendEnd(int clientNum);
    static int next_id;

    /** unique exchange ID */
    int id;

    /** Information about the player that initiated the exchange */
    ExchangingCharacter playerChar;
    Client* starterClient;
    int player;

    bool exchangeEnded;    //exchange ended and should be deleted
    bool exchangeSuccess; //exchange was successful and items should not be returned to owners

    ExchangeManager* exchangeMgr;
};

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

class PlayerToPlayerExchange : public Exchange
{
public:
    PlayerToPlayerExchange(Client* client, Client* target, csRef<ExchangeManager> manager);
    virtual ~PlayerToPlayerExchange();

    bool CheckExchange(int clientNum, bool checkRange=false);
    bool HandleAccept(Client * client);
    void HandleEnd(Client * client);
    bool ExchangedItems(ExchangingCharacter givingChar, csString& text);    //added: RMS
    virtual bool SendExchange(int clientNum);
    void SendExchangeToBoth();
    virtual bool Involves(psCharacter * character);
    int GetMoneyCount( Client *client, int moneyType );
    bool AdjustMoney( Client* client, int moneyType, int newMoney );

    /** Add an offering item from a client.
      *
      * @param fromClient The client that is offering the item.
      * @param item The item that is being offered.
      * @param toSlot The slot that the item should go in.
      *
      * @return True if the item was added to the exchange correctly.
      */
    virtual bool AddItem( Client* fromClient, psItem *& item, int toSlot );

    virtual psItem* RemoveItem( Client* fromClient, int slot, int stackCount, int& remain );

    virtual void DeleteObjectCallback(iDeleteNotificationObject * object);

    virtual psItem* GetTargetOffer(int slot);

private:

    /// Sends a message to both clients that an exchange has started.
    void SendRequestToBoth();

    PlayerToPlayerExchange();

    /** Information about the player that has been asked to exchange by another player*/
    Client* targetClient;
    ExchangingCharacter targetChar;
    int target;

    bool playerAccepted, targetAccepted;
};

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

class PlayerToNPCExchange : public Exchange
{
public:
    PlayerToNPCExchange(Client* starter, gemObject* target, csRef<ExchangeManager> manager);
    virtual ~PlayerToNPCExchange();
    gemObject * GetTargetGEM();
    bool CheckExchange(int clientNum, bool checkRange=false);
    virtual void HandleEnd(Client * client);
    virtual bool HandleAccept(Client * client);
    virtual bool Involves(psCharacter * character);
    virtual void DeleteObjectCallback(iDeleteNotificationObject * object);

protected:
    /**
     * A check to se if a exchange trigger trigg any response from the npc
     *
     * @param client The client
     * @param dlg    The dialog of the selected NPC
     * @param trigger A XML string of the items exchanged.
     * @return Return true if a trigger was run with sucess.
     */
    bool CheckXMLResponse(Client * client, psNPCDialog *dlg, csString trigger);

    gemObject* target;
};

/* Maintains a list of all the exchanges that are ongoing.
 */
class ExchangeManager : public MessageManager
{
public:

    ExchangeManager(ClientConnectionSet *pCCS,
                    EntityManager *celserv);
    virtual ~ExchangeManager();

    void StartExchange( Client* client, bool withPlayer );

    virtual void HandleMessage(MsgEntry *pMsg,Client *client);

    /** Utility function to handle exchange objects */
    Exchange * GetExchange(int id);

    /** Adds given exchange to the exchange manager's exchanges list */
    void AddExchange( Exchange* exchange ) { exchanges.Push(exchange); }

    /** Removes given exchange from the exchange manger's exchanges list */
    void DeleteExchange(Exchange * exchange);

protected:

    EntityManager* entitymanager;
    ClientConnectionSet* clients;

    psDatabase *database;

    csPDelArray<Exchange> exchanges;
};

#endif
