/*************************************************************************\
*   Copyright (C) 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            *
\*************************************************************************/

//--- LOCAL CLASSES ---
#include "extendeddesktoppainter.h"
#include "pageanimator.h"
#include "utils.h"
#include "weatherservice.h"
#include "yawpday.h"
#include "yawpdefines.h"

#include "logger/streamlogger.h"

//--- QT4 CLASSES ---
#include <QGraphicsSceneMouseEvent>
#include <QFontMetrics>

#include <QAbstractTextDocumentLayout>
#include <QTextCursor>
#include <QTextDocument>
#include <QTextTable>

//--- KDE4 CLASSES ---
#include <KCalendarSystem>
#include <KGlobalSettings>
#include <Plasma/Svg>
#include <Plasma/Theme>


/** This parameter calculates the applet height based on the mini applet height.
 *  The mini applet and the satellite map receive the same space. The weather forecast text
 *  receives the remaining space (in this case 60 percent of mini applet width.
 */
const qreal ExtendedDesktopPainter::EXTENDED_LAYOUT_HEADER_OFFSET = 1.135;

/** This parameter calculates the applet width based on the mini applet height in a worst case scenario.
 *  The mini applet and the satellite map receive the same space. The weather forecast text
 *  receives the remaining space (in this case 60 percent of mini applet width.
 */
const qreal ExtendedDesktopPainter::EXTENDED_LAYOUT_APPLET_WIDTH = 2.675;


ExtendedDesktopPainter::ExtendedDesktopPainter (
	QGraphicsWidget * widget,
	const Yawp::ConfigData * configData,
	StateMachine * stateMachine,
	bool popupMode)
    : DesktopPainter(widget, configData, stateMachine),
      m_bDesktopMode(popupMode),
      m_bShowForecastHeaderDayNames(true)
{
}

ExtendedDesktopPainter::~ExtendedDesktopPainter()
{
}

Plasma::AspectRatioMode
ExtendedDesktopPainter::aspectRatioMode() const
{
	return Plasma::IgnoreAspectRatio;
}

Yawp::PainterType
ExtendedDesktopPainter::painterType() const
{
	return Yawp::ExtendedDesktopPainter;
}

int
ExtendedDesktopPainter::heightForWidth(int width) const
{
  	// make sure width of applet is a positiv number
	width = width <= 0 ? YAWP_ORIGINAL_WIDTH : width;

	return qRound((qreal)width / EXTENDED_LAYOUT_APPLET_WIDTH * EXTENDED_LAYOUT_HEADER_OFFSET);
}

int
ExtendedDesktopPainter::widthForHeight(int height) const
{
	// make sure height of applet is a positiv number
	height = height <= 0 ? YAWP_ORIGINAL_HEIGHT : height;

	return qRound((qreal)height / EXTENDED_LAYOUT_HEADER_OFFSET * EXTENDED_LAYOUT_APPLET_WIDTH);
}

