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

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif // HAVE_CONFIG_H

#include <tqfile.h>
#include <tqdatetime.h>
#include <tqtextstream.h>
#include <tdeglobal.h>
#include <tdelocale.h>
#include <kdebug.h>
#include <math.h>
#include <krfcdate.h>
#include <tdeio/job.h>
#include <kurl.h>
#include <knotifyclient.h>
#include <tdetempfile.h>
#include <unistd.h>
#include <tdeapplication.h>
#include <kpassivepopup.h>
#include <tdeconfig.h>

#include "metar_parser.h"
#include "stationdatabase.h"
#include "sun.h"
#include "weatherlib.h"
#include "weather_icon.h"

#include "weatherlib.moc"


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

		void clear();

		/** The current weather state outside */
		struct WeatherInfo wi;
		TQDateTime age;
		KTempFile *target;
		bool downloading;
		bool updated;
		TDEIO::Job *job;
};

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

void WeatherLib::Data::clear()
{
	age = TQDateTime::currentDateTime();
	downloading = false;
	updated = false;
	job = 0;

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

WeatherLib::WeatherLib(StationDatabase *stationDB, TQObject *parent, const char *name)
	: TQObject (parent, name)
{
	TDEGlobal::locale()->insertCatalogue("kweather");
	
	m_StationDb    = stationDB;
	
	data.setAutoDelete( true );

	// Initialize correct icon setting
	TDEConfig *conf = new TDEConfig("weather_panelappletrc");
	conf->setGroup("General Options");
	WeatherIconPrivate::instance()->useIconTheme(conf->readBoolEntry("use_icon_theme", true));
	delete conf;
}

WeatherLib::~WeatherLib()
{
}

void WeatherLib::slotCopyDone(TDEIO::Job* job)
{
	kdDebug(12006) << "Copy done..." << endl;
	if( job->error())
	{
		kdDebug(12006) << "Error code: " << job->error() << endl;
		//job->showErrorDialog(0L);
		if ((job->error() == TDEIO::ERR_COULD_NOT_CONNECT || job->error() == TDEIO::ERR_UNKNOWN_HOST)
		    && !hostDown)
		{
			hostDown= true;
      // no need to show a passive popup here, as below all stations will show "dunno" icon
		}
	}
	// Find the job
	TQDictIterator<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;
				TQFile file( d->target->name() );
				file.open( IO_ReadOnly );
				TQTextStream *t = new TQTextStream( &file );
				//TQTextStream *t = d->target->textStream();
				if( t )
				{
					TQString s = TQString();
					while ( !t->eof() )
					{
						s += " " + t->readLine();
					}

					if ( !s.isEmpty() )
					{
						kdDebug( 12006 ) << "Parse: " << s << endl;
						MetarParser parser(m_StationDb, TDEGlobal::locale()->measureSystem());
						d->wi = parser.processData(d->wi.reportLocation, s);
						d->age = TQDateTime::currentDateTime().addSecs(1800);
						emit fileUpdate(d->wi.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()  == TDEIO::ERR_DOES_NOT_EXIST)
			{
				data.remove(d->wi.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() == TDEIO::ERR_COULD_NOT_CONNECT ||
				job->error() == TDEIO::ERR_UNKNOWN_HOST)
			{
				kdDebug( 12006 ) << "Offline now..." << endl;
				d->clear();
				d->wi.theWeather = "dunno";

				TQString offlineStr = i18n("The network is currently offline...");
				if (!d->wi.qsCurrentList.contains(offlineStr)) {
					d->wi.qsCurrentList.append(offlineStr);
					d->wi.qsCurrentList.append(i18n("Please update later."));
				}
				emit fileUpdate(d->wi.reportLocation);
			}
			else
			{
				kdDebug( 12006 ) << "Duh?..." << endl;
			}

		}
	}
}

void WeatherLib::getData(Data *d, bool force /* try even if host was down last time*/)
{
	if(!d->downloading && (force || !hostDown) )
	{
		d->downloading = true;
		d->updated = false;
		TQString u = "http://tgftp.nws.noaa.gov/data/observations/metar/stations/";
		u += d->wi.reportLocation.upper().stripWhiteSpace();
		u += ".TXT";

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

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

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

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

	return d;
}

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

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

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

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

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

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

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

TQString WeatherLib::iconName(const TQString &stationID, uint iconSize) {
	TQString result = TQString::null;
	if (!stationID.isEmpty()) {
		WeatherIcon *wi = weatherIcon(stationID);
		result = wi->name(iconSize);
		delete wi;
	}

	if (result.isEmpty())
		result = WeatherIcon::unknown(iconSize).name;

	return result;
}

TQString WeatherLib::iconName(const TQString &stationID) {
	return iconName(stationID, IconSize(TDEIcon::Panel));
}

TQString WeatherLib::iconPath(const TQString &stationID, uint iconSize) {
	TQString result = TQString::null;
	if (!stationID.isEmpty()) {
		WeatherIcon *wi = weatherIcon(stationID);
		result = wi->path(iconSize);
		delete wi;
	}

	if (result.isEmpty())
		result = WeatherIcon::unknown(iconSize).path;
	
	return result;
}

/** Returns a WeatherIcon object for the current weather conditions */
WeatherIcon* WeatherLib::weatherIcon(const TQString &stationID) {
	Data *d = findData(stationID);
	if (d->wi.theWeather == "dunno")
	{
		return new WeatherIcon();
	}

	int condition = d->wi.wiCondition;
	int strength  = d->wi.wiStrength;
	bool night    = d->wi.wiNight;

	WeatherIcon* wi;
	if (d->wi.wiStrength != 0) // Ranged condition
		wi = new WeatherIcon(condition, night, strength);

	else // Simple condition
		wi = new WeatherIcon(condition, night);

	return wi;
}

TQString WeatherLib::date(const TQString &stationID){
	Data *d = findData(stationID);
	if (d->wi.qsDate.isValid()) {
		TQDateTime gmtDateTime(d->wi.qsDate, d->wi.qsTime);
		TQDateTime localDateTime = gmtDateTime.addSecs(KRFCDate::localUTCOffset() * 60);
		return TDEGlobal::locale()->formatDateTime(localDateTime, false, false);
	}
	return TQString::null;
}

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

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

/** return the weather text */
TQStringList WeatherLib::weather(const TQString &stationID){
	Data *d = findData(stationID);
	return d->wi.qsCurrentList;
}

bool WeatherLib::stationNeedsMaintenance(const TQString &stationID)
{
	Data *d = findData(stationID);
	return d->wi.stationNeedsMaintenance;
}

bool WeatherLib::weatherDataAvailable(const TQString &stationID)
{
	Data *d = findData(stationID);
	return !(d->wi.theWeather == "dunno");
}

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

	TQDateTime timeout = TQDateTime::currentDateTime();

	kdDebug (12006) << "Current Time: " << TDEGlobal::locale()->formatDateTime(timeout, false, false) <<
			" Update at: " << TDEGlobal::locale()->formatDateTime(d->age, false, false) << endl;
	if( timeout >= d->age || !d->updated)
		getData(d, true /* try even if host was down last time */);
	else
		emit fileUpdate(d->wi.reportLocation);
}

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

void WeatherLib::forceUpdate(const TQString &stationID)
{
	hostDown = false; // we want to show error message if host is still down
	Data *d = findData(stationID);
	getData( d );
}

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