/***************************************************************************
                          emoticonswidget.cpp -  description
                             -------------------
    begin                : Mon 12 09 2005
    copyright            : (C) 2005 by Diederik van der Boor
    email                : "vdboor" --at-- "codingdomain.com"
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "emoticonswidget.h"

#include "../emoticonmanager.h"
#include "../emoticontheme.h"
#include "../kmessdebug.h"
#include "../utils/kmessshared.h"
#include "../dialogs/addemoticondialog.h"
#include "../accountsmanager.h"
#include "../currentaccount.h"

#include <QAction>
#include <QDockWidget>
#include <QListWidgetItem>
#include <QMenu>
#include <QMouseEvent>
#include <QPoint>



// The constructor
EmoticonsWidget::EmoticonsWidget( QWidget *parent )
 : QWidget(parent)
 , emoticonDialog_(0)
 , initialized_(false)
 , noEmoticonsWarning_(0)
{
  // Get and connect to the emoticon manager. We'll need to update ourselves when the emoticon
  // theme changes
  manager_ = EmoticonManager::instance();
  connect( manager_, SIGNAL(          updated() ),
           this,     SLOT  ( slotThemeUpdated() ) );

  // Create and set the properties for list widget
  list_ = new EmoticonListWidget( this );

  // React to mouse button clicks
  connect( list_ , SIGNAL(       leftClicked(const QListWidgetItem*)               ),
           this,   SLOT  (   slotItemClicked(const QListWidgetItem*)               ) );
  // Enable the right-click context menu
  connect( list_, SIGNAL(       rightClicked(const QListWidgetItem*,const QPoint&) ),
           this,  SLOT  (    showContextMenu(const QListWidgetItem*,const QPoint&) ) );
  connect( list_, SIGNAL(     requestPreview(const QListWidgetItem*)               ),
           this,    SLOT( slotRequestPreview(const QListWidgetItem*)               ) );

  // Set the label for preview
  previewLabel_ = new QLabel( this );
  previewLabel_->setFrameShape( QFrame::StyledPanel );
  previewLabel_->setFrameShadow( QFrame::Sunken );
  previewLabel_->setFixedSize( 48, 48 );
  previewLabel_->setAlignment( Qt::AlignHCenter | Qt::AlignVCenter );

  // Set the movie preview
  previewMovie_ = new QMovie( this );
  previewMovie_->stop();
  previewLabel_->setMovie( previewMovie_ );

  // Create an initial layout for the widget
  layout_ = new QBoxLayout( QBoxLayout::LeftToRight, this );
  layout_->setContentsMargins( 0,0,0,0 );
  layout_->setSpacing( 0 );
  layout_->addWidget( list_ );
  layout_->addWidget( previewLabel_ );

  // Install the event filter for the preview label
  installEventFilter( this );
}



// Constructor for emoticon list widget class
EmoticonListWidget::EmoticonListWidget( QWidget *parent )
  : QListWidget(parent)
  , previousItem_(0)
{
  // Set the list properties
  setViewMode( QListView::IconMode );
  setResizeMode( QListView::Adjust );
  setIconSize( QSize( 16,16 ) );
  setGridSize( QSize( 20,20 ) );
  setUniformItemSizes( true );
  setMovement( QListView::Static );
  setSelectionMode( QAbstractItemView::SingleSelection );
  setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
  setFrameShape( QFrame::NoFrame );
  setStyleSheet( "QListWidget { background-color: transparent; }" );

  // Install event filter
  viewport()->installEventFilter( this );
}



// Preview requested
void EmoticonsWidget::slotRequestPreview( const QListWidgetItem *item )
{
  if( ! initialized_ )
  {
    kWarning() << "Uninitialized usage of EmoticonsWidget!";
    return;
  }

  // Stop the movie and set the new emoticon
  previewMovie_->stop();

  // If the new item is not null set the emoticon to preview
  if( item != 0 )
  {
    previewLabel_->setMovie( previewMovie_ );
    previewMovie_->setFileName( item->data( PictureRole ).toString() );
  }
  else
  {
    // if the item is null, set empty pixmap
    previewLabel_->setPixmap( QPixmap() );
    return;
  }

  // Check if the file format is valid
  if( ! previewMovie_->isValid() )
  {
    return;
  }

  previewMovie_->start();

  // Scale the emoticon if it is larger than its container
  if( previewMovie_->currentPixmap().size().width()  > previewLabel_->size().width()
  ||  previewMovie_->currentPixmap().size().height() > previewLabel_->size().height() )
  {
    previewLabel_->setScaledContents( true );
  }
  else
  {
    previewLabel_->setScaledContents( false );
  }
}



// Reimplement the mouse press event to grep left and right click
void EmoticonListWidget::mousePressEvent( QMouseEvent *e )
{
  // Buttons press event
  QListWidgetItem *item = itemAt( e->pos() );

  if( item == 0 )
  {
    return;
  }

  if( e->button() == Qt::LeftButton )
  {
    emit leftClicked( item );
  }
  else if( e->button() == Qt::RightButton )
  {
    emit rightClicked( item, e->pos() );
  }
}



// Reimplement the event filter
bool EmoticonListWidget::eventFilter( QObject */*obj*/, QEvent *e )
{
  if( e->type() == QEvent::HoverMove )
  {
    // The cursor mouse is moving over the widget
    QListWidgetItem *item = itemAt( static_cast<QHoverEvent*>(e)->pos() );

    // Check if the mouse is over the same emoticon or the selection is changed
    if( item == previousItem_ )
    {
      return false;
    }

    previousItem_ = item;
    emit requestPreview( item );
  }

  return false;
}



