//
// Copyright (C) 2006 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 MyChordSuccessorList.cc
 * @author Markus Mauch, Ingmar Baumgart, Dilum Bandara
 * @version 0.1
 * @data 07/06/2010
 */

#include <cassert>
#include "MyChordSuccessorList.h"
#include "MyChord.h"

namespace oversim {

Define_Module(MyChordSuccessorList);

using namespace std;

// predefined node handle
const MySuccessorListEntry MyChordSuccessorList::UNSPECIFIED_NODE;


std::ostream& operator<<(std::ostream& os, const MySuccessorListEntry& e)
{
    os << e.nodeHandle << " " << e.newEntry;
    return os;
};


void MyChordSuccessorList::initialize(int stage)
{
    // because of IPAddressResolver, we need to wait until interfaces
    // are registered, address auto-assignment takes place etc.
    if (stage != MIN_STAGE_OVERLAY)
        return;

    WATCH_MAP(successorMap);
}


void MyChordSuccessorList::handleMessage(cMessage* msg)
{
    error("this module doesn't handle messages, it runs only in initialize()");
}


void MyChordSuccessorList::initializeList(uint32_t size, NodeHandle owner,
                                        MyChord *overlay, int id)
{
    successorMap.clear();
    successorListSize = size;
    thisNode = owner;
    this->overlay = overlay;
	this->groupId = id;
    addSuccessor(thisNode, true, groupId);
}


uint32_t MyChordSuccessorList::getSize()
{
    return successorMap.size();
}


bool MyChordSuccessorList::isEmpty()
{
    if (successorMap.size() == 1 && getSuccessor().nodeHandle == thisNode)
        return true;
    else
        return false;
}


//const NodeHandle& MyChordSuccessorList::getSuccessor(uint32_t pos)
const MySuccessorListEntry& MyChordSuccessorList::getSuccessor(uint32_t pos)
{
    // check boundaries, default pos is 0
    if (pos == 0 && successorMap.size() == 0)
		return MyChordSuccessorList::UNSPECIFIED_NODE;

    if (pos >= successorMap.size())
        error("Index out of bound (MyChordSuccessorList, getSuccessor())");

    std::map<OverlayKey, MySuccessorListEntry>::iterator it =
        successorMap.begin();

    for (uint32_t i= 0; i < pos; i++) 
	{
        it++;
        if (i == (pos-1))
            return it->second;
    }
    return it->second;	//When pos=0
}


void MyChordSuccessorList::updateList(MyNotifyResponse* notifyResponse)
{
    addSuccessor(notifyResponse->getSrcNode(), false, notifyResponse->getSrcGroupId()); //Add sender

    for (uint32_t k = 0; ((k < static_cast<uint32_t>(notifyResponse->getSucNum()))
                     && (k < (successorListSize - 1))); k++) 
	{
        NodeHandle successor = notifyResponse->getSucNode(k);
        // don't add nodes, if this would change our successor
        if (successor.getKey().isBetweenLR(thisNode.getKey(),
                                      notifyResponse->getSrcNode().getKey()))
            continue;
        addSuccessor(successor, false, notifyResponse->getSucGroupId(k));
    }

    removeOldSuccessors();
    assert(!isEmpty());
}


void MyChordSuccessorList::addSuccessor(NodeHandle successor, bool resize, int id)
{
    OverlayKey sum = successor.getKey() - (thisNode.getKey() + OverlayKey::ONE);

    std::map<OverlayKey, MySuccessorListEntry>::iterator it =
        successorMap.find(sum);

    // Make a CommonAPI update() upcall to inform application
    // about our new neighbor in the successor list
    if (it == successorMap.end()) 
	{
        // TODO: first add node and then call update()
        overlay->callUpdate(successor, true);	//New successor
    } 
	else
        successorMap.erase(it); //Remove existing entry

    MySuccessorListEntry entry;
    entry.nodeHandle = successor;
    entry.newEntry = true;
	entry.groupId = id;
    successorMap.insert(make_pair(sum, entry));

	//If grow than what is allowed, remove last entry
    if ((resize == true) && (successorMap.size() > (uint32_t)successorListSize)) 
	{
        it = successorMap.end();
        it--;
        overlay->callUpdate(it->second.nodeHandle, false);	//Node remove from list
        successorMap.erase(it);
    }
}


bool MyChordSuccessorList::handleFailedNode(const TransportAddress& failed)
{
    assert(failed != thisNode);
    std::map<OverlayKey, MySuccessorListEntry>::iterator iter;
    for (iter = successorMap.begin(); iter != successorMap.end(); ++iter) 
	{
        if (failed == iter->second.nodeHandle) 
		{
            successorMap.erase(iter);
            overlay->callUpdate(failed, false);
            // ensure that thisNode is always in the successor list
            if (getSize() == 0)
                addSuccessor(thisNode, true, groupId);
            return true;
        }
    }
    return false;
}


void MyChordSuccessorList::removeOldSuccessors()
{
    std::map<OverlayKey,MySuccessorListEntry>::iterator it;
    for (it = successorMap.begin(); it != successorMap.end();) 
	{
        if (it->second.newEntry == false)	//Old entry 
		{	//Remove & inform application
            overlay->callUpdate(it->second.nodeHandle, false);
            successorMap.erase(it++);
        } 
		else //New one, mark it as old
		{
            it->second.newEntry = false;
            it++;
        }
    }

    it = successorMap.end();
    it--;

    while (successorMap.size() > successorListSize)
        successorMap.erase(it--);	//Remove last one

    if (getSize() == 0)	// ensure that thisNode is always in successor list
        addSuccessor(thisNode, true, groupId);
}


void MyChordSuccessorList::updateDisplayString()
{
    // FIXME: doesn't work without tcl/tk
    //    	if (ev.isGUI()) {
    if (1) {
        char buf[80];

        if (successorMap.size() == 1) {
            sprintf(buf, "1 successor");
        } else {
            sprintf(buf, "%zi successors", successorMap.size());
        }

        getDisplayString().setTagArg("t", 0, buf);
        getDisplayString().setTagArg("t", 2, "blue");
    }

}


void MyChordSuccessorList::updateTooltip()
{
    if (ev.isGUI()) {
        std::stringstream str;
        for (uint32_t i = 0; i < successorMap.size(); i++)	{
            str << getSuccessor(i).nodeHandle;
            if ( i != successorMap.size() - 1 )
                str << endl;
        }

        char buf[1024];
        sprintf(buf, "%s", str.str().c_str());
        getDisplayString().setTagArg("tt", 0, buf);
    }
}

void MyChordSuccessorList::display()
{
    cout << "Content of MyChordSuccessorList:" << endl;
    for (std::map<OverlayKey,MySuccessorListEntry>::iterator it =
        successorMap.begin(); it != successorMap.end(); it++)
        cout << it->first << " with Node: " << it->second.nodeHandle << endl;
}

}; //namespace
