/********************************************************************************
 *   Copyright (C) 2008-2009 by Bram Schoenmakers <bramschoenmakers@kde.nl>     *
 *                                                                              *
 *   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 .             *
 ********************************************************************************/

#include <QAction>
#include <QGroupBox>
#include <QPainter>
#include <QTimeLine>

#include <KConfigDialog>
#include <KMessageBox>
#include <KRun>
#include <Plasma/PaintUtils>
#include <Plasma/Theme>

#include "clusterdialog.h"
#include "photosetdialog.h"
#include "../engine/flickr_engine.h"

// Include the correct coordinate dialog.
#ifdef WITH_MARBLE
#include "marbledialog.h"
#else
#include "coordinatedialog.h"
#endif

#include "flickrop.h"

using namespace Plasma;

#if QT_VERSION < 0x040500
// this Plasma:: should stay here, otherwise it breaks the applet!
Q_DECLARE_METATYPE( Plasma::DataEngine::Data )
#endif

FlickrOP::FlickrOP( QObject* p_parent, const QVariantList& p_args )
: Applet( p_parent, p_args )
, m_engine( 0 )
, m_timeline( 0 )
, m_alpha( 1.0 )
, m_authenticated( false )
, m_cluster_chosen( false )
, m_location_chosen( false )
, m_general_page( 0 )
, m_stream_page( 0 )
, m_authentication_page( 0 )
, m_visitAuthor( 0 )
, m_visitPhotoPage( 0 )
, m_visitAuthorPhotos( 0 )
, m_config_dialog( 0 )
, m_round_corners( false )
, m_show_background( true )
, m_soft_borders( false )
{
  KGlobal::locale()->insertCatalog("flickrop");
  
  setBackgroundHints( Applet::NoBackground );
  setAspectRatioMode( KeepAspectRatio );
  setHasConfigurationInterface( true );
}

FlickrOP::~FlickrOP()
{
  KConfigGroup cfg = config();

  cfg.writeEntry( "token", m_config["token"] );

  delete m_clusterdialog;
  delete m_photosetdialog;
  delete m_locationdialog;

  delete m_general_page;
  delete m_stream_page;
  delete m_authentication_page;
}

void FlickrOP::init()
{
  m_timeline = new QTimeLine( 1000, this );
  m_timeline->setCurveShape( QTimeLine::EaseInCurve );
  connect( m_timeline, SIGNAL( valueChanged( qreal ) ), SLOT( setAlpha( qreal ) ) );

  m_nextPhoto = new QAction( SmallIcon("system-run"), i18n("Ne&xt Photo"), this );
  m_nextPhoto->setEnabled( false );
  m_actions.append( m_nextPhoto );
  connect( m_nextPhoto, SIGNAL( triggered( bool ) ), SLOT( nextPhoto() ) );

  m_markFavorite = new QAction( SmallIcon("emblem-favorite"), i18n("Mark As &Favorite"), this );
  m_markFavorite->setEnabled( false );
  m_actions.append( m_markFavorite );
  connect( m_markFavorite, SIGNAL( triggered( bool ) ), SLOT( markAsFavorite() ) );

  QAction *separator = new QAction( this );
  separator->setSeparator( true );
  m_actions.append( separator );

  m_visitAuthor = new QAction( SmallIcon("user-identity"), i18n("Visit &Author's Profile"), this );
  m_visitAuthor->setEnabled( false );
  m_actions.append( m_visitAuthor );
  connect( m_visitAuthor, SIGNAL( triggered( bool ) ), SLOT( visitAuthor() ) );

  m_visitAuthorPhotos = new QAction( SmallIcon("camera-photo"), i18n("Visit Aut&hor's Photos"), this );
  m_visitAuthorPhotos->setEnabled( false );
  m_actions.append( m_visitAuthorPhotos );
  connect( m_visitAuthorPhotos, SIGNAL( triggered( bool ) ), SLOT( visitAuthorPhotos() ) );

  m_visitPhotoPage = new QAction( SmallIcon("internet-web-browser"), i18n("Visit &Photo Page"), this );
  m_visitPhotoPage->setEnabled( false );
  m_actions.append( m_visitPhotoPage );
  connect( m_visitPhotoPage, SIGNAL( triggered( bool ) ), SLOT( visitPhoto() ) );

  separator = new QAction( this );
  separator->setSeparator( true );
  m_actions.append( separator );

  m_engine = dataEngine("flickrop");
  m_engine->connectAllSources( this );

  m_config["force_reload"] = true;
  m_config["check_token"] = true;
  readConfig(); // will also fire the engine
}

