/***************************************************************************
			weatherlib.cpp  -  description
			-------------------
begin                : Wed Jul 5 2000
copyright            : (C) 2000 by Ian Reinhart Geiser
email                : geiseri@msoe.edu
***************************************************************************/

/***************************************************************************
*                                                                         *
*   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 "config.h"
#include <qfile.h>
#include <qdatetime.h>
#include <qtextstream.h>
#include <kglobal.h>
#include <klocale.h>
#include <kdebug.h>
#include <math.h>
#include <krfcdate.h>
#include <kio/job.h>
#include <kurl.h>
#include <knotifyclient.h>
#include <ktempfile.h>
#include <unistd.h>
#include <kapplication.h>
#include <kpassivepopup.h>
#include "stationdatabase.h"
#include "sun.h"
#include "weatherlib.h"

#include "weatherlib.moc"

class WeatherLib::Data
{
	public:
		Data();
		~Data(){ if ( target ) delete target; }

		void clear();

		/** The current weather state outside */
		QString theWeather;
		int clouds;
		int windMPH;
		float tempF;
		bool heavy;
		QStringList qsCoverList;
		QStringList qsCurrentList;
		QDate qsDate;
		QString qsPressure;
		QString qsTemperature;
		QString qsDewPoint;
		QString qsRelHumidity;
		QTime qsTime;
		QString qsVisibility;
		QString qsWindSpeed;
		QString qsWindChill;
		QString qsHeatIndex;
		QString qsWindDirection;
		QString reportLocation;
		QDateTime age;
		KTempFile *target;
		bool downloading;
		bool updated;
		KIO::Job *job;
};

WeatherLib::Data::Data()
		: target( 0 ), job( 0 )
{
	clear();
}

void WeatherLib::Data::clear()
{
	theWeather = QString::null;
	clouds = -1;
	windMPH = 0;
	tempF = 0;
	heavy = false;
	qsCoverList.clear();
	qsCurrentList.clear();
	qsDate = QDate::currentDate();
	qsTime = QTime::currentTime();
	qsPressure = QString::null;
	qsTemperature = QString::null;
	qsDewPoint = QString::null;
	qsRelHumidity = QString::null;
	qsVisibility = QString::null;
	qsWindSpeed = QString::null;
	qsWindChill = QString::null;
	qsHeatIndex = QString::null;
	qsWindDirection = QString::null;
	age = QDateTime::currentDateTime();
	downloading = false;
	updated = false;
	job = 0;

	if ( target )
	{
		delete target;
		target = 0;
	}
}

WeatherLib::WeatherLib(StationDatabase *stationDB, QObject *parent, const char *name)
	: QObject (parent, name)
{
	KGlobal::locale()->insertCatalogue("kweather");
	
	m_StationDb    = stationDB;
	
	CoverRegExp    = QRegExp("^(FEW|SCT|BKN|OVC|SKC|CLR|CAVOK)([0-9]{3})?(?:TCU|CB)?$");
	CurrentRegExp  = QRegExp("^(\\+|-|VC)?([A-Z]{2,4})$");
	WindRegExp     = QRegExp("^([0-9]{3}|VRB)([0-9]{2,3})(?:G([0-9]{2,3}))?(KT|KMH|MPS)$");
	// VisRegExp does not work with fractions...yet
	VisRegExp      = QRegExp("^([0-9]{1,2})SM$");
	TempRegExp     = QRegExp("^(M)?([0-9]{2})/(?:(M)?([0-9]{2}))?$");
	TimeRegExp     = QRegExp("^([0-9]{2}:[0-9]{2})$");
	DateRegExp     = QRegExp("^([0-9]{4}/[0-9]{2}/[0-9]{2})$");
	PressRegExp    = QRegExp("^([AQ])([0-9]{4})$");

	data.setAutoDelete( true );
}

WeatherLib::~WeatherLib()
{
}

