/*************************************************************************\
*   Copyright (C) 2009 - 2011 by Ulf Kreissig                             *
*   udev@gmx.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.,                                       *
*   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA            *
\*************************************************************************/

#define PANEL_DESKTOP_INTERFACE_NAME	QLatin1String("panel-desktop-interface")


//--- LOCAL CLASSES ---
#include "../config.h"
#include "painter/abstractpainter.h"
#include "painter/basedesktoppainter.h"
#include "painter/basepanelpainter.h"
#include "painter/desktoppainter.h"
#include "painter/extendeddesktoppainter.h"
#include "painter/panelpainter.h"
#include "countrymap.h"
#include "ionlistmodel.h"
#include "paneldesktopinterface.h"
#include "yawp.h"
#include "yawpday.h"
#include "weatherdataprocessor.h"
#include "weatherservice.h"
#include "utils.h"

#include "logger/streamlogger.h"

//--- QT4 CLASSES ---
#include <QActionGroup>
#include <QFile>
#include <QDir>
#include <QFileInfo>
#include <QPainter>
#include <QTimer>

//--- KDE4 CLASSES ---
#include <KAboutData>
#include <KAboutApplicationDialog>
#include <KActionMenu>
#include <KColorScheme>
#include <KConfigDialog>
#include <KDateTime>
#include <KIcon>
#include <KIO/DeleteJob>
#include <KMenu>
#include <KTimeZone>
#include <KSystemTimeZone>
#include <KUrl>

#include <Plasma/Containment>
#include <Plasma/Extender>
#include <Plasma/Theme>
#include <Plasma/ToolTipManager>

#if KDE_IS_VERSION(4,3,70)
	#include <KUnitConversion/Value>
#endif

#include <limits.h>
#include <math.h>


//	cp lib/plasma_yawp3.so /usr/lib/kde4/
//	cp plasma_yawp3.desktop /usr/share/kde4/services/plasma-yawp3.desktop
//
//	plasmoidviewer -c desktop -f horizontal yaWP3


YaWP::YaWP( QObject * parent, const QVariantList & args )
	: Plasma::PopupApplet(parent, args),
	  m_svg(this),
	  m_customSvg(this),
	  m_pConfigDlg(NULL),
	  m_pAppletPainter(0),
	  m_pWeatherModel(0),
	  m_iIdPendingEngineConnection(-1),
	  m_iIdTraverseLocations(-1)
#if KDE_IS_VERSION(4,7,95)
	  ,	// <--- this comma seperates the following member variable from the previous one
	  m_pPanelDesktopInterface(0)
#endif
{
	Q_INIT_RESOURCE(yawpresource);
	dStartFunct() << "============================================";
	dInfo();
	dInfo() << "yaWP" << YAWP_VERSION_STRING << "compiled at" << __DATE__ << __TIME__ << "for KDE" << KDE_VERSION_STRING;
	dInfo() << "yaWP" << YAWP_VERSION_STRING << "is running on KDE" << KDE::versionString();
	dInfo();

	//--- Set Default Configuration Values ---
	m_configData.iCityIndex = 0;
	m_configData.bUseCustomTheme = false;
	m_configData.bUseCustomThemeBackground = false;
	m_configData.bUseCustomFontColor = false;
	m_configData.bUseInteractivePanelWeatherIcons = true;
	m_configData.bDisableTextShadows = false;
	m_configData.iUpdateInterval = 45; // in minutes
	m_configData.iStartDelay = 0; // in minutes
	m_configData.iAnimationDuration = 1000;
	m_configData.sBackgroundName = QLatin1String("default");
	
	m_configData.desktopPainterType = Yawp::DesktopPainter;
	//m_configData.panelPainterType = Yawp::PanelPainter;
	m_configData.popupPainterType = Yawp::DesktopPainter;

	if( KGlobal::locale()->measureSystem() == KLocale::Metric )
	{
	#if KDE_IS_VERSION(4,3,70)
			m_configData.distanceSystem = KUnitConversion::Kilometer;
			m_configData.pressureSystem = KUnitConversion::Kilopascal;
			m_configData.temperatureSystem	= KUnitConversion::Celsius;
			m_configData.speedSystem	= KUnitConversion::KilometerPerHour;
	#else
			m_configData.distanceSystem = WeatherUtils::Kilometers;
			m_configData.pressureSystem = WeatherUtils::Kilopascals;
			m_configData.temperatureSystem	= WeatherUtils::Celsius;
		#if KDE_VERSION_MINOR >= 3
			m_configData.speedSystem = WeatherUtils::KilometersPerHour;
		#else
			m_configData.speedSystem = WeatherUtils::KilometersAnHour;
		#endif
	#endif
	}
	else
	{
	#if KDE_IS_VERSION(4,3,70)
		m_configData.distanceSystem = KUnitConversion::Mile;
		m_configData.pressureSystem = KUnitConversion::InchesOfMercury;
		m_configData.temperatureSystem = KUnitConversion::Fahrenheit;
		m_configData.speedSystem = KUnitConversion::MilePerHour;
	#else
		m_configData.distanceSystem = WeatherUtils::Miles;
		m_configData.pressureSystem = WeatherUtils::InchesHG;
		m_configData.temperatureSystem = WeatherUtils::Fahrenheit;
		#if KDE_VERSION_MINOR >= 3
			m_configData.speedSystem = WeatherUtils::MilesPerHour;
		#else
			m_configData.speedSystem = WeatherUtils::MilesAnHour;
		#endif
	#endif
	}

	m_configData.todaysWeatherPanelFormat	= Yawp::PanelTemperature | Yawp::PanelIcon;
	m_configData.forecastWeatherPanelFormat	= Yawp::PanelTemperature | Yawp::PanelIcon;
	m_configData.iPanelForecastDays = 3;

	m_configData.pageAnimation = PageAnimator::RollOutHorizontally;
	m_configData.daynamesAnimation = PageAnimator::RollOutHorizontally;
	m_configData.detailsAnimation = PageAnimator::CrossFade;
	m_configData.iconAnimation = PageAnimator::FlipVertically;

	m_configData.fontColor = QColor(Qt::white);
	m_configData.lowFontColor = QColor(Qt::gray);
	m_configData.shadowsFontColor = QColor(0,0,0,100);

	m_configData.bUseCompactPanelLayout = false;
	m_configData.bUseExtendedTooltip = true;
	m_configData.extendedTooltipOptions = Yawp::PreviewPage | Yawp::SatellitePage;

	m_configData.bTraverseLocationsPeriodically = false;
	m_configData.iTraverseLocationTimeout = 30;

	// specifies the order of the shown properties in details page
	// when a provider do not support a property, this one will be skipped.
	// Remove a property you do not want to see on details page or change
	// the order as you like.
	m_configData.vDetailsPropertyRankingList
		<< Yawp::WeatherDescription
		<< Yawp::SunriseSunset
		<< Yawp::RealfeelTemperature
		<< Yawp::Pressure
		<< Yawp::Visibility
		<< Yawp::Dewpoint
		<< Yawp::UV;

	/*** About data ***/
	m_aboutData = new KAboutData ( "plasma_yawp",
		QByteArray (), ki18n( YAWP_NAME ), YAWP_VERSION_STRING,
		ki18n( "Yet Another Weather Applet" ),
		KAboutData::License_GPL,
		ki18n( "Copyright (C) 2008-2010 Ruan Strydom\n"
		       "Copyright (C) 2008-2010 Ezequiel R. Aguerre\n"
		       "Copyright (C) 2008-2010 Pierpaolo Vittorini\n"
		       "Copyright (C) 2008-2011 Marián Kyral\n"
		       "Copyright (C) 2009-2011 Ulf Kreißig" ),
		ki18n( "This plasmoid shows for the selected place "
		       " current weather and forecast for 4 days." ),
		"http://www.kde-look.org/content/show.php?content=94106",
		"plasmafactory@jcell.co.za" );

	//--- Authors ---
	m_aboutData->addAuthor( ki18n("Ulf Kreißig"),
		ki18n("Developer"), "udev@gmx.net" );
	m_aboutData->addAuthor( ki18n("Marián Kyral" ),
		ki18n("Developer"), "mkyral@email.cz" );
	m_aboutData->addAuthor( ki18n("Ezequiel R. Aguerre"),
		ki18n("Developer"), "ezeaguerre@gmail.com" );
	m_aboutData->addAuthor( ki18n("Pierpaolo Vittorini"),
		ki18n("Developer"), "pierpaolo.vittorini@gmail.com" );
	m_aboutData->addAuthor( ki18n("Ruan Strydom"),
		ki18n("Developer"), "ruans@kr8.co.za" );

/*
      Copyright 2007 Thomas Luebking <thomas.luebking@web.de>
      Copyright 2008 Aleix Pol <aleixpol@gmail.com>
      Copyright 2007 Frerich Raabe <raabe@kde.org>
      Copyright 2007 Aaron Seigo <aseigo@kde.org>
*/

	//--- Credits ---
	m_aboutData->addCredit ( ki18n("Ulf Kreißig"),
		ki18n("Complete rewrite!" ), "udev@gmx.net" );
	m_aboutData->addCredit ( ki18n("Ruan Strydom"),
		ki18n("Idea, graphics" ), "ruans@kr8.co.za" );
	m_aboutData->addCredit( ki18n("AccuWeather.com"),
		ki18n("For the weather data"), "", "http://www.accuweather.com"  );
	m_aboutData->addCredit( ki18n("Thomas Luebking"),
		ki18n("Algorithm for animated page-changes is a slightly modified version from Bespin. (Qt4 Style)"),
		"thomas.luebking@web.de" );
	m_aboutData->addCredit( ki18n("das Gnu"),
		ki18n("German translation"), "dasgnu@gmail.com" );
	m_aboutData->addCredit( ki18n("Jesús Vidal Panalés"),
		ki18n("Spanish translation"));
	m_aboutData->addCredit( ki18n("Bribanick Dominique"),
		ki18n("French translation"), "chepioq@gmail.com" );
	m_aboutData->addCredit( ki18n("Mihail Pisanov"),
		ki18n("Russian translation"), "miha@52.ru" );
	m_aboutData->addCredit( ki18n("Richard Frič"),
		ki18n("Slovak translation"), "Richard.Fric@kdemail.net" );
	m_aboutData->addCredit( ki18n("Mihael Simonič"),
		ki18n("Slovenian translation"), "smihael@gmail.com" );
	m_aboutData->addCredit( ki18n("Maciej Bulik"),
		ki18n("Polish translation"), "Maciej.Bulik@post.pl" );
        m_aboutData->addCredit( ki18n("Hasan Kiran"),
                ki18n("Turkish translation"), "under67@hotmail.com" );
	m_aboutData->addCredit( ki18n("All people reporting bugs, send feedback and new feature requests") );

	m_aboutData->setProgramIconName("weather-clear");

	//--- Translators ---
	m_aboutData->setTranslator( ki18nc("NAME OF THE TRANSLATORS", "Your names"),
		ki18nc("EMAIL OF THE TRANSLATORS", "Your emails") );
	/*** About data end ***/
	
	resize(273, 255);	// set a fixed initial size that fits the desktop mode. We simply suppose we are always in desktop-mode.

	dEndFunct();
}