QRect
ExtendedDesktopPainter::getContentsRect(const QRect& contentsRect) const
{
	const qreal dScale = calculateLayoutScalingFactor(contentsRect);

	/*  When theme "default" is been used than we draw the default plasmoid applet border
	 *  otherwise we do not need the border. If we do not paint the background, than we do
	 *  not need to add some space at all. But when we paint on the popup window than we
	 *  need the boarder as well.
	 */
	if (!m_bDesktopMode || configData()->sBackgroundName.compare("default") == 0)
	{
		return contentsRect.adjusted(
			qRound(20.0 * dScale),
			qRound(16.0 * dScale),
			-qRound(37.0 * dScale),
			-qRound(30.0 * dScale));
	}
	else
		return contentsRect;
}
/*
QSize
ExtendedDesktopPainter::getSize(const QSizeF & currentSize) const
{
	//--- the extended desktop painter is using the entire space ---
	return QSize(currentSize.width(), currentSize.height());
}
*/
void
ExtendedDesktopPainter::mousePressEvent(QGraphicsSceneMouseEvent* event)
{
	//--- when we have no city, than there is nothing to click ---
	if (stateMachine()->serviceModel()->rowCount() == 0)
		return;

	const bool hasPreviewPage( stateMachine()->hasPage( Yawp::PreviewPage) );
	const bool hasDetailsPage( stateMachine()->hasPage( Yawp::DetailsPage) );
	const Yawp::PageType currentPage( stateMachine()->currentPage() );
	const int detailsDayIndex = currentPage == Yawp::PreviewPage ? 0 : stateMachine()->detailsDayIndex();

	const QRectF rectPreviewPageButton = getPageButtonRect(m_headerRectangle,  Yawp::PreviewPage );
	const QRectF rectDetailsPageButton = getPageButtonRect(m_headerRectangle, Yawp::DetailsPage );
	
	const QRect appletContentsRect = DesktopPainter::getContentsRect(m_miniAppletRectangle);
	const QRect detailsHeaderRect = getDetailsHeaderRect(appletContentsRect);
	const QRect topWeatherIconRect = getTodaysWeatherIconRect(appletContentsRect);
	const QRectF rectPrevCityButton = getCityButtonRect(m_headerRectangle, true);
	const QRectF rectNextCityButton = getCityButtonRect(m_headerRectangle, false);

	const CityWeather * currentCity = stateMachine()->currentCity();

	
	/***  Check if one of the page buttons (Preview, Details or Satellite Page) has been pressed ***
	 */
	//--- one of the first two buttons has been pressed ---
	if( (currentPage != Yawp::DetailsPage && hasDetailsPage && rectDetailsPageButton.contains(event->pos())) ||
	    (currentPage != Yawp::PreviewPage && hasPreviewPage && rectPreviewPageButton.contains(event->pos())) )
	{
		if( detailsDayIndex > 0 )
		{
			Yawp::PageType pageType = (rectDetailsPageButton.contains(event->pos()) ? Yawp::DetailsPage : Yawp::PreviewPage);
			initMiniAppletPageChange(appletContentsRect, 0, pageType);
		}
		else
		{
			/*  just update the details rectangle since we see the first day
		         *  and the current page is either PreviewPage or DetailsPage.
		         */
			initPartChange(appletContentsRect, AbstractPainter::FullDetailsChange, m_bShowForecastHeaderDayNames, true);
		}
		event->accept();
		return;
	}
	
	/*** Check if button for previous or next city has been clicked
	 */
	if (configData()->iCityIndex > 0 && rectPrevCityButton.contains(event->pos()))
	{
		initPageChange( AbstractPainter::CityChangeAnimation, configData()->iCityIndex - 1, false );
		event->accept();
		return;
	}
	else if (configData()->iCityIndex+1 < stateMachine()->serviceModel()->rowCount() &&
	         rectNextCityButton.contains(event->pos()))
	{
		initPageChange( AbstractPainter::CityChangeAnimation, configData()->iCityIndex + 1, true );
		event->accept();
		return;
	}
	
	/*** Check if rectangel that contains the daynames or dates has been clicked
	 */
	if (currentPage == Yawp::PreviewPage && detailsHeaderRect.contains(event->pos().x(), event->pos().y()))
	{
		initPartChange(appletContentsRect, AbstractPainter::SlidingDayNames, m_bShowForecastHeaderDayNames, false);
		m_bShowForecastHeaderDayNames = !m_bShowForecastHeaderDayNames;
		event->accept();
		return;
	}
	
	/*** Check if top weather icon has been clicked, we do not check for the lower ones,
	 *   since be paint day and night at once.
	 */
	if (topWeatherIconRect.contains(event->pos().x(), event->pos().y()))
	{
		if (currentCity->days().count() > detailsDayIndex &&
		    currentCity->days().at(detailsDayIndex)->hasNightValues())
		{
			initWeatherIconChange(topWeatherIconRect, detailsDayIndex);
		}
		event->accept();
		return;
	}
	
	if (currentPage == Yawp::DetailsPage)
	{
		const QRect rectPreviousDayButton = DesktopPainter::getDetailsDayButtonRect(detailsHeaderRect, true);
		const QRect rectNextDayButton = DesktopPainter::getDetailsDayButtonRect(detailsHeaderRect, false);

		if (detailsDayIndex > 0 &&
		    rectPreviousDayButton.contains(event->pos().x(), event->pos().y()))
		{
			initMiniAppletPageChange(appletContentsRect, detailsDayIndex - 1, currentPage);
			event->accept();
			return;
		}
		else if (detailsDayIndex + 1 < stateMachine()->currentCity()->days().count() &&
		         rectNextDayButton.contains(event->pos().x(), event->pos().y()))
		{
			initMiniAppletPageChange(appletContentsRect, detailsDayIndex + 1, currentPage);
			event->accept();
			return;
		}
		else if (stateMachine()->maxPropertyPage() > 1 &&
		         DesktopPainter::getDetailsContentsRect(appletContentsRect).contains(event->pos().x(), event->pos().y()))
		{
			DesktopPainter::initPartChange(appletContentsRect, AbstractPainter::DetailsChange, m_bShowForecastHeaderDayNames, false);
			event->accept();
			return;
		}
	}
}