/** Parse the current cover type */
bool WeatherLib::parseCover(const QString &s, Data *d){
	if (CoverRegExp.search(s) > -1)
	{
		kdDebug(12006) << "Cover: " << CoverRegExp.capturedTexts().join("-")
			<< endl;

		QString sCode = CoverRegExp.cap(1);
		float fFeet = CoverRegExp.cap(2).toFloat();
		QString sClouds;
		QString skycondition;

		if (KGlobal::locale()->measureSystem() == KLocale::Metric)
		{
			fFeet = fFeet * 0.3048;  // Check this!
		}

		if (fFeet >= 10)
		{
			fFeet /= 10;
			if (KGlobal::locale()->measureSystem() == KLocale::Metric)
				sClouds = i18n("%1 kilometers").arg(fFeet,0,'f',1);
			else
				sClouds = i18n("%1 thousand feet").arg(fFeet,0,'f',1);
		}
		else
		{
			fFeet *= 100;
			if (KGlobal::locale()->measureSystem() == KLocale::Metric)
				sClouds = i18n("%1 meters").arg(fFeet,0,'f',0);
			else
				sClouds = i18n("%1 feet").arg(fFeet);
		}

		if (sCode == "FEW")
		{
			skycondition = i18n( "Few clouds at %1" ).arg(sClouds);
			d->clouds += 2;
		}
		else if (sCode == "SCT")
		{
			skycondition = i18n( "Scattered clouds at %1" ).arg(sClouds);
			d->clouds += 4;
		}
		else if (sCode == "BKN")
		{
			skycondition = i18n( "Broken clouds at %1" ).arg(sClouds);
			d->clouds += 8;
		}
		else if (sCode == "OVC")
		{
			skycondition = i18n( "Overcast clouds at %1" ).arg(sClouds);
			d->clouds += 64;
		}
		else if ((sCode == "CLR") || (sCode == "SKC") || (sCode == "CAVOK"))
		{
			skycondition = i18n("Clear skies");
			d->clouds = 0;
		}

		kdDebug(12006) << "*** Clouds: " << d->clouds << endl;
		d->qsCoverList << skycondition;

		return true;
	}
	return false;
}

