/*
 * (c) 2005 Research Group Dr. Fuhrmann, University of Karlsruhe, Germany
 * All rights reserved.
 *
 * Author:
 *         Johannes Franz
 *
 * $Id: datastructs.cpp,v 1.20 2006/01/09 16:08:57 kutzner Exp $
 *
 */


#include "datastructs.h"
#include <iostream>


using namespace std;




// cListOfTimerjobs

/*
 * @brief return the job with the highest prio for schedtime
 * return NULL if no job is there
 *
 */
cTimerjob *cListOfTimerjobs::gethighestprio(int schedtime) {
    cTimerjob *job = NULL;
    list<cTimerjob>::iterator i; 
    int prio = 0;
    assert(schedtime%60 == 0);
    int index = schedtime / 60;
    for(i = jobs[index % DISTANCE].begin(); i != jobs[index % DISTANCE].end(); i++) {
	if ( (*i).getPrio() > prio) {
	    if ( job == NULL ) {
		job = new cTimerjob;
	    }
	    prio = (*i).getPrio();
	    *job = (*i);
	}
    }
    // no job for this schedtime, return NULL
    if (job == NULL) {
	return NULL;
    }
    // mark for no other retrieval in getnextprio
    job->mark();
    return job;
}

/*
 * @brief returns the next highest priority job at scheduleTime. 
 * scheduleTime has to be in seconds
 * once returned, the job won't be returned next time
 */
 
cTimerjob cListOfTimerjobs::getnextprio(int scheduleTime) {
    assert(scheduleTime%60 == 0);
    int index = scheduleTime / 60;
    
    cTimerjob job;
    int prio = 0;
    for(list<cTimerjob>::iterator i = jobs[index % DISTANCE].begin(); i != jobs[index % DISTANCE].end(); i++) {
	if ( (!(*i).ismarked()) && ( (*i).getPrio() > prio)) {
	    prio = (*i).getPrio();
	    job = (*i);
	}
    }
    job.mark();
    return job;
   // if no job then prio = 0 
    
}

void cListOfTimerjobs::sort(int schedtime) {
    assert(schedtime%60 == 0);
    int index =  schedtime / 60;

    jobs[index % DISTANCE].sort();
}

void cListOfTimerjobs::sortall() {
    for (int i = 0; i < DISTANCE; i++) {
	sort(i);
    }
}

void cListOfTimerjobs::print(int schedtime) {
    assert(schedtime%60 == 0);
    int index = schedtime / 60;
    cout << "elements for index " << index << ": " << endl;
    for(list<cTimerjob>::iterator i = jobs[index].begin(); i != jobs[index].end(); i++) {
	cout << "\t" << (i)->getChannel() << ", " << (i)->getPrio() << endl;
    }
    cout << "*******************************" << endl;
}

void cListOfTimerjobs::printall() {
    for(int i = 0; i < DISTANCE; i++) {
	print(i);
    }
    cout << "|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |" << endl;  
}

/*!
 * @brief cleans the array field for schedtime
 *
 * @param schedtime the scheduletime in seconds
 */
void cListOfTimerjobs::clean(int schedtime) {
    assert(schedtime%60 == 0);
    int index = schedtime / 60;
    jobs[index % DISTANCE].clear();	
}

void cListOfTimerjobs::cleanall() {
    for(int i=0; i < DISTANCE; i++) {
	clean(i);
    }
}

void cListOfTimerjobs::insert(cTimerjob j, int scheduleTime) {
    assert(scheduleTime%60 == 0);
    scheduleTime /= 60;
    jobs[scheduleTime % DISTANCE].push_back(j);
}

/*! 
 * @brief send appropriate messages for schedtime
 *
 */