YaWP::~YaWP()
{
	dStartFunct();
	Plasma::ToolTipManager::self()->clearContent(this);

	destroyExtenderItem();
	
	if (hasFailedToLaunch())
	{
		// Do some cleanup here
	}
	else
	{
		// Save settings
		saveConfig(m_configData, *m_pWeatherModel);
	}

	if (m_pAppletPainter != 0)
		delete m_pAppletPainter;

	delete m_pWeatherModel;
	delete m_aboutData;

	dEndFunct();
}

void
YaWP::init()
{
	dStartFunct();

	PopupApplet::init();
	
	Plasma::DataEngine * pEngine = dataEngine("weather");
	m_storage.setEngine( pEngine );

	WeatherDataProcessor * pDataProcessor = new WeatherDataProcessor( &m_storage );
	m_pWeatherModel = new WeatherServiceModel( &m_storage, this, pDataProcessor );
	m_pWeatherModel->setObjectName("EngineModel");

	m_stateMachine.setServiceModel( m_pWeatherModel );
	
	// Contextual actions - thanks to Ezequiel, ported from stdin plasmoid :-)
	m_pManualUpdate = new QAction( i18n ("&Refresh"), this );
	m_pManualUpdate->setIcon ( KIcon ( "view-refresh" ) );
	addAction( "refresh", m_pManualUpdate );
	connect ( m_pManualUpdate, SIGNAL(triggered()), m_pWeatherModel, SLOT(reconnectEngine()) );

	QAction * aboutAction = new QAction(i18n("&About"), this);
	aboutAction->setIcon( KIcon("help-about") );
	addAction( "about", aboutAction );
	connect( aboutAction, SIGNAL(triggered()), this, SLOT(about()) );

	QAction * separator1 = new QAction ( this );
	separator1->setSeparator ( true );

	m_pCitySubMenu = new KActionMenu(KIcon("preferences-desktop-locale"), i18n("Show city"), this);
	m_pCitySubMenu->setEnabled( false );
	m_pGrpActionCities = new QActionGroup( this );
	m_pGrpActionCities->setExclusive( true );
	connect( m_pGrpActionCities, SIGNAL(triggered(QAction *)), this, SLOT(changeCity(QAction *)) );
	
	m_pOpenForecastUrl = new QAction( i18n("Open Forecast URL"), this);
	m_pOpenForecastUrl->setIcon( KIcon("text-html") );
	//addAction( "open_url", m_pOpenForecastUrl);
	connect( m_pOpenForecastUrl, SIGNAL(triggered()), this, SLOT(openForecastUrl()) );
	m_pOpenForecastUrl->setEnabled(false);

	QAction * separator2 = new QAction ( this );
	separator2->setSeparator ( true );

	m_actions.append( m_pManualUpdate );
	m_actions.append( aboutAction );
	m_actions.append( separator1 );
	m_actions.append( m_pCitySubMenu );
	m_actions.append( m_pOpenForecastUrl );
	m_actions.append( separator2 );

	connect( m_pWeatherModel,   SIGNAL(isBusy(bool)),       this,   SLOT(setBusy(bool)) );
	connect( m_pWeatherModel,   SIGNAL(cityUpdated(WeatherServiceModel::ServiceUpdate)),
	         this,              SLOT(slotCityUpdate(WeatherServiceModel::ServiceUpdate)) );
	connect( Plasma::Theme::defaultTheme(), SIGNAL(themeChanged()),	this,	SLOT(slotThemeChanged()) );

	setHasConfigurationInterface(true);
	setAspectRatioMode(Plasma::KeepAspectRatio);
	setPopupIcon(QIcon());
	setPassivePopup(true);
	
	m_svg.setImagePath("widgets/yawp_theme15");
	m_svg.setContainsMultipleImages( true );

	loadConfig();
	
	//--- just 4 debug ---
/*	CityWeather dummyCity1;
	dummyCity1.setProvider(QString("accuweather"));
	dummyCity1.setCity("Dresden, Germany (Saxony)");
	dummyCity1.setCountry("Germany");
	dummyCity1.setCountryCode("de");
	dummyCity1.setExtraData("EUR.DE.GM014.DRESDEN");
	dummyCity1.setTimeZone("Europe/Berlin");
	m_pWeatherModel->addCity(dummyCity1);

	CityWeather dummyCity2;
	dummyCity2.setProvider(QString("accuweather"));
	dummyCity2.setCity("Mar Del Plata, Argentina (Buenos Aires)");
	dummyCity2.setCountry("Argentina");
	dummyCity2.setCountryCode("ar");
	dummyCity2.setExtraData("SAM.AR.AR001.MAR+DEL+PLATA");
	//dummyCity2.setTimeZone("Asia/Shanghai");
	m_pWeatherModel->addCity(dummyCity2);
	//--- end of debug section ---
*/

	// Every 5 days, 15 minutes after start of applet, cache directory will be checked to remove old files.
	if (QDate::currentDate().dayOfYear() % 5 == 0)
		QTimer::singleShot(15*60*1000, this, SLOT(slotStartCacheCleanUp()) );
	
	dEndFunct();
}