// The destructor
EmoticonsWidget::~EmoticonsWidget()
{
  delete emoticonDialog_;
  delete noEmoticonsWarning_;
}



// Event filter for the preview label
bool EmoticonsWidget::eventFilter( QObject *obj, QEvent *event )
{
  Q_UNUSED( obj );

  int type = event->type();

  if( type == QEvent::Hide || type == QEvent::Leave )
  {
    // Stop the preview while the emoticon widget is hidden
    previewLabel_->setPixmap( QPixmap() );
    previewMovie_->stop();
  }

  return false;
}



// One item was clicked
void EmoticonsWidget::slotItemClicked( const QListWidgetItem * item )
{
  if( ! initialized_ )
  {
    kWarning() << "Uninitialized usage of EmoticonsWidget!";
    return;
  }

  // Deselect current item so it has the click effect
  list_->setCurrentItem( 0 );

  emit insertEmoticon( item->data( Qt::UserRole ).toString() );
}



// The "Click to open the custom emoticons settings" link has been clicked
void EmoticonsWidget::slotNoEmoticonsLinkClicked()
{
  AccountsManager::instance()->showAccountSettings( CurrentAccount::instance(), window(), AccountSettingsDialog::PageEmoticonsTabCustom );
}



// The theme has changed, we need to reload it
void EmoticonsWidget::slotThemeUpdated()
{
  if( ! initialized_ )
  {
    kWarning() << "Uninitialized usage of EmoticonsWidget!";
    return;
  }

#ifdef KMESSDEBUG_EMOTICONSWIDGET
  kDebug() << "Removing old theme.";
#endif

  // Clear list
  list_->clear();

#ifdef KMESSDEBUG_EMOTICONSWIDGET
  kDebug() << "Updating with current emoticons.";
#endif

  // Add all theme emoticons to the list
  EmoticonTheme *theme = manager_->getTheme( isCustom_ );
  const QList<Emoticon*> &emoticons = theme->getEmoticons();

  // If the custom emoticon theme is empty, show the label
  if( isCustom_ && emoticons.isEmpty() )
  {
    if( noEmoticonsWarning_ == 0 )
    {
      // Create and set the properties for the warning message
      noEmoticonsWarning_ = new QLabel( this );
      noEmoticonsWarning_->setScaledContents( true );
      noEmoticonsWarning_->setWordWrap( true );
      noEmoticonsWarning_->setText( i18nc( "Informative label on the chat's emoticons bar",
                                    "<html><p><b>You have not added any custom emoticons yet.</b></p>"
                                    "<p>To add new emoticons, <a href='#'>click here</a>!</p></html>" ) );

      // Enable the link click action
      connect( noEmoticonsWarning_, SIGNAL(              linkActivated(const QString&) ),
               this,                SLOT  (               slotNoEmoticonsLinkClicked() ) );

      layout_->addWidget( noEmoticonsWarning_ );
    }

    // Show the label and hide the listwidget
    noEmoticonsWarning_->show();
    list_->hide();

    // Set the preview movie and label
    previewMovie_->stop();
    previewLabel_->hide();

#ifdef KMESSDEBUG_EMOTICONSWIDGET
    kDebug() << "Custom emoticon theme is empty";
#endif
    return;
  }

  // If the label was created, hide it!
  if( noEmoticonsWarning_ != 0 )
  {
    previewLabel_->show();
    noEmoticonsWarning_->hide();
  }

  list_->show();

  foreach( Emoticon *emoticon, emoticons )
  {
    if( ! emoticon->isValid() )
    {
      continue;
    }

    QListWidgetItem *item = new QListWidgetItem( QIcon( emoticon->getPicturePath() ), QString(), list_ );

    // Set the shortcut into data of current item widget
    item->setData( ShortcutRole, emoticon->getShortcut()    );
    item->setData( PictureRole,  emoticon->getPicturePath() );

    // Add the emoticon tooltip
    item->setToolTip( emoticon->getTooltip() );
  }

#ifdef KMESSDEBUG_EMOTICONSWIDGET
  kDebug() << "Updated total" << emoticons.count() << "emoticons";
#endif
}