void
ExtendedDesktopPainter::handleLayoutChanges()
{
	setButtonNames();
	m_sVisualCityName = createVisualCityName(stateMachine()->currentCity(), 600);
	calculateLayout(contentsRect(),
			m_headerRectangle,
			m_miniAppletRectangle,
			m_forecastTextRectangle,
			m_satelliteImageRectangle);
}

void
ExtendedDesktopPainter::drawBackground(QPainter* painter, const QRect& contentsRect) const
{
	Q_UNUSED(painter);
	Q_UNUSED(contentsRect);
	
	// this layout does not have a background
	// but we need to prevent that base class is painting its background
}

void
ExtendedDesktopPainter::drawPage(QPainter* painter, const QRect& contentsRect) const
{
	Q_UNUSED(contentsRect);
	
/*	painter->setPen(QColor(Qt::yellow));
	painter->setBrush(QColor(Qt::yellow));
	painter->drawRect(contentsRect);
	
	painter->setPen(QColor(Qt::green));
	painter->setBrush(QColor(Qt::blue));
	painter->drawRect(m_headerRectangle);
	
	painter->setBrush(QColor(Qt::darkBlue));
	painter->drawRect(m_miniAppletRectangle);
	
	painter->setBrush(QColor(Qt::darkGreen));
	painter->drawRect(m_forecastTextRectangle);
	
	painter->setBrush(QColor(Qt::darkBlue));
	painter->drawRect(m_satelliteImageRectangle);
*/ 
	drawNavigationHeader(painter, m_headerRectangle);
	
	//-- paint applet ---
	DesktopPainter::drawBackground(painter, m_miniAppletRectangle);
	
	QRect appletContentsRect = DesktopPainter::getContentsRect(m_miniAppletRectangle);
	drawMiniApplet(painter, appletContentsRect, m_bShowForecastHeaderDayNames);

	//--- paint satellite image ---
	DesktopPainter::drawSatelliteImage(painter, m_satelliteImageRectangle, false, Qt::AlignRight | Qt::AlignTop);
	
	//--- paint forecast text ---
	drawForecastText(painter, m_forecastTextRectangle, (qreal)m_miniAppletRectangle.width() / YAWP_ORIGINAL_WIDTH);
}