void
YaWP::loadConfig()
{
	dStartFunct();
	
	// first of all we stop timer for delayed weather engine connection
	stopPendingEngineConnection();

	// stop to sliding through cities automatically
	stopTraverseLocationTimeout();
	
	// when we have some old cities, remove them otherwise we get more cities as expected
	if (m_pWeatherModel->rowCount() > 0)
	{
		// first of all we disconnect from weather engine
		m_pWeatherModel->disconnectEngine();
		m_pWeatherModel->removeRows(0, m_pWeatherModel->rowCount(), QModelIndex());
	}
	
	//--- Start loading configuration
	KConfigGroup cfg = config();

	/*** SETTINGS PAGE ***/
	m_configData.iUpdateInterval
		= cfg.readEntry( "update interval", QVariant(m_configData.iUpdateInterval) ).toInt();
	m_configData.iStartDelay
		= cfg.readEntry( "start delay", QVariant(m_configData.iStartDelay) ).toInt();
	m_configData.bTraverseLocationsPeriodically
		= cfg.readEntry( "traverse locations", m_configData.bTraverseLocationsPeriodically );
	m_configData.iTraverseLocationTimeout
		= cfg.readEntry( "traverse locations timeout", QVariant(m_configData.iTraverseLocationTimeout) ).toInt();

	m_configData.distanceSystem
		= (YAWP_DISTANCE_UNIT)cfg.readEntry( "system.distance",
			(int)m_configData.distanceSystem );
	m_configData.pressureSystem
		= (YAWP_PRESSURE_UNIT)cfg.readEntry( "system.pressure",
		 	(int)m_configData.pressureSystem );
	m_configData.temperatureSystem
		= (YAWP_TEMPERATURE_UNIT)cfg.readEntry( "system.temperature",
			(int)m_configData.temperatureSystem );
	m_configData.speedSystem
		= (YAWP_SPEED_UNIT)cfg.readEntry( "system.speed",
			(int)m_configData.speedSystem );

	m_configData.daynamesAnimation
		= (PageAnimator::Transition)cfg.readEntry( "animation.daysnames",
			(int)m_configData.daynamesAnimation );
	m_configData.detailsAnimation
		= (PageAnimator::Transition)cfg.readEntry( "animation.details",
			(int)m_configData.detailsAnimation );
	m_configData.pageAnimation
		= (PageAnimator::Transition)cfg.readEntry( "animation.page",
			(int)m_configData.pageAnimation );
	m_configData.iconAnimation
		= (PageAnimator::Transition)cfg.readEntry( "animation.icon",
			(int)m_configData.iconAnimation );
	m_configData.iAnimationDuration
		= cfg.readEntry( "animation.duration",
			(int)m_configData.iAnimationDuration );
		
	/*** DESKTOP PAGE ***/
	m_configData.desktopPainterType
		= (Yawp::PainterType)cfg.readEntry( "desktop.paintertype",
			(int)m_configData.desktopPainterType);

	/*** PANEL PAGE - LAYOUT ***/
	m_configData.todaysWeatherPanelFormat
		= (Yawp::PanelDayFormat)cfg.readEntry( "panel.today.format",
			(int)m_configData.todaysWeatherPanelFormat );
	m_configData.forecastWeatherPanelFormat
		= (Yawp::PanelDayFormat)cfg.readEntry( "panel.forecast.format",
			(int)m_configData.forecastWeatherPanelFormat );
	m_configData.iPanelForecastDays
		= cfg.readEntry( "panel.forecast.days",	m_configData.iPanelForecastDays );
	m_configData.bUseCompactPanelLayout
		= cfg.readEntry( "panel.layout.compact", m_configData.bUseCompactPanelLayout );
		
	/*** PANEL PAGE - POPUP WINDOW ***/
	m_configData.bUseInteractivePanelWeatherIcons
		= cfg.readEntry( "panel.weathericon.interactive", m_configData.bUseInteractivePanelWeatherIcons );
	m_configData.popupPainterType
		= (Yawp::PainterType)cfg.readEntry( "panel.popupwindow.paintertype",
			(int)m_configData.popupPainterType);

	/*** PANEL PAGE - TOOLTIP ***/
	m_configData.bUseExtendedTooltip
		= cfg.readEntry( "panel.tooltip.extended.enabled", m_configData.bUseExtendedTooltip );
	m_configData.extendedTooltipOptions
		= (Yawp::ExtendedTooltipOptions)cfg.readEntry( "panel.tooltip.extended.format",
			(int)m_configData.extendedTooltipOptions );

	/*** THEME PAGE ***/
	m_configData.sBackgroundName
		= cfg.readEntry( "theme", m_configData.sBackgroundName );
	m_configData.sCustomThemeFile
		= cfg.readEntry( "custom.theme.file", m_configData.sCustomThemeFile );
	m_configData.bUseCustomTheme
		= cfg.readEntry( "custom.theme.enabled", m_configData.bUseCustomTheme );
	m_configData.bUseCustomThemeBackground
		= cfg.readEntry( "custom.theme.background.enabled",
			QVariant( m_configData.bUseCustomThemeBackground ) ).toBool();

	m_configData.fontColor
		= cfg.readEntry( "custom.fontcolor.normal", m_configData.fontColor );
	m_configData.lowFontColor
		= cfg.readEntry( "custom.fontcolor.lowtemp", m_configData.lowFontColor );
	m_configData.shadowsFontColor
		= cfg.readEntry( "custom.fontcolor.shadows", m_configData.shadowsFontColor );
	m_configData.forecastTextColor
		= cfg.readEntry( "custom.fontcolor.forecasttext", m_configData.forecastTextColor );
	m_configData.bUseCustomFontColor
		= cfg.readEntry( "custom.fontcolor.usage", m_configData.bUseCustomFontColor );
	m_configData.bDisableTextShadows
		= cfg.readEntry( "text.shadows", m_configData.bDisableTextShadows );
	if( !m_configData.bUseCustomFontColor )
		setDefaultFontColors();

	setupWeatherServiceModel();

	/*** LOCATION PAGE ***/
	if( cfg.hasGroup("locations") )
	{
		KConfigGroup	cityCfg = cfg.group("locations");
		QString		sKey;
		QStringList	lst;
		int		iCityIndex = 0;
		CityWeather	cityInfo;

		while( true )
		{
			sKey = QString("city%1").arg(iCityIndex+1, 2, 10, QChar('0'));
			if( !cityCfg.hasKey( sKey ) )
				break;
			lst = cityCfg.readEntry(sKey, QStringList());
			if( lst.count() < 5 )
				break;

			cityInfo.clear();
			cityInfo.setProvider(    QUrl::fromPercentEncoding(lst.at(0).toUtf8()) );
			cityInfo.setCity(        QUrl::fromPercentEncoding(lst.at(1).toUtf8()) );
			cityInfo.setCountry(     QUrl::fromPercentEncoding(lst.at(2).toUtf8()) );
			cityInfo.setCountryCode( QUrl::fromPercentEncoding(lst.at(3).toUtf8()) );
			cityInfo.setExtraData(   QUrl::fromPercentEncoding(lst.at(4).toUtf8()) );

			if( lst.count() > 5 )
				cityInfo.setTimeZone( QUrl::fromPercentEncoding(lst.at(5).toUtf8()) );
			if( !cityInfo.timeZone().isValid() )
			{
				QStringList vTimeZones( Utils::GetTimeZones( cityInfo, &m_storage ) );
				if( vTimeZones.count() == 1 )
					cityInfo.setTimeZone( vTimeZones.at(0) );
			}
			if( cityInfo.countryCode().isEmpty() && cityInfo.timeZone().isValid() )
				cityInfo.setCountryCode( cityInfo.timeZone().countryCode() );

			dDebug() << cityInfo.provider() << cityInfo.city() << cityInfo.country() << cityInfo.countryCode()
			         << cityInfo.extraData() << cityInfo.timeZone().name() << cityInfo.timeZone().countryCode();

			m_pWeatherModel->addCity( cityInfo );

			iCityIndex += 1;
		}
		m_configData.iCityIndex = cityCfg.readEntry("selected", 0);	// currently shown city in the applet
	}
	else
		m_configData.iCityIndex = 0;
	
	// Update yawp visual to new loaded data
	loadCustomTheme();
	initAppletPainter();

	updateCitySubMenu();
	setCityIndex( m_configData.iCityIndex );

	const bool bHasCities = (m_pWeatherModel->rowCount() > 0);

	if (bHasCities)
	{
		// start requesting weather information from data engine
		// the connection with the data engine will be delayed because some
		// systems need a while to get ready...
		m_iIdPendingEngineConnection = startTimer( m_configData.iStartDelay*60*1000 );
	}

	// When no city has been configured inform user
	// that this applet needs to be configured
	setConfigurationRequired( !bHasCities );

	m_pManualUpdate->setEnabled( bHasCities );
	m_pCitySubMenu->setEnabled( bHasCities);

	startTraverseLocationTimeout();

	dEndFunct();
}