/** Parse the current weather conditions */
bool WeatherLib::parseCurrent(const QString &s, Data *d){
	if (CurrentRegExp.search(s) > -1)
	{
		QString sIntensity = CurrentRegExp.cap(1);
		QString sCode = CurrentRegExp.cap(2);
		QString currentWeather = "";

		kdDebug(12006) << "Current: " << CurrentRegExp.capturedTexts().join("-") << endl;

		// Decode the intensity
		if (sIntensity == "+")
		{
			currentWeather = i18n("Heavy");
			d->heavy = true;
		}
		else if (sIntensity == "-")
		{
			currentWeather = i18n("Light");
			d->heavy = false;
		}

		// Decode the descriptor
		if (sCode.contains("MI"))
			currentWeather += i18n(" Shallow");
		else if (sCode.contains("PR"))
			currentWeather += i18n(" Partial");
		else if (sCode.contains("BC"))
			currentWeather += i18n(" Patches");
		else if (sCode.contains("DR"))
			currentWeather += i18n(" Low Drifting");
		else if (sCode.contains("BL"))
			currentWeather += i18n(" Blowing");
		else if (sCode.contains("SH"))
		{
			currentWeather += i18n(" Showers");
			d->theWeather = "shower";
		}
		else if (sCode.contains("TS"))
		{
			currentWeather += i18n(" Thunder Storm");
			d->theWeather = "tstorm";
		}
		else if (sCode.contains("FZ"))
		{
			currentWeather += i18n(" Freezing");
		}

		// Decode weather phenomena
		if (sCode.contains("DZ"))
		{
			currentWeather += i18n(" Drizzle");
			d->theWeather = "light_rain";
		}
		else if (sCode.contains("RA"))
		{
			currentWeather += i18n(" Rain");
			d->theWeather = "shower";
		}
		else if (sCode.contains("SN"))
		{
			currentWeather += i18n(" Snow");
			d->theWeather = "snow";
		}
		else if (sCode.contains("SG"))
		{
			currentWeather += i18n(" Snow Grains");
			d->theWeather = "snow4";
		}
		else if (sCode.contains("IC"))
		{
			currentWeather += i18n(" Ice Crystals");
			d->theWeather = "hail";
		}
		else if (sCode.contains("PE"))
		{
			currentWeather += i18n(" Ice Pellets");
			d->theWeather = "hail";
		}
		else if (s.contains("GR"))
		{
			currentWeather += i18n(" Hail");
			d->theWeather = "hail";
		}
		else if (sCode.contains("GS"))
		{
			currentWeather += i18n(" Small Hail Pellets");
			d->theWeather = "hail";
		}
		else if (s.contains("UP"))
		{
			currentWeather += i18n(" Unknown Precipitation");
			d->theWeather = iconName("shower1", d);
		}
		else if (sCode.contains("BR"))
		{
			currentWeather += i18n(" Mist");
			// Mist has lower priority than say rain or snow
			if ( d->theWeather.isEmpty() )
			{
				d->theWeather = "mist";
			}
		}
		else if (sCode.contains("FG"))
		{
			currentWeather += i18n(" Fog");
			// Fog has lower priority than say rain or snow
			if ( d->theWeather.isEmpty() )
			{
				d->theWeather = "fog";
			}
		}
		else if (sCode.contains("FU"))
			currentWeather += i18n(" Smoke");
		else if (sCode.contains("VA"))
			currentWeather += i18n(" Volcanic Ash");
		else if (sCode.contains("DU"))
			currentWeather += i18n(" Widespread Dust");
		else if (sCode.contains("SA"))
			currentWeather += i18n(" Sand");
		else if (sCode.contains("HZ"))
			currentWeather += i18n(" Haze");
		else if (sCode.contains("PY"))
			currentWeather += i18n(" Spray");
		else if (sCode.contains("PO"))
			currentWeather += i18n(" Dust/Sand Swirls");
		else if (sCode.contains("SQ"))
			currentWeather += i18n(" Sudden Winds");
		else if (sCode.contains("FC"))
		{
			if (sIntensity == "+")
				currentWeather = i18n("Tornado");
			else
				currentWeather += i18n(" Funnel Cloud");
		}
		else if (sCode.contains("SS"))
			currentWeather += i18n(" Sand Storm");
		else if (sCode.contains("DS"))
			currentWeather += i18n("Dust Storm");

		if (!currentWeather.isEmpty())
			d->qsCurrentList << currentWeather;

		return true;
	}
	return false;
}

/** Parse out the current temperature */
bool WeatherLib::parseTemperature(const QString &s, Data *d){
	if (TempRegExp.search(s) > -1)
	{
		QString unit;
		kdDebug(12006) << "Temp: " << TempRegExp.capturedTexts().join("-")
			<< endl;

		float fTemp = TempRegExp.cap(2).toFloat();
		if (TempRegExp.cap(1) == "M" && fTemp != 0 )
			fTemp *= -1;
		float fDew = TempRegExp.cap(4).toFloat();
		if (TempRegExp.cap(3) == "M" && fDew != 0 )
			fDew *= -1;

		#define E(t) ::pow(10, 7.5*t/(237.7+t))
		float fRelHumidity = E(fDew)/E(fTemp) * 100;
		d->qsRelHumidity.setNum(fRelHumidity, 'f', 1);
		d->qsRelHumidity += "%";

		float fHeatIndex = 0;
		d->tempF = (fTemp*9/5)+32;
		if (d->tempF >= 80)
		{
			#define SQR(a)  ((a)*(a))
			fHeatIndex = -42.379 + (2.04901523*d->tempF)
				+ (10.14333127*fRelHumidity)
				- (0.22475541*d->tempF*fRelHumidity)
				- (0.00683783*SQR(d->tempF))
				- (0.05481717*SQR(fRelHumidity))
				+ (0.00122874*SQR(d->tempF)*fRelHumidity)
				+ (0.00085282*d->tempF*SQR(fRelHumidity))
				- (0.00000199*SQR(d->tempF)*SQR(fRelHumidity));

			if ( fHeatIndex <= d->tempF )
				fHeatIndex = 0;
		}

		if (KGlobal::locale()->measureSystem() == KLocale::Metric)
		{
			unit = i18n("°C");
			d->qsTemperature.setNum(fTemp);
			d->qsDewPoint.setNum(fDew);
			if (fHeatIndex >= 80)
				d->qsHeatIndex.setNum((fHeatIndex-32)*5/9, 'f', 1);
		}
		else
		{
			unit = i18n("°F");
			d->qsTemperature.setNum((fTemp*9/5) + 32);
			d->qsDewPoint.setNum((fDew*9/5) + 32);
			if (fHeatIndex >= 80)
				d->qsHeatIndex.setNum(fHeatIndex, 'f', 1);
		}
		d->qsTemperature += unit;
		d->qsDewPoint += unit;
		if (!d->qsHeatIndex.isEmpty())
			d->qsHeatIndex += unit;

		return true;
	}
	return false;
}