void FlickrOP::paintInterface ( QPainter *p_painter, const QStyleOptionGraphicsItem *p_option, const QRect &p_contentsRect )
{
  setBackgroundHints( m_show_background ? Applet::TranslucentBackground : Applet::NoBackground );
  if ( m_pixmapCache.isNull() || p_contentsRect.size() != m_pixmapCache.size() )
  {
    paintCache( p_option, p_contentsRect );
  }

  if ( !m_oldPixmapCache.isNull() )
  {
    p_painter->setOpacity( m_alpha );
    p_painter->drawPixmap( p_contentsRect, m_pixmapCache, p_contentsRect );
    p_painter->setOpacity( 1.0 - m_alpha );
    p_painter->drawPixmap( p_contentsRect, m_oldPixmapCache, p_contentsRect );
  }
  else
  {
    p_painter->drawPixmap( p_contentsRect, m_pixmapCache, p_contentsRect );
  }
}

void FlickrOP::paintCache ( const QStyleOptionGraphicsItem *p_option, const QRect &p_contentsRect )
{
  Q_UNUSED( p_option );

  m_pixmapCache = QPixmap( p_contentsRect.size() );
  m_pixmapCache.fill( Qt::transparent );
  QPainter painter( &m_pixmapCache );

  if( !m_error.isEmpty() )
  {
    painter.drawText( p_contentsRect, Qt::AlignCenter, m_error );
  }
  else if( !m_photo.filename.isNull() )
  {
    // photo
    int y = p_contentsRect.y();
    int width;
    int height;
    if ( m_pixmapPhoto.width() > m_pixmapPhoto.height() )
    {
      width = qMin( p_contentsRect.width(), m_pixmapPhoto.width() );
      height = (int)( width * m_pixmapPhoto.height() / m_pixmapPhoto.width() );
      y += (int)((p_contentsRect.height() * 0.5 ) - ( height * 0.5 ) );
    }
    else // height <= width
    {
      height = qMin( p_contentsRect.height(), m_pixmapPhoto.height() );
      width = (int)( height * m_pixmapPhoto.width() / m_pixmapPhoto.height() );
      if ( !m_show_author )
      {
        // if the author isn't shown, we can really center the image vertically
        y += (int)((p_contentsRect.height() * 0.5 ) - ( height * 0.5 ) );
      }
    }
    int x = p_contentsRect.x() + (int)((p_contentsRect.width() * 0.5 ) - ( width * 0.5 ) );

    QRect photoRect = QRect( x, y, width, height );

    if ( m_show_author )
    {
      // text
      QString text;
      if ( m_photo.title.isEmpty() )
      {
        text = i18nc( "author", "By %1", m_photo.author.username );
      }
      else if ( m_photo.author.username.isEmpty() )
      {
        text = m_photo.title;
      }
      else
      {
        text = i18nc( "title and author", "\"%1\" by %2", m_photo.title, m_photo.author.username );
      }
      painter.setPen( m_author_color );
      painter.setFont( m_author_font );

      QRect textArea( x, y + height, width, p_contentsRect.bottom() - y - height );
      QRect author_boundbox = painter.boundingRect( textArea, Qt::AlignRight | Qt::TextWordWrap, text );
      painter.drawText( author_boundbox, Qt::AlignRight | Qt::TextWordWrap, text );
    }

    painter.setRenderHint( QPainter::Antialiasing );
    painter.setRenderHint( QPainter::SmoothPixmapTransform );

    painter.save();
    QPainterPath roundPath;
    if ( m_round_corners )
    {
      roundPath = Plasma::PaintUtils::roundedRectangle( photoRect, (int)(size().width() * 0.03) );
      // it sucks that Qt does not antialias the corners when clipping, it looks a bit rubbish
      // if soft borders are not set, it will be covered with a black line, drawn below
      painter.setClipPath( roundPath );
    }

    painter.drawPixmap( photoRect, m_pixmapPhoto );
    painter.restore();

    if ( m_soft_borders )
    {
      painter.save();
      painter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
      painter.drawPixmap( photoRect, createMask( photoRect.size() ) );
      painter.restore();
    }

    if ( !m_soft_borders && m_round_corners )
    {
      painter.setPen(QPen(Qt::black, 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
      painter.drawPath( roundPath );
    }
  }
}

QPixmap FlickrOP::createMask( const QSize &p_size )
{
  QPixmap pixmap( p_size );
  pixmap.fill( Qt::transparent );

  QPainter painter( &pixmap );

  painter.setBrush( Qt::NoBrush );
  painter.setRenderHint( QPainter::Antialiasing, false );
  qreal opacity = 0.0;
  qreal step = 1.0 / ( size().width() * 0.01 );
  QRectF rect = pixmap.rect();

  // draw gradient
  while ( opacity < 1.0 )
  {
    painter.setOpacity( opacity );
    painter.drawRect( rect );
    opacity += step;
    rect.adjust( 1.0, 1.0, -1.0, -1.0 );
  }

  // fill rectangle
  painter.setBrush( Qt::black );
  painter.drawRect( rect );

  painter.end();
  return pixmap;
}

void FlickrOP::readConfig()
{
  KConfigGroup cfg = config();

  m_config["interval"] = cfg.readEntry( "interval", 3600 );
  m_round_corners = cfg.readEntry( "roundcorners", false );
  m_show_background = cfg.readEntry( "showbackground", true );
  m_soft_borders = cfg.readEntry( "softborders", false );

  QString token = cfg.readEntry( "token", "" );
  m_config["token"] = token;
  // don't check for token validity if there is none
  if ( token.isEmpty() )
  {
    m_config["check_token"] = false;
  }

  m_show_author = cfg.readEntry( "show_author", true );
  m_author_font = cfg.readEntry( "author_font", KGlobalSettings::generalFont() );
  QFontMetrics fm( m_author_font );
  m_font_height = fm.height();
  m_author_color = cfg.readEntry( "author_color", Theme::defaultTheme()->color( Theme::TextColor ) );
  QString size = cfg.readEntry( "size", "" );
  m_config["size"] = size;
  setSize( size );
  m_config["listtype"] = cfg.readEntry( "listtype", "interestingness" );

  // photosets
  m_config["photoset-id"] = cfg.readEntry( "photoset-id", "" );
  m_config["photoset-username"] = cfg.readEntry( "photoset-username", "" );
  m_config["photoset-nsid"] = cfg.readEntry( "photoset-nsid", "" );
  m_photoset_chosen = !m_config["photoset-id"].toString().isEmpty();

  // tags
  m_config["tag"] = cfg.readEntry( "tag", "" );
  m_config["cluster-id"] = cfg.readEntry( "cluster-id", "" );
  m_cluster_chosen = !m_config["cluster-id"].toString().isEmpty();

  // location
  m_config["location-lon"] = cfg.readEntry( "location-lon", 9.05747 );
  m_config["location-lat"] = cfg.readEntry( "location-lat", 48.5331 );
  int accuracy = cfg.readEntry( "location-accuracy", 11 ); // default to city level
  m_config["location-accuracy"] = accuracy;
  accuracy = ( accuracy < 1 || accuracy > 16 ) ? 11 : accuracy; // normalize
  m_location_chosen = true;

  sendConfigurationToEngine();
  setBackgroundHints( m_show_background ? Applet::TranslucentBackground : Applet::NoBackground );
  resetPixmapCache();
  update();
}

void FlickrOP::sendConfigurationToEngine()
{
#if QT_VERSION < 0x040500
  m_engine->setProperty( "config", QVariant::fromValue( m_config ) );
#else
  m_engine->setProperty( "config", m_config );
#endif

  m_config["force_reload"] = false;
  m_config["check_token"] = false;
}

void FlickrOP::writeConfig()
{
  KConfigGroup cfg = config();

  int index = m_general_page->m_interval_combo->currentIndex();
  int unit = m_general_page->m_interval_combo->itemData( index ).toInt();
  int interval = m_general_page->m_interval_value->value() * unit;

  cfg.writeEntry( "interval", interval );
  cfg.writeEntry( "roundcorners", m_general_page->m_round_corner_check->isChecked() );
  cfg.writeEntry( "showbackground", m_general_page->m_show_background_check->isChecked() );
  cfg.writeEntry( "softborders", m_general_page->m_soft_borders_check->isChecked() );

  QString newsize = m_general_page->m_size_combo->itemData( m_general_page->m_size_combo->currentIndex() ).toString();
  cfg.writeEntry( "size", newsize );

  cfg.writeEntry( "show_author", m_general_page->m_show_author_check->isChecked() );
  cfg.writeEntry( "author_font", m_general_page->m_author_font_req->font() );
  cfg.writeEntry( "author_color", m_general_page->m_colorbutton->color() );

  QString listtype;
  QString clusterid;
  QString photosetid;
  qreal longitude = m_config["location-lon"].toDouble();
  qreal latitude = m_config["location-lat"].toDouble();
  int accuracy = m_config["location-accuracy"].toInt();
  if ( m_stream_page->m_favorite_radio->isChecked() )
  {
    listtype = "favorites";
  }
  else if ( m_stream_page->m_tag_radio->isChecked() && m_cluster_chosen )
  {
    listtype = "tag";
  }
  else if ( m_stream_page->m_photoset_radio->isChecked() && m_photoset_chosen )
  {
    listtype = "photoset";
  }
  else if ( m_stream_page->m_location_radio->isChecked() && m_location_chosen )
  {
    listtype = "location";
  }
  else
  {
    listtype = "interestingness";
  }

  cfg.writeEntry( "listtype", listtype );

  if ( m_photoset_chosen )
  {
    photosetid = m_photosetdialog ? m_photosetdialog->photoset() : m_config["photoset-id"].toString();
    cfg.writeEntry( "photoset-id", photosetid );
    cfg.writeEntry( "photoset-username", m_stream_page->m_photoset_edit->text() );
    cfg.writeEntry( "photoset-nsid", m_nsid );
  }

  if ( m_cluster_chosen )
  {
    cfg.writeEntry( "tag", m_stream_page->m_tag_edit->text() );
    clusterid = m_clusterdialog ? m_clusterdialog->cluster() : m_config["cluster-id"].toString();
    cfg.writeEntry( "cluster-id", clusterid );
  }

  if ( m_location_chosen )
  {
    longitude = m_locationdialog ? m_locationdialog->getLongitude() : m_config["location-lon"].toDouble();
    latitude = m_locationdialog ? m_locationdialog->getLatitude() : m_config["location-lat"].toDouble();
    accuracy = m_locationdialog ? m_locationdialog->getAccuracy() : m_config["location-accuracy"].toInt();
    cfg.writeEntry( "location-lon", longitude );
    cfg.writeEntry( "location-lat", latitude );
    cfg.writeEntry( "location-accuracy", accuracy );
  }

  cfg.writeEntry( "token", m_config["token"] );

  bool forceReload = ( m_config["listtype"] != listtype
      || newsize != m_config["size"].toString()
      || m_config["cluster-id"].toString() != clusterid
      || m_config["photoset-id"].toString() != photosetid
      || m_config["location-lon"].toDouble() != longitude
      || m_config["location-lat"].toDouble() != latitude
      || m_config["location-accuracy"].toInt() != accuracy );
  m_config[ "force_reload" ] = forceReload;
  if ( forceReload )
  {
    m_nextPhoto->setEnabled( false );
  }
}

// http://www.flickr.com/services/api/misc.urls.html
// s	small square 75x75
// t	thumbnail, 100 on longest side
// m	small, 240 on longest side
// -	medium, 500 on longest side
// b	large, 1024 on longest side (only exists for very large original images)
// o	original image, either a jpg, gif or png, depending on source format
void FlickrOP::setSize( const QString &p_size )
{
  // some margin
  const int mw = 30;
  const int mh = 30 + ( m_show_author ? 2 * m_font_height : 0 );

  if ( p_size == "s" )
  {
    resize( 75 + mw, 75 + mh );
  }
  else if ( p_size == "t" )
  {
    resize( 100 + mw, 100 + mh );
  }
  else if ( p_size == "m" )
  {
    resize( 240 + mw, 240 + mh );
  }
  else if ( p_size.isEmpty() )
  {
    resize( 500 + mw, 500 + mh );
  }
  else if ( p_size == "b" )
  {
    resize( 1024 + mw, 1024 + mh );
  }
  // TODO: Original format
}

void FlickrOP::createConfigurationInterface( KConfigDialog *p_parent )
{
  // we need this in order to enable, disable the OK/Apply buttons from slots
  // in writeConfig and readConfig its already too late
  m_config_dialog = p_parent;
  m_config_dialog->setMinimumWidth( 550 );

  p_parent->setButtons( KDialog::Ok | KDialog::Cancel | KDialog::Apply );
  connect( p_parent, SIGNAL( applyClicked() ), SLOT( writeConfig() ) );
  connect( p_parent, SIGNAL( applyClicked() ), SLOT( readConfig() ) );
  connect( p_parent, SIGNAL( okClicked() ), SLOT( writeConfig() ) );
  connect( p_parent, SIGNAL( okClicked() ), SLOT( readConfig() ) );

  m_general_page = new Ui::GeneralWidget;
  m_stream_page = new Ui::StreamWidget;
  m_authentication_page = new Ui::AuthenticationWidget;

  QWidget *page = new QWidget();
  m_general_page->setupUi( page );
  p_parent->addPage( page, i18n( "General" ), "configure" );

  page = new QWidget();
  m_stream_page->setupUi( page );
  p_parent->addPage( page, i18n( "Streams" ), "camera-photo" );

  page = new QWidget();
  m_authentication_page->setupUi( page );
  p_parent->addPage( page, i18n( "Authentication" ), "preferences-desktop-user-password" );

  // read settings and set widgets accordingly
  setupWidgets();
}

void FlickrOP::setupWidgets()
{
  const int minute = 60;
  const int hour = 3600;
  const int day = 86400;

  m_general_page->m_interval_combo->setItemData( 0, 1 );      //seconds
  m_general_page->m_interval_combo->setItemData( 1, minute ); // minutes
  m_general_page->m_interval_combo->setItemData( 2, hour );   // hours
  m_general_page->m_interval_combo->setItemData( 3, day );    // days

  int interval = m_config.value("interval").toInt();
  if ( interval > 0 && interval % day == 0 )
  {
    m_general_page->m_interval_value->setValue( interval / day );
    m_general_page->m_interval_combo->setCurrentIndex( 3 );
  }
  else if ( interval > 0 && interval % hour == 0 )
  {
    m_general_page->m_interval_value->setValue( interval / hour );
    m_general_page->m_interval_combo->setCurrentIndex( 2 );
  }
  else if ( interval > 0 && interval % minute == 0 )
  {
    m_general_page->m_interval_value->setValue( interval / minute );
    m_general_page->m_interval_combo->setCurrentIndex( 1 );
  }
  else
  {
    // seconds
    m_general_page->m_interval_value->setValue( interval );
    m_general_page->m_interval_combo->setCurrentIndex( 0 );
  }

  m_general_page->m_size_combo->setItemData( 0, "s" );
  m_general_page->m_size_combo->setItemData( 1, "t" );
  m_general_page->m_size_combo->setItemData( 2, "m" );
  m_general_page->m_size_combo->setItemData( 3, "" );
  m_general_page->m_size_combo->setItemData( 4, "b" );

  m_general_page->m_size_combo->setCurrentIndex( m_general_page->m_size_combo->findData( m_config["size"].toString() ) );

  m_general_page->m_show_background_check->setChecked( m_show_background );
  m_general_page->m_round_corner_check->setChecked( m_round_corners );
  m_general_page->m_soft_borders_check->setChecked( m_soft_borders );

  m_general_page->m_author_font_req->setFont( m_author_font );
  m_general_page->m_colorbutton->setColor( m_author_color );
  m_general_page->m_show_author_check->setChecked( m_show_author );
  m_general_page->m_font_label->setEnabled( m_show_author );
  m_general_page->m_author_font_req->setEnabled( m_show_author );
  m_general_page->m_font_color_label->setEnabled( m_show_author );
  m_general_page->m_colorbutton->setEnabled( m_show_author );
  connect( m_general_page->m_show_author_check, SIGNAL( toggled( bool ) ), m_general_page->m_font_label, SLOT( setEnabled( bool ) ) );
  connect( m_general_page->m_show_author_check, SIGNAL( toggled( bool ) ), m_general_page->m_author_font_req, SLOT( setEnabled( bool ) ) );
  connect( m_general_page->m_show_author_check, SIGNAL( toggled( bool ) ), m_general_page->m_font_color_label, SLOT( setEnabled( bool ) ) );
  connect( m_general_page->m_show_author_check, SIGNAL( toggled( bool ) ), m_general_page->m_colorbutton, SLOT( setEnabled( bool ) ) );

  // streams page
  connect( m_stream_page->m_interesting_radio, SIGNAL( toggled( bool ) ), this, SLOT( radioChanged() ) );
  connect( m_stream_page->m_favorite_radio, SIGNAL( toggled( bool ) ), this, SLOT( radioChanged() ) );
  connect( m_stream_page->m_photoset_radio, SIGNAL( toggled( bool ) ), this, SLOT( radioChanged() ) );
  connect( m_stream_page->m_tag_radio, SIGNAL( toggled( bool ) ), this, SLOT( radioChanged() ) );
  connect( m_stream_page->m_location_radio, SIGNAL( toggled( bool ) ), this, SLOT( radioChanged() ) );

  m_stream_page->m_get_photoset_button->setEnabled( m_photoset_chosen );
  m_stream_page->m_photoset_edit->setEnabled( false );
  m_stream_page->m_photoset_edit->setEnabled( false );
  m_stream_page->m_photoset_edit->setText( m_config["photoset-username"].toString() );
  connect( m_stream_page->m_get_photoset_button, SIGNAL( clicked( bool ) ), SLOT( getPhotosets() ) );
  connect( m_stream_page->m_photoset_edit, SIGNAL( textChanged( const QString & ) ), SLOT( photosetUserChanged( const QString & ) ) );
  connect( m_stream_page->m_photoset_edit, SIGNAL( textEdited( const QString & ) ), SLOT( photosetUserEdited( const QString & ) ) );
  connect( m_stream_page->m_photoset_edit, SIGNAL( returnPressed() ), SLOT( getPhotosets() ) );

  connect( m_stream_page->m_get_cluster_button, SIGNAL( clicked( bool ) ), SLOT( getClusters() ) );
  m_stream_page->m_get_cluster_button->setEnabled( m_cluster_chosen );
  connect( m_stream_page->m_tag_edit, SIGNAL( textChanged( const QString & ) ), SLOT( tagTextChanged( const QString & ) ) );
  connect( m_stream_page->m_tag_edit, SIGNAL( textEdited( const QString & ) ), SLOT( tagTextEdited( const QString & ) ) );
  connect( m_stream_page->m_tag_edit, SIGNAL( returnPressed() ), SLOT( getClusters() ) );
  m_stream_page->m_tag_edit->setEnabled( false );

  connect( m_stream_page->m_get_location_button, SIGNAL( clicked( bool ) ), SLOT( showMarbleDialog() ) );

  QString type = m_config["listtype"].toString();
  m_stream_page->m_interesting_radio->setChecked( type == "interestingness" );
  m_stream_page->m_favorite_radio->setChecked( type == "favorites" );
  m_stream_page->m_photoset_radio->setChecked( type == "photoset" );
  m_stream_page->m_photoset_edit->setEnabled( type == "photoset" );
  m_stream_page->m_tag_radio->setChecked( type == "tag" );
  m_stream_page->m_tag_edit->setEnabled( type == "tag" );
  m_stream_page->m_location_radio->setChecked( type == "location" );
  m_stream_page->m_get_location_button->setEnabled( type == "location" );
  m_stream_page->m_tag_edit->setText( m_config["tag"].toString() );

  // authentication
  m_authentication_page->m_auth_button->setEnabled( !m_authenticated );
  connect( m_authentication_page->m_auth_button, SIGNAL( clicked( bool ) ), SLOT( authenticate() ) );
}

QList<QAction *> FlickrOP::contextualActions()
{
  return m_actions;
}

void FlickrOP::dataUpdated( const QString &p_sourceName, const DataEngine::Data &p_data )
{
  if ( p_sourceName == "error" )
  {
    m_error.clear();
    int type = p_data.value( "type" ).toInt();
    QString message = p_data.value( "message" ).toString();
    if ( type == ERR_APPLET )
    {
      // show on applet
      m_error = message;
      update();
    }
    else if ( type == ERR_MESSAGEBOX )
    {
      // delay it, such that we leave this function before it is called again
      QMetaObject::invokeMethod( this, "showErrorBox", Qt::QueuedConnection, Q_ARG( QString, message ) );
    }
    else if ( !message.isEmpty() )
    {
      kDebug() << message << endl;
    }
  }
  else if ( p_sourceName == "authentication" )
  {
    m_authenticated = p_data.value( "token_status" ).toBool();
    m_config["token"] = p_data.value( "token" ).toString();
  }
  else if ( p_sourceName == "clusters" )
  {
    if ( m_clusterdialog )
    {
      m_clusterdialog->reset();
    }
    foreach( const QVariant &variant, p_data )
    {
      // filter out noise
      if ( variant.type() != QVariant::StringList )
      {
        continue;
      }
      if ( !m_clusterdialog )
      {
        m_clusterdialog = new ClusterDialog();
        connect( m_clusterdialog, SIGNAL( accepted() ), SLOT( clusterChosen() ) );
        connect( m_config_dialog, SIGNAL( okClicked() ), m_clusterdialog, SLOT( deleteLater() ) );
      }
      m_clusterdialog->addCluster( variant.toStringList() );
    }

    QTimer::singleShot( 0, this, SLOT( showClusterDialog() ) );
  }
  else if ( p_sourceName == "nsid" && !p_data["nsid"].toString().isEmpty() )
  {
    m_nsid = p_data["nsid"].toString();
    QMetaObject::invokeMethod( m_engine, "retrievePhotosets", Qt::DirectConnection, Q_ARG( QString, m_nsid ) );
  }
  else if ( p_sourceName == "photosets" )
  {
    if ( !p_data[ p_sourceName ].canConvert( QVariant::String ) )
    {
      if ( !m_photosetdialog )
      {
        m_photosetdialog = new PhotosetDialog();
        connect( m_photosetdialog, SIGNAL( accepted() ), SLOT( photosetChosen() ) );
        connect( m_config_dialog, SIGNAL( okClicked() ), m_photosetdialog, SLOT( deleteLater() ) );
      }
      m_photosetdialog->reset();
      m_photosetdialog->addPhotosets( p_data );
      QTimer::singleShot( 0, this, SLOT( showPhotosetDialog() ) );
    }
  }
  else if ( p_sourceName == "flickr" && QString( p_data["flickr"].typeName() ) == "Photo" ) // flickr stream
  {
    m_error.clear();
    m_photo = p_data.value( "flickr" ).value<Photo>();
    m_pixmapPhoto.load( m_photo.filename, "JPG" );
    resetPixmapCache();
    m_nextPhoto->setEnabled( true );
    m_visitAuthor->setEnabled( true );
    m_visitPhotoPage->setEnabled( true );
    m_visitAuthorPhotos->setEnabled( true );
    m_markFavorite->setEnabled( m_authenticated );
    if ( m_oldPixmapCache.isNull() )
    {
      m_alpha = 1.0;
      update();
    }
    else
    {
      // there's an old pixmap available, do a nice fadeout
      m_timeline->start();
    }
  }
}

void FlickrOP::showErrorBox( const QString &p_message )
{
  KMessageBox::error( 0, p_message );
}

void FlickrOP::nextPhoto()
{
  m_nextPhoto->setEnabled( false );
  QMetaObject::invokeMethod( m_engine, "nextPhoto", Qt::DirectConnection );
}

void FlickrOP::visitAuthor()
{
  KRun::runUrl( m_photo.author.profileUrl, "text/html", 0 );
}

void FlickrOP::visitAuthorPhotos()
{
  KRun::runUrl( m_photo.author.photosUrl, "text/html", 0 );
}

void FlickrOP::visitPhoto()
{
  QString nsid = m_photo.author.nsid;
  QString photoid = m_photo.id;
  QString url = QString( "http://www.flickr.com/photos/%1/%2" ).arg( nsid ).arg( photoid );
  KRun::runUrl( KUrl( url ), "text/html", 0 );
}

void FlickrOP::authenticate()
{
  QMetaObject::invokeMethod( m_engine, "startAuthentication", Qt::QueuedConnection );
  if ( KMessageBox::warningContinueCancel( 0, i18n( "A browser will open now with the request to authenticate Flickr On Plasma. When you accepted this request, press Continue." ) ) == KMessageBox::Continue )
  {
    QMetaObject::invokeMethod( m_engine, "continueAuthentication", Qt::DirectConnection );
  }
}

void FlickrOP::markAsFavorite()
{
  QMetaObject::invokeMethod( m_engine, "markAsFavorite", Qt::DirectConnection );
}

void FlickrOP::getClusters()
{
  QMetaObject::invokeMethod( m_engine, "retrieveClusters", Qt::DirectConnection, Q_ARG( QString, m_stream_page->m_tag_edit->text() ) );
}

void FlickrOP::showClusterDialog()
{
  if ( m_clusterdialog )
  {
    m_clusterdialog->exec();
  }
}

void FlickrOP::showPhotosetDialog()
{
  if ( m_photosetdialog )
  {
    m_photosetdialog->exec();
  }
}

void FlickrOP::tagTextChanged( const QString &p_tag )
{
  m_stream_page->m_get_cluster_button->setEnabled( !p_tag.isEmpty() );
}

void FlickrOP::tagTextEdited( const QString &p_tag )
{
  Q_UNUSED( p_tag );
  m_cluster_chosen = false;
  setOkButton();
}

void FlickrOP::radioChanged()
{
  bool photosetsSelected = m_stream_page->m_photoset_radio->isChecked();
  m_stream_page->m_photoset_edit->setEnabled( photosetsSelected );
  m_stream_page->m_get_photoset_button->setEnabled( photosetsSelected );
  bool tagsSelected = m_stream_page->m_tag_radio->isChecked();
  m_stream_page->m_tag_edit->setEnabled( tagsSelected );
  m_stream_page->m_get_cluster_button->setEnabled( tagsSelected && !m_stream_page->m_tag_edit->text().isEmpty() );
  m_stream_page->m_get_location_button->setEnabled( m_stream_page->m_location_radio->isChecked() );
  setOkButton();
}

void FlickrOP::setOkButton()
{
  if ( m_config_dialog )
  {
    m_config_dialog->enableButtonOk(
        ( !m_stream_page->m_tag_radio->isChecked() || m_cluster_chosen )
     && ( !m_stream_page->m_photoset_radio->isChecked() || m_photoset_chosen )
     && ( !m_stream_page->m_location_radio->isChecked() || m_location_chosen ) );
  }
}

void FlickrOP::clusterChosen()
{
  m_cluster_chosen = true;
  setOkButton();
}

void FlickrOP::photosetUserChanged( const QString &p_username )
{
  m_stream_page->m_get_photoset_button->setEnabled( !p_username.isEmpty() );
}

void FlickrOP::photosetUserEdited( const QString &p_username )
{
  Q_UNUSED( p_username );
  m_photoset_chosen = false;
  setOkButton();
}

void FlickrOP::photosetChosen()
{
  m_photoset_chosen = true;
  setOkButton();
}

void FlickrOP::getPhotosets()
{
  QMetaObject::invokeMethod( m_engine, "retrieveNSID", Qt::DirectConnection, Q_ARG( QString, m_stream_page->m_photoset_edit->text() ) );
}

void FlickrOP::showMarbleDialog()
{
  if ( !m_locationdialog )
  {
#ifdef WITH_MARBLE
    m_locationdialog = new MarbleDialog();
#else
    m_locationdialog = new CoordinateDialog();
#endif
    connect( m_locationdialog, SIGNAL( accepted() ), SLOT( locationChosen() ) );
    connect( m_config_dialog, SIGNAL( okClicked() ), m_locationdialog, SLOT( deleteLater() ) );
    m_locationdialog->setLongitude( m_config["location-lon"].toDouble() );
    m_locationdialog->setLatitude( m_config["location-lat"].toDouble() );
    m_locationdialog->setAccuracy( m_config["location-accuracy"].toInt() );
  }
  m_locationdialog->exec();
}

void FlickrOP::locationChosen()
{
  m_location_chosen = true;
  setOkButton();
}

void FlickrOP::setAlpha( qreal p_value )
{
  m_alpha = p_value;
  update();
}

void FlickrOP::resetPixmapCache()
{
  m_oldPixmapCache = m_pixmapCache;
  m_pixmapCache = QPixmap();
}

K_EXPORT_PLASMA_APPLET(flickrop, FlickrOP)