void
YaWP::saveConfig( const Yawp::ConfigData & configData, const WeatherServiceModel & weatherModel )
{
	dStartFunct();
	KConfigGroup cfg = config();

	/*** SETTINGS PAGE ***/
	cfg.writeEntry( "update interval",		(int)configData.iUpdateInterval );
	cfg.writeEntry( "start delay",			(int)configData.iStartDelay );
	cfg.writeEntry( "traverse locations",		configData.bTraverseLocationsPeriodically );
	cfg.writeEntry( "traverse locations timeout",	configData.iTraverseLocationTimeout );

	cfg.writeEntry( "system.distance",		(int)configData.distanceSystem );
	cfg.writeEntry( "system.pressure",		(int)configData.pressureSystem );
	cfg.writeEntry( "system.temperature",		(int)configData.temperatureSystem );
	cfg.writeEntry( "system.speed",			(int)configData.speedSystem );

	cfg.writeEntry( "animation.daysnames",		(int)configData.daynamesAnimation );
	cfg.writeEntry( "animation.details",		(int)configData.detailsAnimation );
	cfg.writeEntry( "animation.page",		(int)configData.pageAnimation );
	cfg.writeEntry( "animation.icon",		(int)configData.iconAnimation );
	cfg.writeEntry( "animation.duration",		(int)configData.iAnimationDuration );
	
	/*** DESKTOP PAGE ***/
	cfg.writeEntry( "desktop.paintertype",		(int)configData.desktopPainterType );

	/*** PANEL PAGE ***/
	cfg.writeEntry( "panel.today.format",		(int)configData.todaysWeatherPanelFormat );
	cfg.writeEntry( "panel.forecast.format",	(int)configData.forecastWeatherPanelFormat );
	cfg.writeEntry( "panel.forecast.days",		configData.iPanelForecastDays );
	cfg.writeEntry( "panel.layout.compact", 	configData.bUseCompactPanelLayout );
	cfg.writeEntry( "panel.popupwindow.paintertype", (int)configData.popupPainterType);
	
	cfg.writeEntry( "panel.tooltip.extended.enabled",	configData.bUseExtendedTooltip );
	cfg.writeEntry( "panel.tooltip.extended.format",	(int)configData.extendedTooltipOptions );
	cfg.writeEntry( "panel.weathericon.interactive",	configData.bUseInteractivePanelWeatherIcons );

	/***  THEME PAGE ***/
	cfg.writeEntry( "theme",				configData.sBackgroundName );
	cfg.writeEntry( "custom.theme.file",			configData.sCustomThemeFile );
	cfg.writeEntry( "custom.theme.enabled",			configData.bUseCustomTheme );
	cfg.writeEntry( "custom.theme.background.enabled",	configData.bUseCustomThemeBackground );

	cfg.writeEntry( "custom.fontcolor.normal",	configData.fontColor );
	cfg.writeEntry( "custom.fontcolor.lowtemp",	configData.lowFontColor );
	cfg.writeEntry( "custom.fontcolor.shadows",	configData.shadowsFontColor );
	cfg.writeEntry( "custom.fontcolor.forecasttext",configData.forecastTextColor );
	cfg.writeEntry( "custom.fontcolor.usage",	configData.bUseCustomFontColor );

	cfg.writeEntry( "text.shadows",	configData.bDisableTextShadows );

	/*** LOCATION PAGE ***/
	if( cfg.hasGroup("locations") )
		cfg.group("locations").deleteGroup();	// delete all old entries in this group
		
	if( weatherModel.rowCount() > 0 )
	{
		KConfigGroup cityCfg = cfg.group("locations");
		for( int iCityIndex = 0; iCityIndex < weatherModel.rowCount(); ++iCityIndex )
		{
			QStringList lst;
			const CityWeather * pCity = weatherModel.getCityInfo( iCityIndex );
			lst << pCity->provider()
			    << pCity->city()
			    << pCity->country()
			    << pCity->countryCode()
			    << pCity->extraData()
				<< pCity->timeZone().name();
			QString sKey = QString("city%1").arg(iCityIndex+1, 2, 10, QChar('0'));
			cityCfg.writeEntry( sKey, lst );
		}
		
		cityCfg.writeEntry("selected", configData.iCityIndex);
	}
	dEndFunct();
}

void
YaWP::loadCustomTheme()
{
	dStartFunct();
	if( !m_configData.bUseCustomTheme )
	{
		dEndFunct();
		return;
	}
	if( !QFile(m_configData.sCustomThemeFile).exists() )
	{
		m_configData.bUseCustomTheme = false;
		dWarning() << "File does not exist: " << m_configData.sCustomThemeFile;
		return;
	}
	/*TODO get the following paramaters from theme:
		rect The rectangle to draw in.
		font The font to use.
		color The font color to use.
	*/
	m_customSvg.setImagePath( m_configData.sCustomThemeFile );
	m_customSvg.setContainsMultipleImages( true );
	dEndFunct();
}