/** Parse out the current date. */
bool WeatherLib::parseDate(const QString &s, Data *d){
	if (DateRegExp.search(s) > -1)
	{
		kdDebug(12006) << "Date: " << DateRegExp.capturedTexts().join("-")
			<< endl;
		QString dateString = DateRegExp.cap(1);
		QString day, month, year;

		day = dateString.mid(8,2);
		month = dateString.mid(5,2);
		year = dateString.mid(0,4);

		QDate theDate(year.toInt(), month.toInt(), day.toInt());


		d->qsDate = theDate;
		return true;
	}
	return false;
}

/** Parse out the current time. */
bool WeatherLib::parseTime(const QString &s, Data *d){
	if (TimeRegExp.search(s) > -1)
	{
		kdDebug(12006) << "Time: " << TimeRegExp.capturedTexts().join("-")
			<< endl;

		QString hour, minute, dateString;

		dateString = TimeRegExp.cap(1);
		hour = dateString.mid(0,2);
		minute = dateString.mid(3,2);
		QTime theTime(hour.toInt(), minute.toInt());

		d->qsTime = theTime;
		return true;
	}
	return false;
}

/** Parse out the current visibility */
bool WeatherLib::parseVisibility( const QString &s, Data *d){
	if (VisRegExp.search(s) > -1)
	{
		float fVisibility = VisRegExp.cap(1).toFloat();

		kdDebug(12006) << "Visibility: " << VisRegExp.capturedTexts().join("-")
			<< endl;

		if (KGlobal::locale()->measureSystem() == KLocale::Metric)
		{
			fVisibility *= 1.6;
			d->qsVisibility.setNum(fVisibility);
			d->qsVisibility += i18n("km");
		}
		else
		{
			d->qsVisibility.setNum(fVisibility);
			d->qsVisibility += i18n("m");
		}
		return true;
	}
	return false;
}

/** Parse out the current pressure. */
bool WeatherLib::parsePressure( const QString &s, Data *d){
	if (PressRegExp.search(s) > -1)
	{
		QString type = PressRegExp.cap(1);
		float fPressure = PressRegExp.cap(2).toFloat();

		kdDebug(12006) << "Pressure: " << PressRegExp.capturedTexts().join("-")
			<< endl;

		if (KGlobal::locale()->measureSystem() == KLocale::Metric)
		{
			if (type == "A")
				fPressure *= (33.8639/100);
			d->qsPressure.setNum(fPressure, 'f', 0);
			d->qsPressure += i18n(" hPa");
		}
		else
		{
			if (type == "Q")
				fPressure /= 33.8639;
			else
				fPressure /= 100;
			d->qsPressure.setNum(fPressure, 'f', 2);
			d->qsPressure += i18n("\" Hg");
		}
		return true;
	}
	return false;
}

struct WeatherLib::wind_info
{
	unsigned int   number;
	QString        name;
};