// Initialize the widget
void EmoticonsWidget::initialize( bool useCustomTheme )
{
  if( initialized_ )
  {
    kWarning() << "EmoticonsWidget is already initialized!";
    return;
  }

  initialized_ = true;

  // Set the theme and load it
  isCustom_ = useCustomTheme;
  slotThemeUpdated();
}



// Connect the widget to a dock
void EmoticonsWidget::setDockWidget( QDockWidget *dockWidget, Qt::DockWidgetArea initialArea )
{
#ifdef KMESSTEST
  KMESS_ASSERT( dockWidget );
#endif

  // We need to change layout when the dock moves
  connect( dockWidget, SIGNAL( dockLocationChanged(Qt::DockWidgetArea) ),
           this,       SLOT  ( slotLocationChanged(Qt::DockWidgetArea) ) );
  connect( dockWidget, SIGNAL(     topLevelChanged(bool)               ),
           this,       SLOT  ( slotTopLevelChanged(bool)               ) );

  slotLocationChanged( initialArea );
}



// The location of the parent dock widget has changed
void EmoticonsWidget::slotLocationChanged( Qt::DockWidgetArea area )
{
#ifdef KMESSDEBUG_EMOTICONSWIDGET
  kDebug() << "Changing location to" << area;
#endif

  switch( area )
  {
    case Qt::TopDockWidgetArea:
    case Qt::BottomDockWidgetArea:
      layout_->setDirection( QBoxLayout::LeftToRight );
      break;

    case Qt::LeftDockWidgetArea:
    case Qt::RightDockWidgetArea:
    default:
      layout_->setDirection( QBoxLayout::TopToBottom );
      break;
  }

  // Update our size constraints
  setMinimumSize( minimumSizeHint() );
}



// The parent dock widget floating status has changed
void EmoticonsWidget::slotTopLevelChanged( bool isTopLevel )
{
  setVisible( ! isTopLevel );
  adjustSize();
}



// Right mouse button was clicked
void EmoticonsWidget::showContextMenu( const QListWidgetItem *item, const QPoint &point )
{
  if( ! initialized_ )
  {
    kWarning() << "Uninitialized usage of EmoticonsWidget!";
    return;
  }

  // Deselect current item so it has the click effect
  list_->setCurrentItem( 0 );

  // The popup menu is only for custom emoticon
  if( isCustom_ == false )
  {
    return;
  }

  // Create the popup menu for custom emoticon
  QMenu menu;
  QAction addAction(    KIcon( "insert-image" ),  i18n( "Add to Chat" ), this );
  QAction insertAction( KIcon( "list-add" ),      i18n( "Add New" ),     this );
  QAction editAction(   KIcon( "draw-freehand" ), i18n( "Edit"   ),      this );
  QAction removeAction( KIcon( "list-remove" ),   i18n( "Remove" ),      this );

  menu.addAction( &addAction    );
  menu.addAction( &insertAction );
  menu.addAction( &editAction   );
  menu.addAction( &removeAction );

  // Exec the menu popup and grep the action selected
  QAction *clickedAction = menu.exec( mapToGlobal( point ) );

  if( clickedAction == 0 )
  {
    // No action clicked
    return;
  }

  // Check which action was clicked
  if( clickedAction == &addAction )
  {
    slotItemClicked( item );
    return;
  }
  else if( clickedAction == &removeAction )
  {
    manager_->getTheme( isCustom_ )->removeEmoticon( item->data( ShortcutRole ).toString() );
    return;
  }

  if( emoticonDialog_ == 0 )
  {
    emoticonDialog_ = new AddEmoticonDialog( manager_->getTheme( true ), this );
  }

  if( clickedAction == &insertAction )
  {
    emoticonDialog_->preSelect( "", "" );
  }
  else if( clickedAction == &editAction )
  {
    emoticonDialog_->editEmoticon( item->data( PictureRole ).toString(), item->data( ShortcutRole ).toString() );
  }

  emoticonDialog_->show();
}


#include "emoticonswidget.moc"