void
YaWP::constraintsEvent(Plasma::Constraints constraints)
{
	dStartFunct();
	
	// Presentation of applet has been changed
	//   (e.g.: applet moved from desktop to panel; panel changed from horizontal to vertical; ...)
	if (constraints.testFlag(Plasma::FormFactorConstraint))
	{
		initAppletPainter();
		
		if (m_pAppletPainter != 0)
		{
			if (m_pAppletPainter->formFactor() != Plasma::Horizontal &&
			    m_pAppletPainter->formFactor() != Plasma::Vertical)
			{
				if (m_configData.sBackgroundName == QLatin1String("default") &&
				    !(m_configData.bUseCustomTheme &&
				      m_configData.bUseCustomThemeBackground) )
				{
					setBackgroundHints(DefaultBackground);
				}
				else
				{
					setBackgroundHints(NoBackground);
				}
			}
			else
			{
				// no backround in horizontal and vertical panel mode
				setBackgroundHints(NoBackground);
			}

			updateSize();
		}
		else
		{
			dWarning() << "Applet is not initialized yet to receive FormFactorConstraint.";
		}
	}
	
	// Size of applet has been changed
	if (constraints.testFlag(Plasma::SizeConstraint))
	{
		if (m_pAppletPainter != 0 )
		{
			if (m_pAppletPainter->formFactor() != Plasma::Planar)
			updateSize();
		}
		else
		{
			dWarning() << "Applet is not initialized yet to receive SizeConstraint.";
		}
	}

	// Now we need to make sure applet will be repainted, update triggers an new paint request
	if (m_pAppletPainter != 0)
		m_pAppletPainter->update();
	
	dEndFunct();
}

void
YaWP::updateSize()
{
	dStartFunct();
	
	if (formFactor() == Plasma::Horizontal)
	{
		int height = contentsRect().height() > 0 ? contentsRect().height() : 45;
		int sizeHintWidth = m_pAppletPainter->widthForHeight(height);

		setMinimumHeight(0);
		setMaximumHeight(QWIDGETSIZE_MAX);

		setMinimumWidth(sizeHintWidth);
		setMaximumWidth(sizeHintWidth);
	}
	else if (formFactor() == Plasma::Vertical)
	{
		int width = contentsRect().width() > 0 ? contentsRect().width() : 45;
		int sizeHintHeight = m_pAppletPainter->heightForWidth(width);

		setMinimumWidth(0);
		setMaximumWidth(QWIDGETSIZE_MAX);

		setMinimumHeight(sizeHintHeight);
		setMaximumHeight(sizeHintHeight);
	}
	else
	{
		QSizeF sizeHint = size();
		const BaseDesktopPainter * desktopPainter = (const BaseDesktopPainter *)m_pAppletPainter;
		
		if (desktopPainter->aspectRatioMode() == Plasma::KeepAspectRatio)
		{
			int heightForWidth = desktopPainter->heightForWidth(qRound(sizeHint.width()));
			if (heightForWidth < sizeHint.height())
				sizeHint.setHeight(heightForWidth);
			else
			{
				int widthForHeight = desktopPainter->widthForHeight(qRound(sizeHint.height()));
				if (widthForHeight < sizeHint.width())
					sizeHint.setWidth(widthForHeight);
			}
		}
		setMinimumWidth(30);
		setMaximumWidth(QWIDGETSIZE_MAX);
		setMinimumHeight(30);
		setMaximumHeight(QWIDGETSIZE_MAX);

		resize(sizeHint);
	}
	dEndFunct();
}

// The paintInterface procedure paints the applet to screen
void
YaWP::paintInterface(QPainter * painter,
                     const QStyleOptionGraphicsItem * option,
                     const QRect & contentsRect)
{
	Q_UNUSED(option);

	if (m_pAppletPainter != 0)
		m_pAppletPainter->paintApplet(painter, option, contentsRect);
}

void
YaWP::createConfigurationInterface(KConfigDialog * parent)
{
	dStartFunct();
	if (m_pConfigDlg != NULL)
		delete m_pConfigDlg;
	m_pConfigDlg = new YawpConfigDialog( parent, &m_storage );
	m_pConfigDlg->copyCities( m_pWeatherModel );
	m_pConfigDlg->setData( &m_configData );

	connect(parent, SIGNAL(applyClicked()), this, SLOT(configAccepted()));
	connect(parent, SIGNAL(okClicked()), this, SLOT(configAccepted()));
	dEndFunct();
}

void
YaWP::configAccepted()
{
	dStartFunct();
	
	if (m_pConfigDlg != 0)
	{
		Yawp::ConfigData configData;
		m_pConfigDlg->getData( &configData );
		saveConfig( configData, *m_pConfigDlg->weatherModel() );
		
		emit configNeedsSaving();
	}
	dEndFunct();
}

void
YaWP::configChanged()
{
	dStartFunct();
	
	// get oldest date time from city model
	QDateTime oldestUpdate;
	bool allCitiesUpdated = true;
	if (m_pConfigDlg != 0 && isUserConfiguring())
	{
		for( int idx = 0; idx < m_pWeatherModel->rowCount(); ++idx)
		{
			const CityWeather * pCity = m_pWeatherModel->getCityInfo(idx);
			if (!pCity->lastUpdate().isValid())
				allCitiesUpdated = false;
			else if (!oldestUpdate.isValid() || oldestUpdate > pCity->lastUpdate())
				oldestUpdate = pCity->lastUpdate();
		}
	}

	//--- load new configuration
	loadConfig();

	// check if we can delay city weather update
	if (m_pConfigDlg != 0 && isUserConfiguring())
	{
		if (oldestUpdate.isValid() && !m_pConfigDlg->weatherDataUpdateRequired())
		{
			int delaySec = oldestUpdate.addSecs(m_configData.iUpdateInterval * 60).toTime_t() - QDateTime::currentDateTime().toTime_t();
			dDebug() << "Update will be delayed about" << delaySec << "seconds.";
			
			if (delaySec > 0 && allCitiesUpdated)
			{
				stopPendingEngineConnection();
				m_iIdPendingEngineConnection = startTimer( delaySec*1000 );

				// we set last update time to all cities, to suppress "(Cache)" string from city name
				for( int idx = 0; idx < m_pWeatherModel->rowCount(); ++idx)
					m_pWeatherModel->getCityInfo(idx)->setLastUpdate(oldestUpdate);
			}
		}
		m_pConfigDlg->resetChanges();
	}
	
	if (m_pWeatherModel->rowCount() == 0)
	{
		// When we have no city anymore, make sure popup will be hidden.
		// This can only happen, when extender is shown and we delete all cities.
		// In this case we do not accept nor pass any mouse events to the applet painter to
		// handle this events. Therefore there is no way for the user to hide the popup manual,
		// unless user is adding at least one city to the applet.
		hidePopup();
		
		// remove current tooltip
		Plasma::ToolTipManager::self()->clearContent(this);
	}

	// trigger size changed otherwise applet might not full visible after theme/layout changes
	constraintsEvent( Plasma::FormFactorConstraint | Plasma::SizeConstraint );
	
	// I noticed applet is not properly repainting after layout changes (in panel mode)
	// I guess the reason for this is that widget repaint event will be triggered before
	// applet is resizing through constraints event.
	// Delaying painter update event solves this problem.
	QTimer::singleShot(50, m_pAppletPainter, SLOT(update()));
	
	dEndFunct();
}

