// File: gnutellaagent.h
// Original by Qi He
// Modified by Andre Dufour

/* GnutellaAgent: implement a Peer-to-Peer protocol */
/* maintains network connections to neighbours      */

#ifndef __GNUTELLAAGENT_H__
#define __GNUTELLAAGENT_H__

#include "gnut_types.h"
#include "peeragent.h"
#include "conntimer.h"
#include "basicstat.h"

class GnutDescHdr;
class GnutQueryMsg;

class GnutellaAgent: public PeerAgent 
{
public:
    // = FOUNDATION

    GnutellaAgent(GnutellaApp* aApp);
    GnutellaAgent(NodeAddr_t aAddr);
    virtual ~GnutellaAgent(void);


    // = ACTION

    void statistics();              //print out statistics

    //APIs to GnutellaApp. GnutellaApps use them to send Gnutella messages.
    virtual int Connect(NodeAddr_t peer, double timeout);
    virtual void Disconnect(NodeAddr_t peer);

    virtual void Ping(NodeAddr_t peer, int ttl);
    virtual void Pong(NodeAddr_t peer, int cnt, const NodeAddr_t* iplist, DescriptorID_t id);

    virtual void Query(NodeAddr_t peer);
    virtual void QueryHit(NodeAddr_t peer, DescriptorID_t id, const GnutQueryMsg& aQuery);

    virtual void Bootstrap(NodeAddr_t peer);
    virtual void Bootstrap_Reply(NodeAddr_t peer, int cnt, NodeAddr_t* addrs);
    virtual void UpdateBootcache();

    //upcalls from Socket, triggered by socket status changes
    virtual int upcall_recv(Socket* , PacketData* , Handler* );
    virtual void upcall_connected(Socket* );
    virtual void upcall_passconn(Socket* );
    virtual void upcall_closing(Socket* );
    virtual void upcall_send(Socket* );

    //internal functions, used to process certain messages
    //broadcast routing protocol messages
    virtual int forward(Socket* incoming, PacketData* data, const GnutDescHdr& hdr); 

    //unicast routing for QueryHit and Pong messages
    virtual int backroute(PacketData* data, const GnutDescHdr& hdr); 
    void conn_timeout(); //connection request timeout
    void gc(); //garbage collection of: descriptors, pending 
               // connection requests and Queries

    Socket* find_desc(DescriptorID_t descid);    
    int find_query(DescriptorID_t aId, int aPoll); 
    int find_ping(DescriptorID_t aId, int aPoll);

    virtual void gnutella_req(Socket* );
    virtual void gnutella_ok(Socket* );
    virtual void gnutella_reject(Socket* );
    void lime_bootstrap(Socket* );

    // TODO: AD ugly. Remove this.
    virtual void gnutella_leaf_guidance(Socket* sock);

    int cleanup(void);


    // = CONSTANTS

    static const NodeAddr_t ALL_NEIGHBOURS;

protected:
    int command(int, const char*const*);


public:
    // = DATA

    ConnTimer conn_timer_;          // outstanding conn request timer
    GnutellaApp* gapp_;             // Gnutella App associated with the Agent
    PendingConns_t conn_pending_;   // outstanding conn requests
    DescMap_t desc_cache_;          // descriptor cache
    GC* gc_;                        // Garbage Collector
    ReqList_t pending_req_;         // outstanding request list

    // flow control options
    int rate_limit_;                // rate limit per socket
    int use_prio_;                  // whether to use priority message 
                                    // queuing for sockets

    // basic statistics
    BasicStat bkblock_;
    BasicStat fwblock_;
    BasicStat rcvRate_;
    int sec_;
    int secRcv_;                    // average number of messages received 
                                    //  per second
private:
    GnutCoord coord_;               // Vivaldi coordinates

    // = FOUNDATION
    GnutellaAgent(const GnutellaAgent&);
    GnutellaAgent operator=(const GnutellaAgent&);

};


#endif /* __GNUTELLAAGENT_H__ */
