/*
 * (c) 2005 Research Group Dr. Fuhrmann, University of Karlsruhe, Germany
 * All rights reserved.
 *
 * Author:
 *         Stefan Denk <denk@ira.uka.de>
 *         Kendy Kutzner <kutzner@ira.uka.de>
 *
 * $Id: videosources.cc,v 1.4 2006/01/05 10:31:44 denk Exp $
 *
 */

// libc

#include <assert.h>

// vidtrans

#include "videosources.h"

/**
 * And kill all dependencies.
 */
cvideosources::~cvideosources(){
	//dsyslog("%s: begin", __PRETTY_FUNCTION__);
	hash_t::iterator i;
	while ((i=_sources.begin()) != _sources.end()){
		sourcelist_t &thelist = i->second;
		sourcelist_t::iterator j;
		while ((j=thelist.begin()) != thelist.end()){
			////dsyslog("%s: deleting sourcelist entry", __PRETTY_FUNCTION__);
			delete *j;
			thelist.erase(j);
		}
		////dsyslog("%s: deleting sourcelist", __PRETTY_FUNCTION__);
		_sources.erase(i);
	}
	////dsyslog("%s: done", __PRETTY_FUNCTION__);
}

/**
 * This member should be called periodically to clean out old
 * entries.
 * \param video_source_max_age says how old video sources can
 * become in seconds.
 * \return true
 */
bool
cvideosources::cleanup(time_t video_source_max_age){
	//dsyslog("%s: begin, %i sources available", __PRETTY_FUNCTION__, _sources.size());
	printf("%s: begin, %i sources available\n", __PRETTY_FUNCTION__, _sources.size());
	time_t now = time(NULL);
	for (hash_t::iterator i = _sources.begin(); i != _sources.end(); i++){
		sourcelist_t &thelist = i->second; // shadow this parameter, otherwise the list is copied and we modify the copy
		//dsyslog("%s: considering list with %i entries", __PRETTY_FUNCTION__, thelist.size());
		for (sourcelist_t::iterator j = thelist.begin(); j!= thelist.end(); j++){
			if ((((*j)->lastupdate()) + video_source_max_age < now) // too far in the past
					|| ((*j)->lastupdate() > now)){ // in the future
				delete *j;
				thelist.erase(j);
				j = thelist.begin(); // start over
				//dsyslog("%s: entry deleted", __PRETTY_FUNCTION__);
			}
		}
		//dsyslog("%s: list with %i entries left", __PRETTY_FUNCTION__, thelist.size());
		if (thelist.size()==0){ // last source deleted
			_sources.erase(i);
			i = _sources.begin(); // start over
			//dsyslog("%s: list itself empty, deleted", __PRETTY_FUNCTION__);
		}
	}
	//dsyslog("%s: end, %i sources available", __PRETTY_FUNCTION__, _sources.size());
	//list();
	printf("%s: end, %i sources available\n", __PRETTY_FUNCTION__, _sources.size());
	return true;
}

void cvideosources::list(){
	for (hash_t::iterator j = _sources.begin(); j != _sources.end(); j++ )
	{
		printf("aggregation destination: %s\n", j->first.tostring().c_str());
	}
	
}

/**
 * Use the information from a parsed message and add
 * information to the local data store. Hint for storage
 * checks: We expect that the message allocated the memory
 * for the video sources. Every video source in the message
 * is either used by us (and cant be used any further by the
 * message) or we delete it (and it cant be used any further
 * by anyone)
 * \param m The message. It is changed.
 * \todo consolidate code with other add() methods
 */
void
cvideosources::add(vtmsg *m){
	//dsyslog("%s: begin", __PRETTY_FUNCTION__);
	assert(m);
	// search for that given aggregation destination in the local store
	hash_t::iterator i = _sources.find(m->dest);
	// if it is not found, add it
	if (i == _sources.end()){
		// nothing there yet; create a new aggr_dest in our _sources
		//dsyslog("%s: nothing known about this target, adding", __PRETTY_FUNCTION__);
		sourcelist_t newlist;
		_sources.insert(pair<aggr_dest, sourcelist_t>(m->dest, newlist)); /// \todo check for alloc-mistakes
		// set i to newly added target
		i = _sources.find(m->dest);
		// and make sure that the entry was added correctly
		assert(i != _sources.end());
	}
	// i's second member is the sourcelist containing IP addresses -> assign to thelist
	sourcelist_t &thelist = i->second;
	// we allow a maximum number of 50 IP addresses per aggregation destination (they can increase up the aggr. tree)
	unsigned int video_sources_per_aggrdest = 50;
	while ((thelist.size() < video_sources_per_aggrdest) && (m->sources.size()>0)){
		// local storage not yet exhausted and message has further information
		//dsyslog("%s: thelist.size() < 50 video sources (IPs) and vtmsg contains further sources", __PRETTY_FUNCTION__);
		cvideosource * singlesource = m->sources.front();
		// fetch first video source in row
		m->sources.pop_front();
		bool add = true;
		// check if this video source is already entered in thelist
		for (sourcelist_t::const_iterator listiter = thelist.begin(); listiter!=thelist.end(); listiter++){
			if ((*(*listiter)) == *singlesource){
				// if it is, do not add it
				add = false;
				//dsyslog("%s: source already known", __PRETTY_FUNCTION__);
				// but update the lastupdate value
				(*listiter)->updatelastupdate(singlesource->lastupdate());
				break;
			}
		}
		// if the source was so far unknown, add it to the list of sources	
		if (add) {
			thelist.push_back(singlesource);
		}
		// if the source was known, it is discarded (only the lastupdate is refreshed in the for-loop) 
		else {
			delete singlesource; /// \todo check for alloc-mistakes
		}
	}
}