void cListOfTimerjobs::sendRCMessages(int schedtime, int sd) {
    //cTimerjob job;
    // sort jobs indirectly
    assert(schedtime%60 == 0);
    int index = schedtime / 60;

    cTimerjob* jobp = gethighestprio(schedtime);
    if (jobp == NULL) return;
    cTimerjob job = *jobp;
    cSchedMsgRC msg(1.0,1.0);
    msg.setChannel(job.getChannel());
    msg.setStartTime(schedtime);
    msg.sendto(sd);
    COUT("RC message sent");

    // stick to this job in vdr
    job.getTimerp()->SetPriority(99);
    job.setPrio(99);

    list<cTimerjob>::const_iterator i = jobs[index % DISTANCE].begin();
    i++;
    for (; i != jobs[index % DISTANCE].end(); i++) {
	msg.setRC(1/(*i).getPrio(), 0.0);
	msg.setChannel( (*i).getChannel());
	msg.setStartTime( schedtime );
	msg.sendto(sd);
	COUT("Suppress message sent");
	// prevent unwanted jobs from recording
	if ( (*i).getTimerp()->Priority() == 99) {
	    (*i).getTimerp()->SetPriority(98);
	}
    }



}

/*
 * @brief corrects the original prio 
 * in looking at the value of the entry in front
 */
void cListOfTimerjobs::correctOriginalPrio(cTimerjob& job, int scheduletime) 
{
    assert(scheduletime%60 == 0);
    int index = scheduletime/60;

    for (list<cTimerjob>::iterator i = jobs[index%DISTANCE - 1].begin(); i != jobs[index%DISTANCE - 1].end(); i++){
	    ///\bug segfault at the line above. Does the list behind this job index always exists?

	if ((*i).getChannel() == job.getChannel()) {
	    job.setOriginalPrio( (*i).getOriginalPrio());
	}
    }
}

bool cListOfTimerjobs::exists(time_t when, string channel) {
    int index = when / 60;
    for (list<cTimerjob>::iterator i = jobs[index%DISTANCE].begin(); i != jobs[index%DISTANCE].end(); i++){
	if ( (*i).getChannel() == channel ) {
	    return true;
	}
    }
    return false;
}

// cListOfTimers

void cListOfTimers::print() {
    int count = 0;
    for(list<timer>::iterator i = begin(); i!=end(); i++, count++) {
	cout << count << ". " << "when: " << (*i).when << ", channel: " << (*i).channel << ", starttime: " << (*i).starttime << endl;
    }
}

void cListOfTimers::add(const timer& tim) {		
    Lock();
    push_back(tim);
    COUT("cListOfTimers.add: new timer added");
    Unlock();
}

//void cListOfTimers::sort() {
//    sort();
//}

/*
 * @brief get the list of all timers that come next
 * all these timers get cleared
 *
 */
list<timer> cListOfTimers::getnext() {
    list<timer> timlist;
    Lock();
    int next = 0;
    for (list<timer>::iterator i = begin(); i != end(); i++) {
	if ( next == 0 || (*i).when < next ) {
	    next = (*i).when;
	}
    }
    for (list<timer>::iterator i = begin(); i != end(); i++) {
	if ( (*i).when == next) {
	    timlist.push_back(*i);
	    COUT("getnext: element added");
	}
    }
    remove(next);
    Unlock();
    return timlist;
}

void cListOfTimers::removeallbefore(time_t tim) {
}

void cListOfTimers::removeallat(time_t tim) {
    Lock();
    remove(timer(tim));
    Unlock();
}

void cListOfTimers::resettimer(timer tim) {
    Lock();
    for (list<timer>::iterator i = begin(); i != end(); i++) {
	if ( ( (*i).channel == tim.channel ) && ( (*i).starttime == tim.starttime) ) {
	    (*i).when = tim.when;
	}
    }
    Unlock();
}

//void cListOfTimers::clean(time_t now) {
//    //time_t tmp;
//    int count = 0;
//
//    sort();
//
//    for(list<cListOfTimers::timer *>::iterator i = timers.begin(); i != timers.end(); i++) {
//	if ( (*i)->when >= now ) {
//	    break;
//	}
//	//	timers.pop_front();
//	count++;
//    }
//
//    for(int i=0; i<count; i++) {
//	timers.pop_front();
//    }
//}
//