const struct WeatherLib::wind_info WeatherLib::wind_direction[] = 
{
	{ 0,   i18n("N")   },   // North is 0 to 11, and so on
	{ 12,  i18n("NNE") },
	{ 33,  i18n("NE")  },
	{ 57,  i18n("ENE") },
	{ 79,  i18n("E")   }, 
	{ 102, i18n("ESE") },
	{ 124, i18n("SE")  },
	{ 147, i18n("SSE") },
	{ 169, i18n("S")   },
	{ 192, i18n("SSW") },
	{ 214, i18n("SW")  },
	{ 237, i18n("WSW") },
	{ 259, i18n("W")   },
	{ 282, i18n("WNW") },
	{ 304, i18n("NW")  },
	{ 327, i18n("NNW") },
	{ 349, i18n("N")   },
	{ 360, i18n("N")   }
};


QString WeatherLib::parseWindDirection(const unsigned int direction)
{
	unsigned int i = 0;
	
	for (i = 0; i < (sizeof(wind_direction) / sizeof(wind_info)) - 1; i++)
	{
		if (direction >= wind_direction[i].number &&
		    direction < wind_direction[i + 1].number)
		{
			break;
		}
	}
	
	return wind_direction[i].name;
}

/** Parse the wind speed */
bool WeatherLib::parseWindSpeed(const QString &s, Data *d)
{
	if (WindRegExp.search(s) > -1)
	{
		unsigned int direction = WindRegExp.cap(1).toInt();
		int iWindSpeed = WindRegExp.cap(2).toInt();
		int iGustSpeed = WindRegExp.cap(3).toInt();
		QString sWindUnit = WindRegExp.cap(4);

		kdDebug(12006) << "Wind: " << WindRegExp.capturedTexts().join("-")
			<< endl;

		if (KGlobal::locale()->measureSystem() == KLocale::Metric)
		{
			if (sWindUnit == "KT")
			{
				iWindSpeed = (int) (iWindSpeed * 3.6/1.94);
				iGustSpeed = (int) (iGustSpeed * 3.6/1.94);
			}
			else if (sWindUnit == "MPS")
			{
				iWindSpeed = (int) (iWindSpeed * 3.6);
				iGustSpeed = (int) (iGustSpeed * 3.6);
			}
			d->windMPH = (int) (iWindSpeed/1.61);
			sWindUnit = i18n(" km/h");
			d->qsWindSpeed.setNum(iWindSpeed);
		}
		else
		{
			if (sWindUnit == "KT")
			{
				iWindSpeed = (int) (iWindSpeed * 2.24/1.94);
				iGustSpeed = (int) (iGustSpeed * 2.24/1.94);
			}
			else if (sWindUnit == "KMH")
			{
				iWindSpeed = (int) (iWindSpeed / 1.61);
				iGustSpeed = (int) (iGustSpeed / 1.61);
			}
			else if (sWindUnit == "MPS")
			{
				iWindSpeed = (int) (iWindSpeed * 2.24);
				iGustSpeed = (int) (iGustSpeed * 2.24);
			}
			d->windMPH = iWindSpeed;
			sWindUnit = i18n(" MPH");
			d->qsWindSpeed.setNum(iWindSpeed);
		}
		d->qsWindSpeed += sWindUnit;

		if (iGustSpeed > 0)
			d->qsCurrentList << i18n("Wind gusts up to %1 %2")
				.arg(iGustSpeed).arg(sWindUnit);

		if ((WindRegExp.cap(1) != "VRB") && (iWindSpeed > 0))
		{
			d->qsWindDirection = parseWindDirection(direction);
		}
		return true;
	}
	return false;
}