/**
 * Add a specific block to the main list of available blocks. Assume that this
 * block is available locally.
 * \todo consolidate code with other add() methods
 */
void
cvideosources::add(const aggr_dest & ad, const hashes_t & hashes){
	//dsyslog("%s: begin", __PRETTY_FUNCTION__);
	hash_t::iterator i = _sources.find(ad);
	if (i == _sources.end()){
		//dsyslog("%s: nothing known about this target, adding", __PRETTY_FUNCTION__);
		sourcelist_t newlist;
		_sources.insert(pair<aggr_dest, sourcelist_t>(ad, newlist)); /// \todo check for alloc-mistakes
		// set i to new target
		i = _sources.find(ad);
		assert(i != _sources.end());
	}
	/*
	consolidate the list of hashes given by param with the existing list; add the overhead;
	*/
	sourcelist_t &thelist = i->second;
	cvideosource * singlesource = new cvideosource(*_transport, hashes, time(NULL));
	bool add = true;
	for (sourcelist_t::const_iterator listiter = thelist.begin(); listiter!=thelist.end(); listiter++){
		if ((*(*listiter)) == *singlesource){ // already known; (uses cvideosource::operator==)
			add = false;
			//dsyslog("%s: source already known for this block, update time", __PRETTY_FUNCTION__);
			(*listiter)->updatelastupdate(singlesource->lastupdate());
			break;
		}
	}
	if (add) {
		thelist.push_back(singlesource);
	} else {
		delete singlesource; /// \todo check for alloc-mistakes; who deletes these thingies?
	}
}

/**
 * Get video sources
 */
sourcelist_t
cvideosources::get(aggr_dest * dest){
	//dsyslog("%s: begin, looking for %s", __PRETTY_FUNCTION__, dest->tostring().c_str());
	sourcelist_t ret;
	ret.clear();
	hash_t::const_iterator i;
	sourcelist_t::const_iterator j;
	i = _sources.find(*dest);
	if (i!=_sources.end()){
		const sourcelist_t & thelist = i->second;
		//dsyslog("%s: found list with %i source entries", __PRETTY_FUNCTION__, thelist.size());
		for (j=thelist.begin(); j!=thelist.end(); j++){
			cvideosource * singlesource = new cvideosource(**j);
			ret.push_back(singlesource);
		}
	}
	//dsyslog("%s: returning list with %i entries", __PRETTY_FUNCTION__, ret.size());
	return ret;
}

/**
 * Check if there are video sources available for a given aggregation destination.
 * \param dest For which destination the check should be performed
 * \return true if there is at least one source
 */
bool
cvideosources::have(aggr_dest * dest){
	//dsyslog("%s: begin, looking for %s", __PRETTY_FUNCTION__, dest->tostring().c_str());
	hash_t::const_iterator i;
	sourcelist_t::const_iterator j;
	i = _sources.find(*dest);
	if (i==_sources.end())
		return false;
	//dsyslog("%s: found", __PRETTY_FUNCTION__);
	const sourcelist_t & thelist = i->second;
	if (thelist.size()==0)
		return false;
	//dsyslog("%s: ... and not empty", __PRETTY_FUNCTION__);
	return true;
}

/**
 * Copy something to be published into d and l.
 * \param d Pointer to a aggr_dest to be modified by this
 * function.
 * \param l Pointer to a list of videosources to be modified by this function.
 * It should be empty before the call. Note that this function currently only
 * copies the list itself, it doesn't copy the single elements. No new memory
 * is allocated. If you need your own memory for the video sources, use
 * cvideosources::get() instead.
 * \return true if copy was successful and pointers are now valid
 */
bool
cvideosources::next2publish(aggr_dest *d, sourcelist_t * l){
	// simplest strategy to select what to publish: monte-carlo. better
	// strategy would be to introduce and check sent counters on sources or
	// keep the sources sorted and announce the first/last
	// or pick - and - remember
	//dsyslog("%s: begin, selecting randomly from %i entries", __PRETTY_FUNCTION__, _sources.size());
	if (_sources.size()<1)
	{
		//dsyslog("%s: nothing to publish", __PRETTY_FUNCTION__);
		return false;
	}
	int which = random()%_sources.size();
	//dsyslog("%s: chose entry %i", __PRETTY_FUNCTION__, which);
	hash_t::iterator i = _sources.begin();
	while (which--)
		i++;
	*d = i->first;
	*l = i->second;
	return true;
}