float cJob::aggregateR() {
    float sum = 0.0;
    for (list<cDDON *>::const_iterator i = neighbours.begin(); i != neighbours.end(); i++) {
	sum += (*i)->r;
    }
    return sum;
}

float cJob::aggregateC() {
    float sum = 0.0;
    for (list<cDDON *>::const_iterator i = neighbours.begin(); i != neighbours.end(); i++) {
	sum += (*i)->c;
    }
    return sum;
}

// cScheMsg

void cSchedMsg::setChannel(const string& channelname) 
{
    channel = channelname;
    serialized = false;
}


void cSchedMsg::setStartTime(const time_t starttime)
{
    this->starttime = starttime;
    serialized = false;
}


void cSchedMsg::sendto(int sd) 
{
    struct sa_igor sa;
    ostringstream os;
    os << getStartTime();
    string addr(os.str());
    sa.address = addr.c_str();
    sa.len = addr.size();
    sa.mustbehashed = true;
    sendto(sd, sa);
}

void cSchedMsg::sendto(int sd, const sa_igor& sa) {

    serialize();
    COUT("The following message will be sent to " << sd << ": " << rawmsg.c_str());
    int sendtores = igor_sendto(sd, rawmsg.c_str(), rawmsg.size() + 1, true, &sa);
    if (sendtores != IGOR_ERR_NOERR) {
	cout << "sendtoerror: " << igor_strerror(sd) << endl;
    }
}

// cSchedMsgRC

void cSchedMsgRC::setRC(float request, float commit)
{
    this->request = request;
    this->commit = commit;
    setNotSerialized();
}

string cSchedMsgRC::serialize()
{
    ostringstream os;
    if (!isSerialized()) {
	os << "RC " <<  getR() << " " << getC() << " " << getStartTime() << " " << getChannel();
	setSerialized();
	return rawmsg = os.str();

    } else {
	return rawmsg;
    }
}

void cSchedMsgRC::createFromRaw(const string& msg)
{
    istringstream is(msg);
    float r,c;
    time_t t;
    string channel;
    string type;
    is >> type;
    assert(type == "RC");
    is >> r >> c >> t >> ws;
    ostringstream os;
    os << is.rdbuf();
    setChannel(os.str()); 
    setRC(r,c);
    setStartTime(t);
}

// cSchedMsgSuppress

void cSchedMsgSuppress::setSuppress(const float suppress)
{
    this->suppress = suppress;
    setNotSerialized();
}

string cSchedMsgSuppress::serialize()
{
    ostringstream os;
    if (!isSerialized()) {
	os << "S " << getSuppress() << " " << getStartTime() << " " << getChannel();
	setSerialized();
	return rawmsg = os.str();
    } else {
	return rawmsg;
    }
}


void cSchedMsgSuppress::createFromRaw(const string& msg)
{
    istringstream is(msg);
    float s;
    time_t t;
    string channel, type;
    is >> type;
    assert(type == "S");
    is >> s >> t >> ws;
    ostringstream os;
    os << is.rdbuf();
    setChannel(os.str());
    setSuppress(s);
    setStartTime(t);
}

// cSchedMsgQueue

cSchedMsgQueue::~cSchedMsgQueue() {
    for ( list<cSchedMsg *>::const_iterator i = queue.begin(); i != queue.end(); i++) {
	delete *i;	
    }		
}

void cSchedMsgQueue::lock() {
    mutex.Lock();
}

void cSchedMsgQueue::unlock() {
    mutex.Unlock();
}

void cSchedMsgQueue::put(cSchedMsg * msg)  {
    queue.push_back(msg);
}

cSchedMsg * cSchedMsgQueue::get() {
    cSchedMsg *msg = queue.front();
    queue.pop_front();
    return msg;
}


/*
   cSchedMsg::Msg::Msg(float req, float com)
   {
   request = req;
   commit = com;
   }

   cSchedMsg::Msg::~Msg() 
   {}

*/