void WeatherLib::calcCurrentIcon(Data *d){
	// Default to overcast clouds
	if ( d->clouds == -1 )
		d->clouds = 64;

	if (d->theWeather.isEmpty())
	{
		if (d->clouds == 0)
			d->theWeather = iconName("sunny", d);
		else if (d->clouds > 0 && d->clouds <= 2)
			d->theWeather = iconName("cloudy1", d);
		else if ( d->clouds > 2 && d->clouds <= 4)
			d->theWeather = iconName("cloudy2", d);
		else if ( d->clouds > 4 && d->clouds <= 8)
			d->theWeather = iconName("cloudy3", d);
		else if ( d->clouds > 8 && d->clouds < 63)
			d->theWeather = iconName( "cloudy4", d );
		else
			d->theWeather = "cloudy5";
	}
	else if (d->theWeather == "tstorm")
	{
		if ( d->heavy )
			d->clouds = 30;

		if (d->clouds >= 0 && d->clouds <= 10)
			d->theWeather = iconName("tstorm1", d);
		else if ( d->clouds > 10 && d->clouds <= 20)
			d->theWeather = iconName("tstorm2", d);
		else
			d->theWeather = "tstorm3";
	}
	else if (d->theWeather == "shower")
	{
		if ( d->heavy )
			d->clouds = 30;

		if (d->clouds >= 0 && d->clouds <= 10)
			d->theWeather = iconName("shower1", d);
		else if ( d->clouds > 10 && d->clouds <= 20)
			d->theWeather = iconName("shower2", d);
		else
			d->theWeather = "shower3";
	}
	else if (d->theWeather == "snow")
	{
		if ( d->heavy )
			d->clouds = 30;

		if (d->clouds >= 0 && d->clouds <= 8)
			d->theWeather = iconName("snow1", d);
		else if ( d->clouds > 8 && d->clouds <= 16)
			d->theWeather = iconName("snow2", d);
		else if (d->clouds > 16 && d->clouds <= 24)
			d->theWeather = iconName("snow3", d);
		else
			d->theWeather = "snow5";
	}
	else if (isNight(d->reportLocation) && d->theWeather == "mist")
		d->theWeather = "mist_night";
	else if (isNight(d->reportLocation) && d->theWeather == "fog")
                d->theWeather = "fog_night";
	else if ( d->theWeather == "mist" || d->theWeather == "fog" )
	{
		if ( d->clouds >= 63 )
			d->theWeather = "cloudy5";
	}

	kdDebug(12006) << "Clouds: " << d->clouds << ", Icon: "
		<< d->theWeather << endl;
}

void WeatherLib::calcWindChill(Data *d){
	float windChill = 35.74 + (0.6215*d->tempF)
		- (35.75*::pow(d->windMPH, 0.16))
		+ (0.4275*d->tempF*::pow(d->windMPH, 0.16));

	kdDebug(12006) << "Wind Chill: " << windChill << endl;

	if (windChill < 48)
	{
		if (KGlobal::locale()->measureSystem() == KLocale::Metric)
		{
			d->qsWindChill.setNum((windChill-32)*5/9, 'f', 1);
			d->qsWindChill += i18n("°C");
		}
		else
		{
			d->qsWindChill.setNum(windChill, 'f', 1);
			d->qsWindChill += i18n("°F");
		}
	}
}

void WeatherLib::processData(const QString &metar, Data *d){

	d->clear();
	kdDebug(12006) << "Processing data: " << metar << endl;

	// Split at whitespace into tokens
	QStringList dataList = QStringList::split(QRegExp("\\s+"), metar);
	bool found = false;

	for (QStringList::ConstIterator it = dataList.begin();
			it != dataList.end(); ++it)
	{
		// Abort if ReMarK code
		if (*it == "RMK")
			break;

		// Don't try to parse the ICAO location code
		if ((!found) && (*it == d->reportLocation.upper().stripWhiteSpace()))
		{
			found = true;
			continue;
		}

		kdDebug(12006) << "Processing Token: " << *it << endl;
		if (parseDate(*it, d))
			continue;
		if (parseTime(*it, d))
			continue;
		if (parseWindSpeed(*it, d))
			continue;
		if (parseVisibility(*it, d))
			continue;
		if (parseTemperature(*it, d))
			continue;
		if (parsePressure(*it, d))
			continue;
		if (parseCover(*it, d))
			continue;
		if (parseCurrent(*it, d))
			continue;
	}

	calcWindChill(d);
	calcCurrentIcon(d);
}

