// Copyright (C) 2007 Institut fuer Telematik, Universitaet Karlsruhe (TH)
//
// 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; either version 2
// of the License, or (at your option) any later version.
//
// 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.
//

/**
 * @file MyDHT.h
 * @author Gregoire Menuel, Ingmar Baumgart
 * @version 0.1.1
 * @date 07/31/2010
 */

#ifndef __MY_DHT_H_
#define __MY_DHT_H_

#include <omnetpp.h>
#include <OverlayKey.h>
#include <SHA1.h>
#include <CommonMessages_m.h>
#include "MyDHTMessage_m.h"
#include "MyDHTDataStorage.h"
#include "MyDHTLookupStorage.h"
#include "BaseApp.h"
#include <RpcMacros.h>
#include <deque>


struct CacheDataEntry
{
	OverlayKey key;
    BinaryValue value;
    uint32_t kind;
    uint32_t id;
    bool is_modifiable;
    NodeHandle sourceNode; 
};

typedef std::deque<CacheDataEntry> CacheDataMap;


/**
 * A Distributed Hash Table (DHT) for KBR MyDHTTestApp
 *
 */

class MyDHT : public BaseApp
{
public:
    MyDHT();
    virtual ~MyDHT();

private:
    enum PendingRpcsStates {
        INIT = 0,
        LOOKUP_STARTED = 1,
        GET_HASH_SENT = 2,
        GET_VALUE_SENT = 3,
        PUT_SENT = 4,
		GET_VALUE_CACHE_SENT = 5
    };

    class PendingRpcsEntry
    {
    public:
        PendingRpcsEntry()
        {
            getCallMsg = NULL;
            putCallMsg = NULL;
            state = INIT;
            hashVector = NULL;
            numSent = 0;
            numAvailableReplica = 0;
            numFailed = 0;
            numResponses = 0;
        };

        DHTgetCAPICall* getCallMsg;
        DHTputCAPICall* putCallMsg;
        PendingRpcsStates state;
        NodeVector replica;
        NodeVector* hashVector;
        std::map<BinaryValue, NodeVector> hashes;
        int numSent;
        int numAvailableReplica;
        int numFailed;
        int numResponses;
    };

    friend std::ostream& operator<<(std::ostream& Stream,
                                            const PendingRpcsEntry& entry);

    void initializeApp(int stage);
    void finishApp();
    void handleTimerEvent(cMessage* msg);

    bool handleRpcCall(BaseCallMessage* msg);
    void handleRpcResponse(BaseResponseMessage* msg, cPolymorphic *context,
                           int rpcId, simtime_t rtt);
    void handleRpcTimeout(BaseCallMessage* msg, const TransportAddress& dest,
                          cPolymorphic* context, int rpcId,
                          const OverlayKey& destKey);
    void handlePutRequest(MyDHTPutCall* dhtMsg);
    void handlePutResponse(MyDHTPutResponse* dhtMsg, int rpcId);
    void handlePutCAPIRequest(DHTputCAPICall* capiPutMsg);
    void handleGetCAPIRequest(DHTgetCAPICall* capiPutMsg);
    void handleDumpDhtRequest(DHTdumpCall* call);
    void update(const NodeHandle& node, bool joined);
	void deliver(OverlayKey& key, cMessage* msg); 
	void handleUDPMessage(cMessage* msg);
	void forward(OverlayKey* key, cPacket** msg, NodeHandle* nextHopNode);
    //void handleLookupResponse(LookupResponse* lookupMsg, int rpcId, simtime_t delay);
    void sendMaintenancePutCall(const TransportAddress& dest,
                                const OverlayKey& key,
                                const DhtDataEntry& entry);
    int resultValuesBitLength(MyDHTGetResMessage* msg);
	void reevaluateCache();
	void getCacheKey(OverlayKey key);

	//Parameters
    uint numReplica;
    int numGetRequests;
    double ratioIdentical;
    double maintenanceMessages;
    double normalMessages;
    double numBytesMaintenance;
    double numBytesNormal;
	double cacheRefreshTime;
	int numQueriesAnswered;
	int numQueriesForwarded;
    int numCacheRequests;
    int numCacheResponses;
	int cacheSize;
	int maxCacheSize;
	bool cacheOthersKeys;
	bool useMsgGroupId;
	double cachingThreshold;
	double removeLookupThreshold;
	double alpha;

	cMessage *cacheTimer;	//Time to revaluate cache entries
    typedef std::map<uint32_t, PendingRpcsEntry> PendingRpcs;
    PendingRpcs pendingRpcs; /**< a map of all pending RPC operations */

    // module references
    MyDHTDataStorage* dataStorage; /**< pointer to the dht data storage */
    MyDHTLookupStorage* lookupStorage; /**< pointer to the dht lookup storage */
	
	//CacheDataMap cacheMap;
};

#endif