void
ExtendedDesktopPainter::drawNavigationHeader(QPainter* painter, const QRect& contentsHeaderRect) const
{
	const CityWeather * city = stateMachine()->currentCity();
	if (!city)
		return;

	painter->save();
	
	const qreal dOpacity = painter->opacity();
	Yawp::PageType currPage = stateMachine()->currentPage();
	const Yawp::PageType vPages[2] = {Yawp::PreviewPage, Yawp::DetailsPage};
	bool vAvailables[3] = {false, false, false};
	int iPageCounter = 0;
	
//	//--- get the state off all pages for the current city ---
	for( int i = 0; i < 2; ++i )
	{
		if (stateMachine()->hasPage(vPages[i]))
		{
			vAvailables[i] = true;
			iPageCounter += 1;
		}
	}

	/*  draw all available buttons on top of widget (Preview Page, Details Page and Satellite Page),
	 *  when there are at least two of them,
	 *  otherwise the user has no choice, which page he/she wants to see.
	 */
	if (iPageCounter > 1)
	{
		for (int i = 0; i < 2; ++i)
		{
			if (vAvailables[i])
			{
				painter->setOpacity( (currPage == vPages[i] ? 1.0 : 0.5) * dOpacity );
				drawImage(painter, getPageButtonRect(contentsHeaderRect, vPages[i]), getButtonName(vPages[i]));
			}
		}
	}
	painter->setOpacity( dOpacity );
	
	QRect rectArrowPrev = getCityButtonRect(contentsHeaderRect, true);
	QRect rectArrowNext = getCityButtonRect(contentsHeaderRect, false);
	int cityRectOffset = rectArrowPrev.right() - contentsHeaderRect.left() + qRound((qreal)rectArrowNext.width() * 0.4);
	
	QRect rectCity( contentsHeaderRect.left() + cityRectOffset + qRound(0.2 * (qreal)rectArrowPrev.height()),
	                qRound(rectArrowPrev.top()),
	                contentsHeaderRect.width() - 2 * cityRectOffset,
	                rectArrowNext.height() );
	
	/*** DRAW LOCATION NAME ***
	 */
	QFont font = painter->font();
	font.setBold(true);
	font.setPixelSize( qRound((qreal)rectArrowPrev.height() * 0.75) );

	QFontMetrics fontMetrics(font);
	painter->setFont(font);
	painter->setBrush(configData()->forecastTextColor);
	painter->setPen(configData()->forecastTextColor);

	if (isBusy())
		painter->drawText(rectCity, Qt::AlignHCenter | Qt::AlignVCenter, tr2i18n("Connecting..."));
	else
		painter->drawText(rectCity, Qt::AlignHCenter | Qt::AlignVCenter, m_sVisualCityName);
	
	/*** DRAW BUTTONS TO SELECT PREVIOUS AND NEXT CITY ***
	 */
	int cityCounter = stateMachine()->serviceModel()->rowCount();
	if( cityCounter > 1 )
	{
		painter->setOpacity( configData()->iCityIndex == 0 ? 0.5 : 1.0 );
		drawImage( painter, rectArrowPrev, QLatin1String("arrow-left") );
		painter->setOpacity( configData()->iCityIndex+1 == cityCounter ? 0.5 : 1.0 );
		drawImage( painter, rectArrowNext, QLatin1String("arrow-right") );
	}

	painter->setOpacity( dOpacity );
	painter->restore();
}

void
ExtendedDesktopPainter::drawMiniApplet(QPainter * painter, const QRect & appletContentsRect, bool showDayNames) const
{
	if (animationType() == AbstractPainter::MiniAppletPageAnimation)
	{
		dTracing() << "We do not paint page during detail change";
		return;
	}
	
	int dayIndex = stateMachine()->currentPage() == Yawp::DetailsPage ? stateMachine()->detailsDayIndex() : 0;
	const qreal dScale = appletContentsRect.height() / YAWP_ORIGINAL_HEIGHT;

	painter->save();
	painter->translate(0, qRound(10.0f * dScale));
	DesktopPainter::drawTopWeatherInfo(painter, dayIndex, appletContentsRect, true);
	painter->restore();
	
	if (stateMachine()->currentPage() == Yawp::DetailsPage)
	{
		drawDetailsHeader(painter, dayIndex, DesktopPainter::getDetailsHeaderRect(appletContentsRect));
		drawDetails(painter, dayIndex, DesktopPainter::getDetailsContentsRect(appletContentsRect));
	}
	else
	{
		DesktopPainter::drawForecastHeader(painter, DesktopPainter::getDetailsHeaderRect(appletContentsRect), showDayNames);
		DesktopPainter::drawForecast(painter, DesktopPainter::getDetailsContentsRect(appletContentsRect), true);
	}
}

