/***************************************************************************
			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 "weatherlib.h"

#include "weatherlib.moc"

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

		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;
};

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

WeatherLib::WeatherLib(QObject *parent, const char *name)
	: QObject (parent, name)
{
	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})$");
}

WeatherLib::~WeatherLib()
{
}

/** Parse the current cover type */
bool WeatherLib::parseCover(QString s, Data *d){
	if (CoverRegExp.search(s) > -1)
	{
		kdDebug(12004) << "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(12004) << "*** Clouds: " << d->clouds << endl;
		d->qsCoverList << skycondition;

		return true;
	}
	return false;
}

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

		kdDebug(12004) << "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 = "shower1";
		}
		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(QString s, Data *d){
	if (TempRegExp.search(s) > -1)
	{
		QString unit;
		kdDebug(12004) << "Temp: " << TempRegExp.capturedTexts().join("-")
			<< endl;

		float fTemp = TempRegExp.cap(2).toFloat();
		if (TempRegExp.cap(1) == "M")
			fTemp *= -1;
		float fDew = TempRegExp.cap(4).toFloat();
		if (TempRegExp.cap(3) == "M")
			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(QString s, Data *d){
	if (DateRegExp.search(s) > -1)
	{
		kdDebug(12004) << "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(QString s, Data *d){
	if (TimeRegExp.search(s) > -1)
	{
		kdDebug(12004) << "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( QString s, Data *d){
	if (VisRegExp.search(s) > -1)
	{
		float fVisibility = VisRegExp.cap(1).toFloat();

		kdDebug(12004) << "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( QString s, Data *d){
	if (PressRegExp.search(s) > -1)
	{
		QString type = PressRegExp.cap(1);
		float fPressure = PressRegExp.cap(2).toFloat();

		kdDebug(12004) << "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;
}

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

		kdDebug(12004) << "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))
		{
			if ((iDirection >=0 && iDirection <= 11)
					|| ( iDirection >= 349 && iDirection <= 360))
				d->qsWindDirection = i18n("N");
			else if (iDirection >= 12 && iDirection <= 32)
				d->qsWindDirection = i18n("NNE");
			else if (iDirection >= 33 && iDirection <= 56)
				d->qsWindDirection = i18n("NE");
			else if (iDirection >= 57 && iDirection <= 78)
				d->qsWindDirection = i18n("ENE");
			else if (iDirection >= 79 && iDirection <= 101)
				d->qsWindDirection = i18n("E");
			else if (iDirection >= 102 && iDirection <= 123)
				d->qsWindDirection = i18n("ESE");
			else if (iDirection >= 124 && iDirection <= 146)
				d->qsWindDirection = i18n("SE");
			else if (iDirection >= 147 && iDirection <= 168)
				d->qsWindDirection = i18n("SSE");
			else if (iDirection >= 169 && iDirection <= 191)
				d->qsWindDirection = i18n("S");
			else if (iDirection >= 192 && iDirection <= 213)
				d->qsWindDirection = i18n("SSW");
			else if (iDirection >= 214 && iDirection <= 236)
				d->qsWindDirection = i18n("SW");
			else if (iDirection >= 237 && iDirection <= 258)
				d->qsWindDirection = i18n("WSW");
			else if (iDirection >= 259 && iDirection <= 281)
				d->qsWindDirection = i18n("W");
			else if (iDirection >= 282 && iDirection <= 303)
				d->qsWindDirection = i18n("WNW");
			else if (iDirection >= 304 && iDirection <= 326)
				d->qsWindDirection = i18n("NW");
			else if (iDirection >= 327 && iDirection <= 348)
				d->qsWindDirection = i18n("NNW");
		}
		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 = "sunny";
		else if (d->clouds > 0 && d->clouds <= 2)
			d->theWeather = "cloudy1";
		else if ( d->clouds > 2 && d->clouds <= 4)
			d->theWeather = "cloudy2";
		else if ( d->clouds > 4 && d->clouds <= 8)
			d->theWeather = "cloudy3";
		else if ( d->clouds > 8 && d->clouds < 63)
			d->theWeather = "cloudy4";
		else
			d->theWeather = "cloudy5";
	}
	else if (d->theWeather == "tstorm")
	{
		if ( d->heavy )
			d->clouds = 30;

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

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

		if (d->clouds >= 0 && d->clouds <= 8)
			d->theWeather = "snow1";
		else if ( d->clouds > 8 && d->clouds <= 16)
			d->theWeather = "snow2";
		else if (d->clouds > 16 && d->clouds <= 24)
			d->theWeather = "snow3";
		else
			d->theWeather = "snow5";
	}

	kdDebug(12004) << "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(12004) << "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(QString metar, Data *d){
	d->clear();

	kdDebug(12004) << "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(12004) << "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(12004) << "Copy done..." << endl;
	if( job->error())
	{
		kdDebug(12004) << "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
			QPixmap pix();
			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(12004) << "Searching for job..." << endl;
		if(it.current()->job == job)
		{

			d = it.current();
			d->downloading = false;
			if( !job->error() )
			{
				QFile f(d->target->name());
				if ( f.open(IO_ReadOnly) )
				{
					QTextStream t(&f);
					QString s;
					while ( !t.eof() )
					{
						s += " " + t.readLine();
						kdDebug( 12004 ) << s << endl;
					}
					processData(s, d);
					d->age = QDateTime::currentDateTime().addSecs(3000);
					emit  fileUpdate(QString(d->reportLocation));
					d->updated = true;
				}
				else
				{
					// File error
					kdDebug( 12004 ) << "File read error..." << endl;
					KPassivePopup::message( i18n("KWeather Error!"),
				i18n("Could not read the temp file."),  0L,"error" );
					d->updated = false;
				}
				f.close();
				delete d->target;

			}
			else if( job->error()  == KIO::ERR_DOES_NOT_EXIST)
			{
				data.remove(d->reportLocation);
				kdDebug( 12004 ) << "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( 12004 ) << "Offline now..." << endl;
				d->updated = false;
				d->theWeather = "dunno";
				d->clouds = 0;
				d->windMPH = 0;
				d->tempF = 0;
				d->qsCoverList.clear();
				d->qsCurrentList.clear();
				d->qsCurrentList.append(i18n("The network is currently offline..."));
				d->qsCurrentList.append(i18n("Please update later."));
				d->qsDate = QDate::currentDate();
				d->qsPressure = "XX";
				d->qsTemperature = "XX";
				d->qsDewPoint = "XX";
				d->qsRelHumidity = "XX";
				d->qsTime = QTime::currentTime();
				d->qsVisibility = "XX";
				d->qsWindSpeed = "XX";
				d->qsWindChill = "XX";
				d->qsHeatIndex = "XX";
				d->qsWindDirection = "XX";
				emit  fileUpdate(QString(d->reportLocation));
				delete d->target;
			}
			else
			{
				kdDebug( 12004 ) << "Duh?..." << endl;
			}

		}
	}
}

void WeatherLib::getData(Data *d)
{
	if(!d->downloading && !hostDown)
	{
		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);
		KURL url(u);
		KURL local(d->target->name());

		d->job = KIO::file_copy( url, local, -1, true, false, false);
		connect( d->job, SIGNAL( result( KIO::Job *)),
			SLOT(slotCopyDone(KIO::Job *)));
		d->downloading = true;
		kdDebug( 12004 ) << "Copying " << url.prettyURL() << " to "
			<< local.prettyURL() << endl;
		emit fileUpdateing(QString(d->reportLocation));
	}
}

WeatherLib::Data* WeatherLib::findData(QString stationID)
{
	Data *d = data[stationID];
	if (!d)
	{
		d = new Data();
		d->reportLocation = stationID;
		data.insert(stationID, d);
		getData(d);
/*		while(!d->updated && !hostDown)
		{
			usleep(250);
			//kdDebug( 12004 ) << "spin..." << endl;
			kapp->processEvents();
		}
*/
	}

	return d;
}

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

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

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

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

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

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

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

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

QString WeatherLib::date(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(QString stationID){
	Data *d = findData(stationID);
	return d->qsCoverList;
}

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

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

void WeatherLib::update(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 () << "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(QString(d->reportLocation));

}

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

void WeatherLib::blockingUpdate(QString stationID)
{
	// Only grab new data if its more than 50 minutes old
	Data *d = findData(stationID);

	QDateTime timeout = QDateTime::currentDateTime();

	kdDebug () << "Current Time: " << KGlobal::locale()->formatDateTime(timeout, false, false) <<
			" Update at: " << KGlobal::locale()->formatDateTime(d->age, false, false) << endl;
	int giveup = 0;
	if( timeout >= d->age)
		getData(d);
	while(!d->updated && !hostDown && giveup < 140) // Timeout after 30 seconds...
	{
		usleep(250);
		//kdDebug( 12004 ) << "spin..." << endl;
		kapp->processEvents();
		++giveup;
	}
}

void WeatherLib::remove(QString stationID)
{
	data.remove(stationID);
}
