/**
 *  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/ssocket.h>
#include <hn/gettickcount.h>
#include <hn/log.h>

SchedBase::SchedBase() : m_upLimit(10*1024), m_downLimit(100*1024),
m_connLimit(100), m_connCnt(0), m_totalUp(0), m_totalDown(0) {}

// Iterate on download requests
void SchedBase::handleDownloads() {
	logDebug(
		boost::format("Scheduler> I have %d download requests pending.")
		% m_downloadReqs.size()
	);
	logDebug(
		boost::format(
			"Scheduler> I have %d bytes download speed spare."
		) % getFreeDown()
	);

	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 = static_cast<uint32_t>(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));
		toRemove.push_back(*i);
	}
	// notify
	while (toRemove.size()) {
		m_downloadReqs.erase(toRemove.back());
		toRemove.back()->notify();
		delete toRemove.back();
		toRemove.pop_back();
	}
}
// Iterate on upload requets
void SchedBase::handleUploads() {
	logDebug(
		boost::format("Scheduler> I have %d upload requests pending.")
		% m_uploadReqs.size()
	);
	logDebug(
		boost::format("Scheduler> I have %d bytes upload speed spare.")
		% getFreeUp()
	);

	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 = static_cast<uint32_t>(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.back());
		toRemove.back()->notify();
		delete toRemove.back();
		toRemove.pop_back();
	}
}

// grant connections up to the connection limit
void SchedBase::handleConnections() {
	std::vector<ConnReqBase*> toRemove;
	for (CIter i = m_connReqs.begin(); i != m_connReqs.end(); ++i) {
		if (getConnection()) {
			if ((*i)->doConn()) {
				toRemove.push_back(*i);
			}
			++m_connCnt;
		} else {
			break;
		}
	}
	while (toRemove.size()) {
		m_connReqs.erase(toRemove.back());
		toRemove.back()->notify();
		delete toRemove.back();
		toRemove.pop_back();
	}
}

// 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 = Utils::getTick();
	while (m_lastSent.size() && m_lastSent.back().first + 100 < m_curTick) {
		m_lastSent.pop_back();
	}
	uint32_t sum = 0;
	for (QIter i = m_lastSent.begin(); i != m_lastSent.end(); ++i) {
		sum += (*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 = Utils::getTick();
	while (m_lastRecv.size() && m_lastRecv.back().first + 100 < m_curTick) {
		m_lastRecv.pop_back();
	}
	uint32_t sum = 0;
	for (QIter i = m_lastRecv.begin(); i != m_lastRecv.end(); ++i) {
		sum += (*i).second;
	}
	return getDownLimit() - sum;
}

bool SchedBase::getConnection() {
	return m_connCnt < m_connLimit;
}