void
ExtendedDesktopPainter::drawForecastText(QPainter * painter, const QRect & forecastContentRect, const qreal dFontScale) const
{
	const CityWeather * currentCity = stateMachine()->currentCity();
	if (currentCity == 0 || currentCity->days().count() == 0)
		return;
	
	int columnIndex = 1;
	int columnMaxWidth = 0;
	int columnOffset = 0;
	int totalTextHeight = 0;
	int columnSpacer = 0;
	
	painter->save();
	painter->setBrush(configData()->forecastTextColor);
	painter->setPen(configData()->forecastTextColor);
	
	QFont font(painter->font());
	font.setPointSize(qRound(11.5f * dFontScale));
	font.setBold(false);
	QFontMetrics fontMetrics(font);
	

	int dayIndex = 0;
	while (dayIndex < currentCity->days().count())
	{
		const YawpDay * day = currentCity->days().at(dayIndex);
		const QString headerText = Utils::CreateForecastTextHeader(*day);
		const QString bodyText = Utils::CreateForecastTextBody(*day, dayIndex == 0, dayIndex == 0, QString(QChar(QChar::LineSeparator)));
		
		// get the size of the header text, that will be needed to draw it
		font.setUnderline(true);
		painter->setFont(font);
		QRect headerRect = painter->boundingRect(
			forecastContentRect.left() + columnOffset,
			forecastContentRect.top() + totalTextHeight,
			columnIndex == 1 ? forecastContentRect.width() : columnMaxWidth,
			forecastContentRect.height(),
			Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap,
			headerText);
		
		// get the size of the body text, that will be needed to draw it
		font.setUnderline(false);
		painter->setFont(font);
		QRect bodyRect = painter->boundingRect(
			forecastContentRect.left() + columnOffset,
			forecastContentRect.top() + totalTextHeight,
			columnIndex == 1 ? forecastContentRect.width() : columnMaxWidth,
			forecastContentRect.height(),
			Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap,
			bodyText);
		
		// check if we have enough space to draw the forecast text
		if (headerRect.height() + bodyRect.height() <= forecastContentRect.height() - totalTextHeight &&
		    headerRect.width() <= forecastContentRect.width() - columnOffset &&
		    bodyRect.width() <= forecastContentRect.width() - columnOffset)
		{
			font.setUnderline(true);
			painter->setFont(font);
			painter->drawText(
				forecastContentRect.left() + columnOffset,
				forecastContentRect.top() + totalTextHeight,
				columnIndex == 1 ? forecastContentRect.width() : columnMaxWidth,
				forecastContentRect.height(),
				Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap,
				headerText);
			
			totalTextHeight += headerRect.height();
			
			font.setUnderline(false);
			painter->setFont(font);
			painter->drawText(
				forecastContentRect.left() + columnOffset,
				forecastContentRect.top() + totalTextHeight,
				columnIndex == 1 ? forecastContentRect.width() : columnMaxWidth,
				forecastContentRect.height(),
				Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap,
				bodyText);
			
			totalTextHeight += bodyRect.height() + fontMetrics.height();

			if (columnIndex == 1)
			{
				columnMaxWidth = qMax(columnMaxWidth, headerRect.width());
				columnMaxWidth = qMax(columnMaxWidth, bodyRect.width());
			}
			dayIndex += 1;
		}
		// check if we have enough space to add another column with forecast days
		else if (forecastContentRect.width() - columnOffset - columnMaxWidth - qRound(dFontScale * 15.0f) >= columnMaxWidth)
		{
			columnSpacer = qRound(dFontScale * 15.0f);
			columnIndex += 1;
			
			columnOffset += columnMaxWidth + columnSpacer;
			totalTextHeight = 0;
		}
		else
			break;
	}
	
	painter->restore();
}

void
ExtendedDesktopPainter::triggerWeatherIconAnimation(int dayIndex)
{
	if ((stateMachine()->currentPage() == Yawp::DetailsPage && dayIndex == stateMachine()->detailsDayIndex()) ||
	    (stateMachine()->currentPage() == Yawp::PreviewPage && dayIndex == 0))
	{
		QRect appletContentsRect = DesktopPainter::getContentsRect(m_miniAppletRectangle);
		initWeatherIconChange(getTodaysWeatherIconRect(appletContentsRect), dayIndex);
	}
	else
		emit signalToggleWeatherIcon(dayIndex);
}

void
ExtendedDesktopPainter::initMiniAppletPageChange(
	const QRect & appletContentsRect,
	int newDayIndex,
	Yawp::PageType pageType)
{
	int dayIndex = stateMachine()->detailsDayIndex();
	PageAnimator::Transition transition = AbstractPainter::getSlidingTransition(configData()->pageAnimation, newDayIndex > dayIndex);
	
	if (transition != PageAnimator::Jump)
	{
		pageAnimator()->resetPages(appletContentsRect.width(), appletContentsRect.height());

		QPainter painter;
		painter.begin(&pageAnimator()->vPages[0]);
		painter.translate(-appletContentsRect.left(), -appletContentsRect.top());
		
		drawMiniApplet(&painter, appletContentsRect, m_bShowForecastHeaderDayNames);
		
		painter.end();
	}
	
	stateMachine()->setDetailsDayIndex(newDayIndex);
	stateMachine()->setCurrentPage(pageType);
	
	if (transition != PageAnimator::Jump)
	{
		QPainter painter;
		painter.begin(&pageAnimator()->vPages[1]);
		painter.translate(-appletContentsRect.left(), -appletContentsRect.top());
		
		drawMiniApplet(&painter, appletContentsRect, m_bShowForecastHeaderDayNames);
		
		painter.end();
		
		//--- setup animation ---
		setAnimationType(AbstractPainter::MiniAppletPageAnimation);
		pageAnimator()->setTransition(transition);
		setAnimationRect(appletContentsRect);

		updatePixmap(false);

		timeLine()->start();
	}
	else
		//--- When no animation has been configured than just print the new applet state ---
		updatePixmap();
}