void
YaWP::mousePressEvent(QGraphicsSceneMouseEvent * event)
{
	const CityWeather * const pCity = m_stateMachine.currentCity();

	if (!pCity ||
	    m_pAppletPainter->timeLine()->state() == QTimeLine::Running ||
	    event->button() != Qt::LeftButton )
	{
		return;
	}

	// every time user interacts with this plasmoid, we reset the timer
	// to automatically switch to next location
	stopTraverseLocationTimeout();

	event->setAccepted(false);

	// when applet is desktop mode or panel mode is configured to interact with mouse
	// than we pass the mouse click event to painter
	if (m_pAppletPainter->formFactor() == Plasma::Planar ||
	    m_configData.bUseInteractivePanelWeatherIcons)
	{
		m_pAppletPainter->mousePressEvent(event);
	}

	// when we are in panel mode and mouse click did not hit and interactive weather icon than toggle popup
	if (!event->isAccepted() && m_pAppletPainter->formFactor() != Plasma::Planar)
		togglePopup();

	//   Restart timeout to traverse through all locations periodically
	startTraverseLocationTimeout();
}

void
YaWP::timerEvent(QTimerEvent * event)
{
	Plasma::PopupApplet::timerEvent(event);
	
	if (event->timerId() == m_iIdPendingEngineConnection)
	{
		stopPendingEngineConnection();
		m_pWeatherModel->reconnectEngine();
	}
	else if (event->timerId() == m_iIdTraverseLocations)
	{
		stopTraverseLocationTimeout();

		int iSelectedCityIndex = (m_configData.iCityIndex + 1) % m_pWeatherModel->rowCount();
		m_pAppletPainter->triggerCityChangeAnimation(iSelectedCityIndex);
	}
}

void
YaWP::slotCityUpdate(WeatherServiceModel::ServiceUpdate updateType)
{
	dStartFunct();
	stopPendingEngineConnection();
	const CityWeather * const pCity = m_stateMachine.currentCity();

	if(!pCity)
	{
		dEndFunct();
		return;
	}

	if( updateType.testFlag(WeatherServiceModel::CityInfoUpdate) )
	{
		updateCitySubMenu();
		saveConfig(m_configData, *m_pWeatherModel);
		emit configNeedsSaving();
	}

	//--- update the paneltooltip when we are in panel mode only ---
	if (m_pAppletPainter->formFactor() != Plasma::Planar)
		createPanelTooltip();
	
	m_pOpenForecastUrl->setEnabled( !pCity->creditUrl().isNull() );

	m_pAppletPainter->update();
	dEndFunct();
}

/***************************************************************************\
*
\***************************************************************************/

void
YaWP::changeCity( QAction * action )
{
	int iSelectedCityIndex = action->data().toInt();
	if( iSelectedCityIndex >= 0 &&
	    iSelectedCityIndex < m_pWeatherModel->rowCount() &&
	    iSelectedCityIndex != m_configData.iCityIndex )
	{
		m_pAppletPainter->triggerCityChangeAnimation(iSelectedCityIndex);
	}
}

void
YaWP::about() const
{
	KAboutApplicationDialog * aboutDialog = new KAboutApplicationDialog ( m_aboutData );
	connect ( aboutDialog, SIGNAL (finished()), aboutDialog, SLOT(deleteLater()) );
	aboutDialog->show ();
}

void
YaWP::openForecastUrl()
{
	const CityWeather * city = m_stateMachine.currentCity();
	if (!city)
		return;
	
	if (!city->creditUrl().isNull())
		Utils::OpenUrl(city->creditUrl());
}

void
YaWP::animationFinished()
{
	startTraverseLocationTimeout();
}

/***********************************************************************************
*/

void
YaWP::setCityIndex( int cityIndex )
{
	dStartFunct();
	m_stateMachine.setCurrentCityIndex( cityIndex );
	m_configData.iCityIndex = m_stateMachine.currentCityIndex();
	const CityWeather * city = m_stateMachine.currentCity();

	if (city)
	{
		//--- select the right menuitem in the city-submenu ---
		QList<QAction *> list = m_pCitySubMenu->menu()->actions();
		int iCityIndex = m_stateMachine.currentCityIndex();
		if (list.count() > iCityIndex)
			list.at(iCityIndex )->setChecked( true );

		//--- update the paneltooltip when we are in panel mode only ---
		if (m_pAppletPainter->formFactor() != Plasma::Planar)
			createPanelTooltip();
		
		m_pOpenForecastUrl->setEnabled( !city->creditUrl().isNull() );
	}
	m_pAppletPainter->update();
	dEndFunct();
}

void
YaWP::slotToggleWeatherIcon(int dayIndex)
{
	m_stateMachine.toggleIconState(dayIndex);
}

void
YaWP::slotThemeChanged()
{
	setDefaultFontColors();

	if (m_pAppletPainter->formFactor() != Plasma::Planar)
		createPanelTooltip();
}

void
YaWP::slotStartCacheCleanUp()
{
	dTracing() << "Start cache clean up...";
	QDir cacheDir(
		WeatherDataProcessor::CacheDirectory,
		"*.dat",
		QDir::NoSort,
		QDir::Files | QDir::NoSymLinks | QDir::Writable);
	QFileInfoList cacheFiles = cacheDir.entryInfoList();

	const QDateTime currentDateTime = QDateTime::currentDateTime();
	KUrl::List selectedCacheFiles;

	foreach(QFileInfo info, cacheFiles)
	{
		// When file is older than five days, we add it to the list of files that will be removed.
		if (info.lastModified().daysTo(currentDateTime) > 5)
		{
			dTracing() << "Remove cache file" << info.absoluteFilePath();
			selectedCacheFiles.append(KUrl(info.absoluteFilePath()));
		}
		else
		{
			dTracing() << "Keeping file" << info.absoluteFilePath();
		}
	}

	// Start job to remove selected files. We hide the progress information,
	// because we do not want to bother the user with this stuff.
	if (selectedCacheFiles.count() > 0)
		KIO::del(selectedCacheFiles, KIO::HideProgressInfo);
}

void
YaWP::setDefaultFontColors()
{
	if( m_configData.bUseCustomFontColor )
		return;

	if (m_configData.sBackgroundName.compare( QLatin1String("default") ) == 0 ||
	    m_configData.sBackgroundName.compare( QLatin1String("naked") ) == 0)
	{
		m_configData.fontColor = KColorScheme(QPalette::Active, KColorScheme::View,
			Plasma::Theme::defaultTheme()->colorScheme()).foreground().color();
		m_configData.lowFontColor = KColorScheme(QPalette::Active, KColorScheme::View,
			Plasma::Theme::defaultTheme()->colorScheme()).foreground(KColorScheme::InactiveText).color();

		if( m_configData.fontColor.red() < 25 &&
		    m_configData.fontColor.green() < 25 &&
		    m_configData.fontColor.blue() < 25 )
		{
			m_configData.lowFontColor = m_configData.fontColor.lighter();
			m_configData.shadowsFontColor = QColor(255,255,255,100);
		}
		else
		{
			m_configData.lowFontColor = m_configData.fontColor.darker(125);
			m_configData.shadowsFontColor = QColor(0,0,0,100);
		}
	}
	else
	{
		m_configData.fontColor = QColor(Qt::white);
		m_configData.lowFontColor = QColor(Qt::gray);
		m_configData.shadowsFontColor = QColor(0,0,0,100);
	}
	
	m_configData.forecastTextColor = m_configData.fontColor;
}

