//
// 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 MyMyDHTLookupStorage.cc
 * @author Dilum Bandara
 * @version 0.1
 * @date 07/01/2010
 */

#include <omnetpp.h>
#include <hashWatch.h>
#include "MyDHTLookupStorage.h"


Define_Module(MyDHTLookupStorage);	//Register module

using namespace std;


void MyDHTLookupStorage::initialize(int stage)
{
    if (stage != MIN_STAGE_APP)
        return;

    //WATCH_MULTIMAP(lookupMap);
}


void MyDHTLookupStorage::handleMessage(cMessage* msg)
{
    error("MyDHTLookupStorage:: This module doesn't handle messages!");
}


void MyDHTLookupStorage::clear()
{
    lookupMap.clear();	//Remove entire lookup store
}


uint32_t MyDHTLookupStorage::getSize()
{
    return lookupMap.size();
}


DhtLookupEntry* MyDHTLookupStorage::getLookupEntry(const OverlayKey& key)
{
	map<OverlayKey, DhtLookupEntry>::iterator it;
	it = lookupMap.find(key);
	return &(it->second);
}


DhtLookupVector* MyDHTLookupStorage::getLookupVector()
{
    DhtLookupVector* vect = new DhtLookupVector();
    DhtLookupEntry entry;

	map<OverlayKey, DhtLookupEntry>::iterator it;

	for(it=lookupMap.begin(); it != lookupMap.end(); it++)
	{
        entry = it->second;
        vect->push_back(make_pair(it->first, entry));	//Append to vector
    }

    return vect;	//Return pointer with all entries that match given key
}


bool MyDHTLookupStorage::addLookup(const OverlayKey& key, uint32_t hops,
                                   	bool success, double alpha, 
									double removeLookupThreshold)
{
	map<OverlayKey, DhtLookupEntry>::iterator it;

	//Update demand for all entries
	for(it = lookupMap.begin(); it != lookupMap.end(); it++)
	{
		it->second.demand *= 1 - alpha;
		if(it->second.demand < removeLookupThreshold)
		{
			if(lookupMap.size() == 1)
			{
				lookupMap.erase(it); //Remove entry then decrement it to point to next entry
				break;
			}
			else
				lookupMap.erase(it--); //Remove entry then decrement it to point to next entry
		}
	}
	
	it = lookupMap.find(key);
	if (it == lookupMap.end())	//New entry
	{
    	DhtLookupEntry entry;
		entry.noLookups = 1;
		entry.totalHops = hops;
		entry.success = success;
		entry.demand = alpha;
		entry.pending = false;
    	lookupMap.insert(make_pair(key, entry));
		return false;
	}
	else			//Old entry
	{
		it->second.noLookups += 1;
		it->second.totalHops += hops;
		it->second.demand += alpha;
		it->second.success = success;
		return true;
	}
}


void MyDHTLookupStorage::removeLookup(const OverlayKey& key)
{
	lookupMap.erase(key);
}


DhtLookupVector* MyDHTLookupStorage::dumpLookup(const OverlayKey& key)
{
    DhtLookupVector* vect = new DhtLookupVector();
    DhtLookupMap::iterator iter, end;

    if (key.isUnspecified()) //If key not given dump entire table
	{
        iter = lookupMap.begin();
        end = lookupMap.end();
    } 
	else 					//Else dump only entries for given key
	{
        iter = lookupMap.lower_bound(key);
        end = lookupMap.upper_bound(key);
    }
    for (; iter != end; iter++)
        vect->push_back(make_pair(iter->first, iter->second));	//Append to vector
    
	return vect;
}

void MyDHTLookupStorage::setPending(const OverlayKey& key, bool pending)
{
	map<OverlayKey, DhtLookupEntry>::iterator it;
	it = lookupMap.find(key);
	
	if(it == lookupMap.end())	//Key not found
		return;
	else
		it->second.pending = pending;
}

