/**
 *  Copyright (C) 2004 Alo Sarv <madcat_@users.sourceforge.net>
 *
 *  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
 */

#include <hn/msocket.h>

// Iterate on download requests
void SchedBase::handleDownloads() {
	std::vector<DownloadReqBase*> toRemove;
	for (DIter i = m_downloadReqs.begin(); i != m_downloadReqs.end(); ++i) {
		// Calculate how much to assign to this download slot
		float perc = (*i)->getScore()*100/getFreeDown();
		uint32_t amount = getFreeDown()*perc/100;
		// Receive data
		uint32_t ret = (*i)->doRecv(amount);
		// Store the resulted downstream bandwidth along with timestamp
		m_lastRecv.push_front(std::make_pair(Utils::getTick(), ret));
		// Check if all of the request was fulfilled
		if ((*i)->getPending() == 0) {
			toRemove.push_back(*i);
		}
	}
	// Cleanup and notify
	while (toRemove.size()) {
		m_downloadReqs.erase(toRemove.front());
		toRemove.front()->notify();
		delete toRemove.front();
		toRemove.pop_front();
	}
}
// Iterate on upload requets
void SchedBase::handleUploads() {
	std::vector<UploadReqBase*> toRemove;
	for (UIter i = m_uploadReqs.begin(); i != m_uploadReqs.end(); ++i) {
		// Calculate bandwidth
		float perc = (*i)->getScore()*100/getFreeUp();
		uint32_t amount = getFreeUp()*perc/100;
		// send data
		uint32_t ret = (*i)->doSend(amount);
		// Store the resulted amount along with timestamp
		m_lastSent.push_front(std::make_pair(Utils::getTick(), ret));
		// Check if all of the request was fulfilled
		if ((*i)->getPending() == 0) {
			toRemove.push_back(*i);
		}
	}
	// Cleanup and notify
	while (toRemove.size()) {
		m_uploadReqs.erase(toRemove.front());
		toRemove.front()->notify();
		delete toRemove.front();
		toRemove.pop_front();
	}
}

// grant connections up to the connection limit
void SchedBase::handleConnections() {
	// Iterate on connection requests
	for (CIter i = m_connReqs.begin(); i != m_connReqs.end(); ++i) {
		if (getConnection()) {
			(*i)->doConn();
			++m_connCnt;
		} else {
			break;
		}
	}
}

// main scheduler loop, called from global event table
void SchedBase::handleEvents() {
	handleDownloads();
	handleUploads();
	handleConnections();
}

// get free downstream bandwidth by summing up last 100ms downstream data and
// comparing it to downstream limit
uint32_t SchedBase::getFreeDown() {
	m_curTick = getTick();
	while (m_lastSent.back().first + 100 < m_curTick) {
		m_lastSent.pop_back();
	}
	uint32_t sum = 0;
	for (uint32_t i = 0; i < m_lastSent.size(); ++i) {
		sum += m_lastSent[i].second;
	}
	return getUpLimit() - sum;
}

// get free upstream bandwidth by summing up the last 100ms upstream data and
// comparing it to upstream limit
uint32_t SchedBase::getFreeUp() {
	m_curTick = getTick();
	while (m_lastRecv.back().first + 100 < m_curTick) {
		m_lastRecv.pop_back();
	}
	uint32_t sum = 0;
	for (uint32_t i = 0; i < m_lastRecv.size(); ++i) {
		sum += m_lastRecv[i].second;
	}
	return getDownLimit() - sum;
}

bool SchedBase::getConnect() {
	return s_connCnt < s_connLimit;
}