void
YaWP::updateCitySubMenu()
{
	//--- first of all we delete all old actions ---
	m_pCitySubMenu->menu()->clear();

	const int iMaxCities = m_pWeatherModel->rowCount();
	for( int i = 0; i < iMaxCities; ++i )
	{
		const CityWeather * cityInfo = m_pWeatherModel->getCityInfo( i );
		QIcon flag = m_storage.countryMap()->getPixmapForCountryCode( cityInfo->countryCode() );
		QAction * action = new QAction( flag, cityInfo->localizedCityString(), this );
		action->setCheckable( true );
		action->setData( QVariant(i) );
		m_pGrpActionCities->addAction( action );
		m_pCitySubMenu->addAction( action );

		if( i == m_configData.iCityIndex )
			action->setChecked( true );
	}
	m_pCitySubMenu->setEnabled( iMaxCities > 0 );
}

void
YaWP::initAppletPainter()
{
	dStartFunct();
	
	if (m_pWeatherModel == 0)
	{
		dWarning() << "Applet is not initialized yet.";
		dEndFunct();
		return;
	}
	
	const Plasma::FormFactor form = formFactor();
	dTracing() << "Current formFactor:" << form;
	const Yawp::PainterType painterType = (form == Plasma::Horizontal || form == Plasma::Vertical) ? Yawp::PanelPainter : m_configData.desktopPainterType;

	if (m_pAppletPainter == NULL ||
	    m_pAppletPainter->formFactor() != form ||
	    m_pAppletPainter->painterType() != painterType)
	{
		if (m_pAppletPainter != NULL)
		{
			dTracing() << "Deleting old appletPainter";
			m_pAppletPainter->disconnect();
			this->disconnect(m_pAppletPainter);
			this->disconnect(m_pAppletPainter->timeLine());

			delete m_pAppletPainter;
		}

		if (form == Plasma::Horizontal || form == Plasma::Vertical)
		{
			dTracing() << "Creating panel painter";
			m_pAppletPainter = createPanelPainter(this, painterType, form);
			createExtenderItem();
			createPanelTooltip();
		}
		else
		{
			dTracing() << "Creating desktop painter";
			BaseDesktopPainter * painter = createDesktopPainter(this, painterType, true);
			setAspectRatioMode(painter->aspectRatioMode());
			m_pAppletPainter = painter;
			
			destroyExtenderItem();
			
			dTracing() << "Clear ToolTipContent";
			Plasma::ToolTipManager::self()->clearContent(this);
		}
		
		dTracing() << "Going to connect appletPainter";
		connect(m_pWeatherModel, SIGNAL(isBusy(bool)), m_pAppletPainter, SLOT(setBusy(bool)));
		connect(m_pAppletPainter, SIGNAL(signalCityChanged(int)), this, SLOT(setCityIndex(int)), Qt::DirectConnection);
		connect(m_pAppletPainter, SIGNAL(signalToggleWeatherIcon(int)), this, SLOT(slotToggleWeatherIcon(int)), Qt::DirectConnection);
		connect(m_pAppletPainter->timeLine(), SIGNAL(finished()),  this, SLOT(animationFinished()));
	}
	else
	{
		setupPainter(m_pAppletPainter);
		
		if (form == Plasma::Horizontal || form == Plasma::Vertical)
		{
			BasePanelPainter * panelPainter = (BasePanelPainter *)m_pAppletPainter;
			if (panelPainter->popupPainter() != NULL)
				setupPainter(panelPainter->popupPainter());
		}
	}
	
	/* Check whether applet painter is in panel mode, desktop interface has been initialized
	 * and painter of desktop interface (painter of popup window) is wrong type
	 * 
	 * When we just change the painter type of the popup painter than we will not go through
	 * previous If-Condititon, therefore we need to change the popup painter in a seperate block.
	 */
	dTracing() << "Verifying conditions to create popupPainter";
	if (m_pAppletPainter != NULL &&
	    (m_pAppletPainter->formFactor() == Plasma::Horizontal || m_pAppletPainter->formFactor() == Plasma::Vertical) &&
	    m_pPanelDesktopInterface != NULL &&
	    (m_pPanelDesktopInterface->desktopPainter() == NULL ||
	     m_pPanelDesktopInterface->desktopPainter()->painterType() != m_configData.popupPainterType))
	{
		dTracing() << "Creating popupPainter";
		BaseDesktopPainter * popupPainter = createDesktopPainter(NULL, m_configData.popupPainterType, false);
		((BasePanelPainter *)m_pAppletPainter)->setPopupPainter(popupPainter);
		m_pPanelDesktopInterface->setDesktopPainter(popupPainter);
	}
	
	dEndFunct();
}

BaseDesktopPainter *
YaWP::createDesktopPainter(QGraphicsWidget * graphicsWidget, Yawp::PainterType painterType, bool desktopMode)
{
	dStartFunct();
	
	BaseDesktopPainter * painter = NULL;
	
	switch (painterType)
	{
		case Yawp::ExtendedDesktopPainter:
			painter = new ExtendedDesktopPainter(graphicsWidget, &m_configData, &m_stateMachine, desktopMode);
			break;
			
		default:
			painter = new DesktopPainter(graphicsWidget, &m_configData, &m_stateMachine);
			break;
	}
	
	setupPainter(painter);
	
	dEndFunct();
	
	return painter;
}

BasePanelPainter *
YaWP::createPanelPainter(QGraphicsWidget * graphicsWidget, Yawp::PainterType painterType, Plasma::FormFactor formFactor)
{
	dStartFunct();
	BasePanelPainter * painter = NULL;
	
	switch (painterType)
	{
		default:
			painter = new PanelPainter(graphicsWidget, &m_configData, &m_stateMachine, formFactor);
			break;
	}
	
	setupPainter(painter);
	
	dEndFunct();
	
	return painter;
}

void
YaWP::setupPainter(AbstractPainter * painter)
{
	dStartFunct();

	if (m_svg.isValid())
		painter->setSvg(&m_svg);
	if (m_customSvg.isValid())
		painter->setCustomSvg(&m_customSvg);

	painter->setupAnimationTimeLine();
	
	dEndFunct();
}

PanelDesktopInterface *
YaWP::createDesktopInterface()
{
	dStartFunct();
	
	BaseDesktopPainter * painter = createDesktopPainter(NULL, m_configData.popupPainterType, false);
	PanelDesktopInterface * pPanelDesktopInterface = new PanelDesktopInterface(painter);

	pPanelDesktopInterface->setMinimumSize(QSizeF(100, 93));
	pPanelDesktopInterface->setPreferredSize(273, 255);

	connect( m_pWeatherModel, SIGNAL(isBusy(bool)), pPanelDesktopInterface, SLOT(setBusy(bool)) );
	
	dEndFunct();
	
	return pPanelDesktopInterface;
}

void
YaWP::createExtenderItem()
{
	dStartFunct();
	
#if KDE_IS_VERSION(4,7,95)
	if (m_pPanelDesktopInterface == NULL)
	{
		m_pPanelDesktopInterface = createDesktopInterface();
		setGraphicsWidget(m_pPanelDesktopInterface);
	}
	PanelPainter * panelPainter = dynamic_cast<PanelPainter *>(m_pAppletPainter);
	if (panelPainter)
		panelPainter->setPopupPainter(m_pPanelDesktopInterface->desktopPainter());
	
#else
	bool itemFound = extender()->hasItem(PANEL_DESKTOP_INTERFACE_NAME);
	Plasma::ExtenderItem * item = NULL;

	if (itemFound)
		item = extender()->item(PANEL_DESKTOP_INTERFACE_NAME);

	// When we already have an extender item, just connect the current painter with this extender item
	if (item != 0 && item->widget() != NULL)
	{
		PanelDesktopInterface * popupDesktopInterface = dynamic_cast<PanelDesktopInterface *>(item->widget());
		PanelPainter * panelPainter = dynamic_cast<PanelPainter *>(m_pAppletPainter);

		if (panelPainter != NULL && popupDesktopInterface != NULL)
		{
			panelPainter->setPopupPainter(popupDesktopInterface->desktopPainter());
			dInfo() << "Reusing existing desktop interface.";
		}
		else
			// should not happen !!! does it ???
			dError() << "Could not connect to existing desktop interface!";
	}
	else
	{
		dInfo() << "Creating new desktop interface.";
		Plasma::ExtenderItem * item = new Plasma::ExtenderItem(extender());
		item->setName(PANEL_DESKTOP_INTERFACE_NAME);
		initExtenderItem(item);
	}
#endif
	dEndFunct();
}