QRect
ExtendedDesktopPainter::getPageButtonRect(const QRect & headerRect, Yawp::PageType pageType) const
{
	int button = pageType == Yawp::DetailsPage ? 1 : 0;

	return QRect(qRound(headerRect.left() + (headerRect.height() * button)),
	             qRound(headerRect.top()),
	             headerRect.height(),
	             headerRect.height());
}

QRect
ExtendedDesktopPainter::getCityButtonRect(const QRect & headerRect, bool previous) const
{
	QRect button(qRound(headerRect.left() + (2.5 * (qreal)headerRect.height())),
	              qRound(headerRect.top()),
	              headerRect.height(),
	              headerRect.height());
	if (!previous)
		button.moveRight(headerRect.right());
	return button;
}

QRect
ExtendedDesktopPainter::getTodaysWeatherIconRect(const QRect & appletContentsRect) const
{
	const qreal dScale = appletContentsRect.height() / YAWP_ORIGINAL_HEIGHT;
	QRect todaysWeatherIconRect = DesktopPainter::getTodaysWeatherIconRect(appletContentsRect);
	return todaysWeatherIconRect.adjusted(0, qRound(12.0f * dScale), 0, qRound(8.0f * dScale));
}

void
ExtendedDesktopPainter::calculateLayout(
	const QRect & contentsRect,
	QRect & headerRectangle,
	QRect & miniAppletRectangle,
	QRect & forecastTextRectangle,
	QRect & satelliteImageRectangle)
{
	const qreal dScale = calculateLayoutScalingFactor(contentsRect);
	const qreal dHeaderSpace = 11.0 * dScale;
	const qreal dCellSpace = 14.0 * dScale;
		
	headerRectangle = QRect(contentsRect.left(),
				contentsRect.top(),
				contentsRect.width() - 1,
				qRound((EXTENDED_LAYOUT_HEADER_OFFSET - 1.0) * YAWP_ORIGINAL_HEIGHT * dScale) - dCellSpace);
	
	miniAppletRectangle = QRect(contentsRect.left(),
				    qRound(headerRectangle.bottom() + dHeaderSpace),
				    qRound(YAWP_ORIGINAL_WIDTH * dScale),
				    qRound(YAWP_ORIGINAL_HEIGHT * dScale));
	
	satelliteImageRectangle = miniAppletRectangle;
	satelliteImageRectangle.moveRight(contentsRect.right()-1);
	
	forecastTextRectangle = QRect(qRound(miniAppletRectangle.right() + dCellSpace),
				      miniAppletRectangle.top(),
				      qRound(contentsRect.width() - 2.0 * (miniAppletRectangle.width() + dCellSpace)),
				      contentsRect.height() - (miniAppletRectangle.top() - headerRectangle.top()));
}

qreal
ExtendedDesktopPainter::calculateLayoutScalingFactor(const QRect & contentsRect)
{
	const qreal appletContentsRatio = (qreal)contentsRect.width() / (qreal)contentsRect.height();
	
	// we assume that we need at least half the applet width for forecast text
	// mini applet and satellite image will be treated equal and receive the same width
	const qreal worstCaseRatio = (EXTENDED_LAYOUT_APPLET_WIDTH * YAWP_ORIGINAL_WIDTH) / (YAWP_ORIGINAL_HEIGHT * EXTENDED_LAYOUT_HEADER_OFFSET);
	
	double dScale = 0.0;
	if (appletContentsRatio >= worstCaseRatio)
	{
		// we have more width as needed
		double appletHeight = contentsRect.height() / EXTENDED_LAYOUT_HEADER_OFFSET;
		dScale = appletHeight / YAWP_ORIGINAL_HEIGHT;
	}
	else
	{
	      // we have more height as needed
	      double appletWidth = contentsRect.width() / EXTENDED_LAYOUT_APPLET_WIDTH;
	      dScale = appletWidth / YAWP_ORIGINAL_WIDTH;
	}

	return dScale;
}
