/***************************************************************************
                                 status.cpp
                             -------------------
    begin                : Sat Sep 29 2001
    copyright            : (C) 2001 - 2003 by Roland Riegel
    email                : feedback@roland-riegel.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#include "status.h"
#include "window.h"
#include "options.h"

Status::Status()
{
	m_min = m_max = m_cur = m_total = 0;
	m_averagesmoothness = 0;
}

Status::~Status()
{
}

//new traffic measurement has been made => update statistics
void Status::update( unsigned long new_value, unsigned long new_total )
{
	
	m_cur = new_value;
	minMax( m_cur ); //calculate new min/max traffic values
	updateAverage( m_cur ); //calculate new average
	
	/*
	 *set new total transfered data
	 *the following is a workaround for the value limitations of the
	 *unsigned int variable used in the kernel to produce
	 *the /proc/net/dev file
	 *(the total bytes value reaches 4GB and then switches to 0)
	 */
	if( new_total < ( m_total % UINT_MAX ) )
		m_total = ( ( m_total / UINT_MAX ) + 1 ) * UINT_MAX + new_total;
	else
		m_total = ( m_total / UINT_MAX ) * UINT_MAX + new_total;
	
}

//print statistics
void Status::print( Window& window, int x, int y, status_format traff_format, status_format data_format )
{
	unsigned long value;
	char fText[100] = "";
	string unit_string;
	float unit_factor;
	
	//print current traffic
	window.setXY( x, y );
	getUnit( traff_format, m_cur, unit_string, unit_factor );
	sprintf( fText, "Curr: %.2f %s/s\n", m_cur / unit_factor, unit_string.c_str() );
	window.print( fText );
	
	//print average traffic
	window.setX( x );
	value = calcAverage();
	getUnit( traff_format, value, unit_string, unit_factor );
	sprintf( fText, "Avg: %.2f %s/s\n", value / unit_factor, unit_string.c_str() );
	window.print( fText );
	
	//print min traffic since nload start
	window.setX( x );
	getUnit( traff_format, m_min, unit_string, unit_factor );
	sprintf( fText, "Min: %.2f %s/s\n", m_min / unit_factor, unit_string.c_str() );
	window.print( fText );
	
	//print max traffic since nload start
	window.setX( x );
	getUnit( traff_format, m_max, unit_string, unit_factor );
	sprintf( fText, "Max: %.2f %s/s\n", m_max / unit_factor, unit_string.c_str() );
	window.print( fText );
	
	//print total traffic since last system reboot
	window.setX( x );
	getUnit( data_format, m_total, unit_string, unit_factor );
	sprintf( fText, "Ttl: %.2f %s\n", m_total / unit_factor, unit_string.c_str() );
	window.print( fText );
}

//reset all displayed values to zero
void Status::resetTrafficData()
{
	m_cur = m_min = m_max = m_total = 0;
	m_average_values.clear();
}

//determine the matching unit string, e.g. "kBit" for status_format::kilobit, and
//the matching conversion factor between Byte and e.g. status_format::kilobit
void Status::getUnit( status_format format, long long value, string& description, float& factor )
{
	factor = (float) 1 / ( format % 2 == 0 ? 8 : 1 );
	description = format % 2 == 0 ? "Bit" : "Byte";
	
	switch( format )
	{
		case human_readable_bit:
		case human_readable_byte:
			factor *= 1024 * 1024 * 1024;
			for( int i = 3; i >= 0; i-- )
			{
				if ( value * ( format % 2 == 0 ? 8 : 1 ) >= factor )
					switch(i)
					{
						case 3:
							description = 'G' + description;
							return;
						case 2:
							description = 'M' + description;
							return;
						case 1:
							description = 'k' + description;
							return;
						default:
							return;
					}
				factor /= 1024;
			}
			return;
		case bit:
		case byte:
			return;
		case kilobit:
		case kilobyte:
			factor *= 1024;
			description = 'k' + description;
			return;
		case megabit:
		case megabyte:
			factor *= 1024 * 1024;
			description = 'M' + description;
			return;
		case gigabit:
		case gigabyte:
			factor *= 1024 * 1024 * 1024;
			description = 'G' + description;
			return;
		default: //should never be executed
			return;
	}

}

//calculate min and max traffic values
void Status::minMax( unsigned long new_value )
{
	
	//if this is the first time call, set min/max to current value
	if( m_min == 0 && m_max == 0 )
	{
		m_min = new_value;
		m_max = new_value;
		return;
	}
	
	m_min = new_value < m_min ? new_value : m_min;
	
	m_max = new_value > m_max ? new_value : m_max;
	
}

//set the "reaction time" to the current traffic situation of the average values
void Status::setAverageSmoothness( OptionInt* new_averagesmoothness )
{
	m_averagesmoothness = new_averagesmoothness;
}

//put new value into average calculation
void Status::updateAverage( unsigned long new_value )
{
	
	/*
	 * average calculation is not very good at the moment as it is dependent
	 * from the display refresh interval.
	 * could need some help here.
	 */
	
	m_average_values.push_front( new_value );
	
	//limit value count dependent of the average smoothness
	//ranges between 1 * 45 and 9 * 45 single values
	while( m_average_values.size() > (unsigned int) *m_averagesmoothness * 45 )
	{
		m_average_values.pop_back();
	}
	
}

//calculate current average
unsigned long Status::calcAverage()
{
	if( m_average_values.size() == 0 ) return 0;

	unsigned long sum = 0;
	for( list<unsigned long>::const_iterator i = m_average_values.begin(); i != m_average_values.end(); i++ )
	{
		sum += (*i);
	}
	
	return sum / m_average_values.size();
}

int Status::averageSmoothness()
{
	int avg_smooth =  m_averagesmoothness ? (int) *m_averagesmoothness : STANDARD_AVERAGE_SMOOTHNESS;
	return avg_smooth > 9 ? 9 : ( avg_smooth < 1 ? 1 : avg_smooth );
}


syntax highlighted by Code2HTML, v. 0.9.1