#if KDE_IS_VERSION(4,7,95)
#else
void
YaWP::initExtenderItem(Plasma::ExtenderItem * item)
{
	PanelPainter * panelPainter = dynamic_cast<PanelPainter *>(m_pAppletPainter);

	/* We accept only one extender item with this name, because it does not make
	 * sence to have multiple items connected.
	 */
	if (item->name() == PANEL_DESKTOP_INTERFACE_NAME &&
	    panelPainter != 0 &&
	    panelPainter->getPopupPainter() == 0)
	{
		PanelDesktopInterface * popupDesktopInterface = createDesktopInterface();
		panelPainter->setPopupPainter(popupDesktopInterface->desktopPainter());

		item->setTitle(i18n("yaWP Desktop Interface"));
		item->setIcon("weather-clear");
		item->setWidget(popupDesktopInterface);
	}
	else
		Plasma::PopupApplet::initExtenderItem(item);
}
#endif

void
YaWP::destroyExtenderItem()
{
	dStartFunct();
	
	hidePopup();
	
#if KDE_IS_VERSION(4,7,95)
	if (m_pPanelDesktopInterface != 0)
	{
		setGraphicsWidget(0);
		m_pPanelDesktopInterface->deleteLater();
		m_pPanelDesktopInterface = 0;
	}
#else
	bool itemFound = extender()->hasItem(PANEL_DESKTOP_INTERFACE_NAME);

	if (itemFound)
	{
		extender()->item(PANEL_DESKTOP_INTERFACE_NAME)->deleteLater();
	}
#endif
	dEndFunct();
}

void
YaWP::createPanelTooltip()
{
	dStartFunct();
	Plasma::ToolTipManager::self()->clearContent(this);

	//--- just exit, when we have no city or no weather information ---
	const CityWeather * city = m_stateMachine.currentCity();
	if( !city || city->days().count() == 0 )
	{
		dEndFunct();
		return;
	}

	Plasma::ToolTipContent toolTipData;
	toolTipData.setMainText( city->localizedCityString() );
	
	//--- Create simple pixmap tooltip ----
	if (!m_configData.bUseExtendedTooltip || city->days().count() == 1)
	{
		DesktopPainter toolTipPainter(0, &m_configData, &m_stateMachine);
		toolTipPainter.setSvg(&m_svg);
		toolTipPainter.setCustomSvg(&m_customSvg);

		QPixmap pix;
		pix = toolTipPainter.createSimpleToolTip(218);
		if (!pix.isNull())
		{
			toolTipData.addResource(
				Plasma::ToolTipContent::ImageResource,
				QUrl("wicon://applet_image"),
				QVariant(pix));
			
			QString toolTipText = "<table><tr>";
			toolTipText += "<td valign=\"top\"><img src=\"wicon://applet_image\"/></td>";
			toolTipText += "</tr></table>";
			toolTipData.setSubText(toolTipText);
		}
	}
	else
	{
		//--- Create extended tooltip ---
		QString toolTipText = "<table cellspacing=\"8\"><tr>";
		
		//--- add applet to extended tooltip ---
		if (m_configData.extendedTooltipOptions.testFlag(Yawp::PreviewPage))
		{
			DesktopPainter toolTipPainter(0, &m_configData, &m_stateMachine);
			toolTipPainter.setSvg(&m_svg);
			toolTipPainter.setCustomSvg(&m_customSvg);

			QPixmap pix;
			pix = toolTipPainter.createExtendedToolTip(218);
			if (!pix.isNull())
			{
				toolTipData.addResource(
					Plasma::ToolTipContent::ImageResource,
					QUrl("wicon://applet_image"),
					QVariant(pix));
				
				toolTipText += "<td valign=\"top\"><img src=\"wicon://applet_image\"/></td>";
			}
		}
		
		//--- add forecast text for 3 days to tooltip ---
		toolTipText += "<td width=\"220\">";
		const int maxDayIndex = qMin(3, city->days().count());
		for(int dayIndex = 0; dayIndex < maxDayIndex; dayIndex++)
		{
			if (city->days().count() > dayIndex)
			{
				const YawpDay * day = city->days().at(dayIndex);
				
				QString forecastBody = Utils::CreateForecastTextBody(
					*day,
					dayIndex == 0,
					dayIndex == 0,
					QLatin1String("<br />"));
				
				if (forecastBody.length() > 0)
					toolTipText += "<u>" + Utils::CreateForecastTextHeader(*day) + "</u><br />"
						+ forecastBody;
						
				if (dayIndex + 1 < maxDayIndex)
					toolTipText += "<br /><br />";
			}
			else
			      break;
		}
		
		//--- add satelite image to tooltip ---
		if (m_configData.extendedTooltipOptions.testFlag(Yawp::SatellitePage) &&
		    !city->satelliteImage().isNull())
		{
			toolTipData.addResource(
				Plasma::ToolTipContent::ImageResource,
				QUrl("wicon://satelite_image"),
				QVariant(city->satelliteImage().scaledToWidth(218, Qt::SmoothTransformation)));

			toolTipText += "<td valign=\"top\"><img src=\"wicon://satelite_image\"/></td>";
		}
		
		toolTipText += "</tr></table>";
		toolTipData.setSubText(toolTipText);
	}

	toolTipData.setAutohide(false);
	Plasma::ToolTipManager::self()->setContent(this, toolTipData);

	dEndFunct();
}

void
YaWP::setupWeatherServiceModel()
{
	m_pWeatherModel->setUpdateInterval( m_configData.iUpdateInterval );
	//m_pWeatherModel->setDetailsPropertyList( m_configData.vDetailsPropertyRankingList );

	WeatherDataProcessor * pDataProcessor = m_pWeatherModel->dataProcessor();
	if (pDataProcessor)
	{
		pDataProcessor->createDetailsPropertyMap( m_configData.vDetailsPropertyRankingList );
		pDataProcessor->setDistanceSystem( m_configData.distanceSystem );
		pDataProcessor->setPressureSystem( m_configData.pressureSystem );
		pDataProcessor->setTemperatureSystem( m_configData.temperatureSystem );
		pDataProcessor->setSpeedSystem( m_configData.speedSystem );
	}
}

void
YaWP::stopPendingEngineConnection()
{
	if (m_iIdPendingEngineConnection > 0)
	{
		killTimer(m_iIdPendingEngineConnection);
		m_iIdPendingEngineConnection = -1;
	}
}

void
YaWP::startTraverseLocationTimeout()
{
	if (m_configData.bTraverseLocationsPeriodically && m_pWeatherModel->rowCount() > 1)
		m_iIdTraverseLocations = startTimer(m_configData.iTraverseLocationTimeout * 1000);
}

void
YaWP::stopTraverseLocationTimeout()
{
	if (m_iIdTraverseLocations > 0)
		killTimer(m_iIdTraverseLocations);
}

// This is the command that links your applet to the .desktop file
K_EXPORT_PLASMA_APPLET(yawp, YaWP);