void WeatherLib::slotCopyDone(KIO::Job* job)
{
	kdDebug(12006) << "Copy done..." << endl;
	if( job->error())
	{
		kdDebug(12006) << "Error code: " << job->error() << endl;
		//job->showErrorDialog(0L);
		if(job->error() == KIO::ERR_COULD_NOT_CONNECT ||
			job->error() == KIO::ERR_UNKNOWN_HOST)
		{
			hostDown= true;
			// should not bother after this point
			KPassivePopup::message( i18n("KWeather Error!"),
				i18n("The network is unavailable for use."),  0L,"error" );
		}
	}
	// Find the job
	QDictIterator<Data> it( data );
	Data *d = 0L;
	for( ; it.current(); ++it )
	{
		kdDebug(12006) << "Searching for job..." << endl;
		if(it.current()->job == job)
		{
			d = it.current();
			d->downloading = false;
			if( !job->error() )
			{
				kdDebug( 12006) << "Reading: " << d->target->name() << endl;
				QFile file( d->target->name() );
				file.open( IO_ReadOnly );
				QTextStream *t = new QTextStream( &file );
				//QTextStream *t = d->target->textStream();
				if( t )
				{
					QString s = QString::null;
					while ( !t->eof() )
					{
						s += " " + t->readLine();
					}

					if ( !s.isEmpty() )
					{
						kdDebug( 12006 ) << "Parse: " << s << endl;
						processData(s, d);
						d->age = QDateTime::currentDateTime().addSecs(1800);
						emit fileUpdate(d->reportLocation);
						d->updated = true;
					}
					else
					{
						// File error
						kdDebug( 12006 ) << "File empty error..." << endl;
						KPassivePopup::message( i18n("KWeather Error!"),
						i18n("The temp file %1 was empty.").arg(d->target->name()),  0L,"error" );
						d->updated = false;
					}
				}
				else
				{
					// File error
					kdDebug( 12006 ) << "File read error..." << endl;
					KPassivePopup::message( i18n("KWeather Error!"),
				i18n("Could not read the temp file %1.").arg(d->target->name()),  0L,"error" );
					d->updated = false;
				}
				delete d->target;
				d->target = 0L;
				d->job = 0L;

			}
			else if( job->error()  == KIO::ERR_DOES_NOT_EXIST)
			{
				data.remove(d->reportLocation);
				kdDebug( 12006 ) << "Bad station data so i am going to remove it" << endl;
				KPassivePopup::message( i18n("KWeather Error!"),
				i18n("The requested station does not exist."),  0L,"error" );
			}
			else if(job->error() == KIO::ERR_COULD_NOT_CONNECT ||
				job->error() == KIO::ERR_UNKNOWN_HOST)
			{
				kdDebug( 12006 ) << "Offline now..." << endl;
				d->clear();
				d->theWeather = "dunno";
				d->qsCurrentList.append(i18n("The network is currently offline..."));
				d->qsCurrentList.append(i18n("Please update later."));
				emit fileUpdate(d->reportLocation);
			}
			else
			{
				kdDebug( 12006 ) << "Duh?..." << endl;
			}

		}
	}
}

void WeatherLib::getData(Data *d)
{
	if(!d->downloading && !hostDown)
	{
		d->downloading = true;
		d->updated = false;
		QString u = "http://weather.noaa.gov/pub/data/observations/metar/stations/";
		u += d->reportLocation.upper().stripWhiteSpace();
		u += ".TXT";

		d->target = new KTempFile(QString::null, "-weather");
		d->target->setAutoDelete(true);
		d->target->file();

		KURL url(u);
		KURL local(d->target->name());

		d->job = KIO::file_copy( url, local, -1, true, false, false);
		d->job->addMetaData("cache", "reload"); // Make sure to get fresh info
		connect( d->job, SIGNAL( result( KIO::Job *)),
			SLOT(slotCopyDone(KIO::Job *)));
		kdDebug( 12006 ) << "Copying " << url.prettyURL() << " to "
			<< local.prettyURL() << endl;
		emit fileUpdating(d->reportLocation);
	}
}

WeatherLib::Data* WeatherLib::findData(const QString &stationID)
{
	Data *d = data[stationID];
	if (!d)
	{
		d = new Data();
		d->reportLocation = stationID;
		d->theWeather = "dunno";
		d->qsCurrentList.append( i18n( "Retrieving weather data..." ) );
		data.insert(stationID, d);
		getData(d);
	}

	return d;
}

QString WeatherLib::temperature(const QString &stationID){
	Data *d = findData(stationID);
	return d->qsTemperature;
}

QString WeatherLib::pressure(const QString &stationID){
	Data *d = findData(stationID);
	return d->qsPressure;
}

QString WeatherLib::wind(const QString &stationID){
	Data *d = findData(stationID);
	return (d->qsWindSpeed + " " + d->qsWindDirection);
}

/**  */
QString WeatherLib::dewPoint(const QString &stationID){
	Data *d = findData(stationID);
	return d->qsDewPoint;
}

QString WeatherLib::relHumidity(const QString &stationID){
	Data *d = findData(stationID);
	return d->qsRelHumidity;
}

QString WeatherLib::heatIndex(const QString &stationID){
	Data *d = findData(stationID);
	return d->qsHeatIndex;
}

QString WeatherLib::windChill(const QString &stationID){
	Data *d = findData(stationID);
	return d->qsWindChill;
}

QString WeatherLib::iconName(const QString &stationID){
	Data *d = findData(stationID);
	return d->theWeather;
}

QString WeatherLib::date(const QString &stationID){
	Data *d = findData(stationID);

	QDateTime gmtDateTime(d->qsDate, d->qsTime);
	QDateTime localDateTime = gmtDateTime.addSecs(KRFCDate::localUTCOffset() * 60);
	return KGlobal::locale()->formatDateTime(localDateTime, false, false);
}

/** Returns the current cover */
QStringList WeatherLib::cover(const QString &stationID){
	Data *d = findData(stationID);
	return d->qsCoverList;
}

/** return the visibility */
QString WeatherLib::visibility(const QString &stationID){
	Data *d = findData(stationID);
	return d->qsVisibility;
}

/** return the weather text */
QStringList WeatherLib::weather(const QString &stationID){
	Data *d = findData(stationID);
	return d->qsCurrentList;
}

void WeatherLib::update(const QString &stationID)
{
	// refresh hostdown just to see
	hostDown = false;
	// Only grab new data if its more than 50 minutes old
	Data *d = findData(stationID);

	QDateTime timeout = QDateTime::currentDateTime();

	kdDebug (12006) << "Current Time: " << KGlobal::locale()->formatDateTime(timeout, false, false) <<
			" Update at: " << KGlobal::locale()->formatDateTime(d->age, false, false) << endl;
	if( timeout >= d->age || !d->updated)
		getData(d);
	else
		emit fileUpdate(d->reportLocation);
}

QStringList WeatherLib::stations()
{
	QStringList l;
	QDictIterator<Data> it( data );
	for( ; it.current(); ++it )
		l += it.currentKey();
	return l;
}

void WeatherLib::forceUpdate(const QString &stationID)
{
	Data *d = findData(stationID);

	getData( d );
}

void WeatherLib::remove(const QString &stationID)
{
	data.remove(stationID);
  emit stationRemoved(stationID);
}

bool WeatherLib::isNight(const QString &stationID) const
{
	Sun theSun;
	QString latitude  = m_StationDb->stationLatitudeFromID(stationID);
	QString longitude = m_StationDb->stationLongitudeFromID(stationID);

	QTime currently = QTime::currentTime();

	QTime rise_time = theSun.computeRiseTime(latitude, longitude);
	QTime set_time  = theSun.computeSetTime(latitude, longitude);

	//kdDebug (12006) << "current, rise, set: " << currently << " " << rise_time << " " << set_time << endl;
	
	return (currently < rise_time || currently > set_time);
}

QString WeatherLib::iconName( const QString &icon, Data *d ) const
{
	QString _iconName = icon;

	if ( isNight( d->reportLocation ) )
		_iconName += "_night";

    return _iconName;
}

