/***************************************************************************
                          contactpropertiesdialog.cpp  -  description
                             -------------------
    begin                : Sun Dec 15 2002
    copyright            : (C) 2002 by Michael Curtis
    email                : magnalium@hotmail.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 "contactpropertiesdialog.h"

#include "../contact/contactextension.h"
#include "../contact/group.h"
#include "../model/contactlist.h"
#include "../utils/kmessconfig.h"
#include "../utils/kmessshared.h"
#include "../currentaccount.h"
#include "../kmessdebug.h"

#include <QTextDocument>

#include <KFileDialog>
#include <KMessageBox>
#include <kmimetype.h>
#include <KIconEffect>
#include <KIconLoader>
#include <KStandardDirs>
#include <Phonon/BackendCapabilities>

#include <math.h>



ContactPropertiesDialog::ContactPropertiesDialog( QWidget *parent )
 : KDialog( parent )
 , Ui::ContactProperties()
 , ok_( false )
 , mediaSound_(0)
{
  // Set up the interface and the dialog
  setObjectName( "ContactProperties" );
  QWidget *mainWidget = new QWidget( this );
  setupUi( mainWidget );
  setMainWidget( mainWidget );

  // Configure the dialog
  setWindowModality( Qt::WindowModal );

  const KIconLoader *loader = KIconLoader::global();

  // Set the dialog size
  setButtons( Ok | Cancel );
  setDefaultButton( Ok );
  restoreDialogSize( KMessConfig::instance()->getGlobalConfig( "ContactPropertiesDialog" ) );

  // Set the icons for the buttons
  playButton_->setIcon(       loader->loadIcon( "media-playback-start", KIconLoader::Small ) );

  // Set properties for clear cache button
  clearCacheButton_->setIcon( loader->loadIcon( "draw-eraser",          KIconLoader::Small ) );
  useButton_       ->setIcon( loader->loadIcon( "user-properties",      KIconLoader::Small ) );

  connect( clearCacheButton_,     SIGNAL( clicked() ), this, SLOT( slotClearCache()         ) );
  connect( useButton_,            SIGNAL( clicked() ), this, SLOT( slotUsePicture()         ) );
  connect( restoreButton_,        SIGNAL( clicked() ), this, SLOT( restoreContactPicture()  ) );
  connect( playButton_,           SIGNAL( clicked() ), this, SLOT( slotPlaySound()          ) );
  connect( removeEmoticonButton_, SIGNAL( clicked() ), this, SLOT( slotRemoveEmoticon()     ) );

  connect( blackListView_,        SIGNAL(        itemSelectionChanged () ),
           this,                  SLOT  ( slotEmoticonSelectionChanged() ) );
  connect( pictureList_,          SIGNAL(        itemSelectionChanged () ),
           this,                  SLOT  (  slotPictureSelectionChanged() ) );

  imageContact_->installEventFilter( this );

  // Allow the user to select a local existing file.
  soundSelect_->setMode( KFile::File | KFile::ExistingOnly | KFile::LocalOnly );

  connect( soundSelect_,          SIGNAL( urlSelected               ( const KUrl & ) ),
           this,                  SLOT  ( checkNotificationSoundFile( const KUrl & ) ) );
}



ContactPropertiesDialog::~ContactPropertiesDialog()
{
  KConfigGroup group = KMessConfig::instance()->getGlobalConfig( "ContactPropertiesDialog" );
  saveDialogSize( group );

  delete mediaSound_;
#ifdef KMESSDEBUG_CONTACTPROPERTIES
  kDebug() << "DESTROYED.";
#endif
}



// The alt name checkbox was toggled.
void ContactPropertiesDialog::alternativeNameCheckBoxToggled(bool checked)
{
  nameEdit_->setEnabled( checked );
}



// Apply all values in the widgets to the contact extension.
void ContactPropertiesDialog::applyChanges()
{
  ContactExtension *extension = contact_->getExtension();

  // if the user clears the name edit, be user-friendly and decide that what
  // they really wanted to do was turn off the alternative name override.
  if ( nameEdit_->text().isEmpty() )
  {
    extension->setUseAlternativeName( false );
  }
  else
  {
    extension->setUseAlternativeName( alternativeNameCheckBox_->isChecked() );
  }

  extension->setAlternativeName     ( nameEdit_->text()                           );
  extension->setNotes               ( notesEdit_->toPlainText()                   );
  extension->setDisableNotifications( disableNotificationsCheckBox_->isChecked()  );
  extension->setContactSoundPath    ( soundSelect_->url().path()                  );
  extension->setContactAlternativePicturePath(  alternativePicturePath_           );


  const QStringList groupIdsList( contact_->getGroupIds() );
  QListWidgetItem *item;

  groupList_->setGridSize( QSize( 2, (int) ceil( (float) groupList_->count() / 2.0 ) ) );

  // Search for added/removed contact group
  for( int i = 0; i < groupList_->count(); i++ )
  {
    item = groupList_->item( i );
    const QString& currentId( item->data( 6 ).toString() );

    if( item->checkState() == Qt::Checked )
    {
      // If item is checked and the groupIdsList doesn't contain
      // the currentId, it means the user want to add the contact in other group
      if( ! groupIdsList.contains( currentId ) )
      {
        emit addContactToGroup( contact_->getHandle(), currentId );
      }
      // else the user is already in the group
      continue;
    }
    else
    {
      // If item is unchecked and the groupIdsList contains
      // the currentId, it means the user want to remove the contact from the group
      if( groupIdsList.contains( currentId ) )
      {
        emit removeContactFromGroup( contact_->getHandle(), currentId );
      }
      // else the user isn't already in the group
    }
  }

  // Save the changed settings
  CurrentAccount::instance()->getContactList()->saveProperties( contact_ );
}



// Choose a picture file (KFile dialog)
void ContactPropertiesDialog::choosePicture()
{
  const KUrl& file( KFileDialog::getImageOpenUrl( alternativePicturePath_ , this ) );

  if ( ! file.isEmpty() )
  {
    const QPixmap& pixmap( file.toLocalFile() );

    // If it's a valid image
    if( ! pixmap.isNull() )
    {
      imageContact_->setPixmap( pixmap );
      alternativePicturePath_ =  file.toLocalFile();
    }
  }
}



// Clear the qlistwidget if there are already object
void ContactPropertiesDialog::clearLists()
{
  // Clear the group list if there are already objects
  int count = groupList_->count();
  int i;

  if( count > 0 )
  {
    for( i = 0; i < count; i++ )
    {
      delete groupList_->takeItem( i );
    }
  }

  // Clear the group list if there are already objects
  count = pictureList_->count();
  if( count > 0 )
  {
    for( i = 0; i < count; ++i )
    {
      delete pictureList_->takeItem( i );
    }
  }
}



bool ContactPropertiesDialog::eventFilter(QObject *obj, QEvent *event )
{
  if( obj == imageContact_ )
  {
    QString image;
    if( alternativePicturePath_.isEmpty() )
    {
      image = contact_->getExtension()->getContactPicturePath();

      if( image.isEmpty() )
      {
        image = contact_->getContactDefaultPicturePath();
      }
    }
    else
    {
      image = alternativePicturePath_;
    }

    int eventType = event->type();

    if( eventType == QEvent::Enter )
    {
      QImage glowingImage( image );
      KIconEffect::toGamma( glowingImage, 0.8f );
      imageContact_->setPixmap( QPixmap::fromImage( glowingImage ) );
    }
    else if( eventType == QEvent::Leave )
    {
      imageContact_->setPixmap( image );
    }
    else if( eventType == QEvent::MouseButtonRelease )
    {
      choosePicture();
    }
  }

  return false;
}



// Show the dialog and obtain the contact handle.
bool ContactPropertiesDialog::launch( Contact *contact, DefaultTab defaultTab )
{
#ifdef KMESSDEBUG_CONTACTPROPERTIES
  kDebug() << "Launch with contact " << contact->getHandle();
#endif

  // Copy the pointer to the contact class
  contact_ = contact;

  // If the contact gets destroyed, close the window immediately
  connect( contact_, SIGNAL( destroyed(QObject*) ),
           this,     SLOT  (    reject()         ) );

  // Set the dialog title
  setCaption( i18n( "Contact Properties for %1", contact->getHandle() ) );

  // Setup the dialog
  setupWidgets();

  // If openNotesTab is true, set the current index to notes tab
  switch ( defaultTab )
  {
    case Information:
      tabs_->setCurrentIndex(0);
      break;

    case Images:
      tabs_->setCurrentIndex(1);
      break;

    case Notes:
      tabs_->setCurrentIndex(2);
      notesEdit_->setFocus();
      break;

    case Emoticons:
      tabs_->setCurrentIndex(3);
      break;

    default:
      tabs_->setCurrentIndex(0);
  }
  /*
  if( openNotesTab )
  {
    tabs_->setCurrentIndex( 2 );
    notesEdit_->setFocus();
  }
  // Else, just open first tab
  else
  {
    tabs_->setCurrentIndex( 0 );
  }
*/
  // Show the dialog modally
  exec();

  // If the ok button was pressed..
  if ( ok_ )
  {
    applyChanges();
  }

  return ok_;
}



// The disable notification checkbox was toggled
void ContactPropertiesDialog::disableNotificationsCheckBoxToggled( bool checked )
{
  soundSelect_->setDisabled( checked );
  playButton_ ->setDisabled( checked );
}



// Restore the original contact picture
void ContactPropertiesDialog::restoreContactPicture()
{
  // Restore the contact's picture
  QString originalPicture( contact_->getExtension()->getContactPicturePath() );

  if( originalPicture.isEmpty() )
  {
    originalPicture = contact_->getContactDefaultPicturePath();
  }

  const QPixmap& pixmap( originalPicture );

  if( ! pixmap.isNull() )
  {
    imageContact_->setPixmap( pixmap );
    alternativePicturePath_ = "";
  }

  restoreButton_->hide();
}



// Setup the dialog's widgets
void ContactPropertiesDialog::setupWidgets()
{
  // Use bold font for the friendly name of the contact
  QFont font;
  font.setBold(true);
  ContactExtension *extension = contact_->getExtension();

  // Set the button default view to hide
  restoreButton_->hide();

  trueNameLabel_->setFont( font );
  trueNameLabel_->setText( contact_->getTrueFriendlyName( STRING_LIST_SETTING_ESCAPED ) );

  // Set the informations about contact
  const QString& pm( contact_->getPersonalMessage( STRING_LIST_SETTING_ESCAPED ) );
  if( pm.isEmpty() )
  {
    // Hide to avoid blank space
    personalMessageLabel_->hide();
  }
  else
  {
    personalMessageLabel_->setText( pm );
  }

  QString lastSeenString, lastMessageString;

  if( contact_->isOnline() )
  {
    lastSeenString = i18n( "Connected" );
  }
  else if( extension->getLastSeen().toTime_t() == 0
            || extension->getLastSeen().toTime_t() == UINT_MAX
            || extension->getLastSeen().isNull() )
  {
    lastSeenString = i18n( "Not seen yet" );
  }
  else
  {
    lastSeenString = extension->getLastSeen().toString();
  }

  if( extension->getLastMessageDate().toTime_t() == 0
       || extension->getLastMessageDate().toTime_t() == UINT_MAX
       || extension->getLastMessageDate().isNull() )
  {
    lastMessageString = i18n( "No messages yet" );
  }
  else
  {
    lastMessageString = extension->getLastMessageDate().toString();
  }

  lastSeenLabel_       ->setText( i18nc( "Contact props dialog info", "Last seen: %1",     lastSeenString )            );
  lastMessageLabel_    ->setText( i18nc( "Contact props dialog info", "Last message: %1",  lastMessageString )         );
  emailLabel_          ->setText( i18nc( "Contact props dialog info", "Email address: %1", contact_->getHandle() )     );
  clientLabel_         ->setText( i18nc( "Contact props dialog info", "Client: %1",        contact_->getClientName() ) );
  nameEdit_            ->setText( extension->getAlternativeName( STRING_ORIGINAL ) );
  notesEdit_      ->setPlainText( extension->getNotes()                            );

  alternativeNameCheckBox_->setChecked( extension->getUseAlternativeName() );
  alternativeNameCheckBoxToggled(       extension->getUseAlternativeName() );

  disableNotificationsCheckBox_->setChecked(    extension->getDisableNotifications() );
  disableNotificationsCheckBoxToggled(          extension->getDisableNotifications() );

  // Set the contact picture
  QPixmap pixmap( contact_->getContactPicturePath() );

  // If pixmap is null set the default contact picture
  if( pixmap.isNull() )
  {
    pixmap.load( contact_->getContactDefaultPicturePath() );
  }

  imageContact_->setPixmap( pixmap );

  alternativePicturePath_ = extension->getContactAlternativePicturePath();

  // If the alternative picture is used, show the restore button
  if( ! alternativePicturePath_.isEmpty() )
  {
    restoreButton_->show();
  }

  soundSelectLabel_->setBuddy(soundSelect_);
  soundSelect_->setUrl( extension->getContactSoundPath() );

  // Set the group list
  const QStringList& groupIdsList( contact_->getGroupIds() );
  const QList<Group *>& groupContactList( CurrentAccount::instance()->getContactList()->getGroupList() );
  QListWidgetItem *item;

  foreach( const Group *group, groupContactList )
  {
    if( ! group->isSpecialGroup() )
    {
      item = new QListWidgetItem( group->getName(), groupList_ );
      item->setFlags( Qt::ItemIsUserCheckable | Qt::ItemIsEnabled );
      item->setData( 6, group->getId() );

      if( groupIdsList.contains( group->getId() ) )
      {
        item->setCheckState( Qt::Checked );
      }
      else
      {
        item->setCheckState( Qt::Unchecked );
      }
    }
  }

  // If contact isn't in FL disable the group list
  if( ! contact_->isFriend() )
  {
    groupList_->setEnabled( false );
  }

  // Set the pictures list
  const QStringList& pictureList( extension->getPictureList() );

  foreach( const QString &pictureFile, pictureList )
  {
    // Load into QPixmap and check if the pixmap is valid
    if( ! pixmap.load( pictureFile ) )
    {
      continue;
    }

    // Set the new item and put it into pictures list
    item = new QListWidgetItem();
    item->setIcon( QIcon( pixmap ) );
    item->setData( Qt::UserRole, pictureFile );

    QFile fileDat( pictureFile + ".dat" );
    if ( fileDat.exists() && fileDat.open( QIODevice::ReadOnly | QIODevice::Text ) )
    {
      QTextStream in( &fileDat );
      item->setText( in.readLine() );
      fileDat.close();
    }

    pictureList_->addItem( item );
  }

  // Set up the emoticon blacklist
  const QStringList& blackList( contact_->getEmoticonBlackList() );
  foreach( const QString &shortcut, blackList )
  {
    item = new QListWidgetItem();
    item->setData( Qt::UserRole, shortcut );
    item->setText( KMessShared::htmlUnescape( shortcut ) );

    blackListView_->addItem( item );
  }
}



// The Ok or Cancel button has been pressed
void ContactPropertiesDialog::slotButtonClicked( int button )
{
  // If the Cancel button has been pressed, close without saving
  if( button != Ok )
  {
    KDialog::slotButtonClicked( button );
    return;
  }

  ok_ = true;
  accept();
}



// The "Clear cache" button has been pressed
void ContactPropertiesDialog::slotClearCache()
{
  if( pictureList_->count() != 0 )
  {
    ContactExtension *extension = contact_->getExtension();
    extension->clearCache();
    pictureList_->clear();
  }
}



// The user has selected or deselected something in the emoticon blacklist
void ContactPropertiesDialog::slotEmoticonSelectionChanged()
{
  // Only activate the Remove Emoticon button if an item is selected
  removeEmoticonButton_->setEnabled( ! blackListView_->selectedItems().isEmpty() );
}



// The user has selected or deselected something in the pictures list
void ContactPropertiesDialog::slotPictureSelectionChanged()
{
  // Only activate the Use as personal image button if an item is selected
  useButton_->setEnabled( ! pictureList_->selectedItems().isEmpty() );
}



// The play sound button has been pressed
void ContactPropertiesDialog::slotPlaySound()
{
  const QString& currentFile( soundSelect_->url().url() );
  if( currentFile.isEmpty() )
  {
    return;
  }

  // Initialize the media sound object and connect it with slot sound finished
  if( mediaSound_ == 0 )
  {
    mediaSound_ = Phonon::createPlayer( Phonon::NotificationCategory );
    connect( mediaSound_, SIGNAL( finished() ), this, SLOT( slotSoundFinished() ) );
  }

  // If the state is playing, stop the sound media and reset the icon
  if( mediaSound_->state() == Phonon::PlayingState )
  {
    mediaSound_->stop();
    playButton_->setIcon( KIcon( "media-playback-start" ) );
    return;
  }

  // Play the sound and change the icon
  mediaSound_->setCurrentSource( currentFile );
  mediaSound_->play();
  playButton_->setIcon( KIcon( "media-playback-stop" ) );
}



// Remove a blacklisted emoticon
void ContactPropertiesDialog::slotRemoveEmoticon()
{
  QListWidgetItem *item = blackListView_->currentItem();

  if( item == 0 )
  {
#ifdef KMESSDEBUG_CONTACTPROPERTIES
    kDebug() << "No emoticon selected!";
#endif
    return;
  }

  bool removed = contact_->manageEmoticonBlackList( false, item->data( Qt::UserRole ).toString() );

#ifdef KMESSDEBUG_CONTACTPROPERTIES
  kDebug() << "Blacklist operation succeeded?" << removed;
#endif

  if( removed )
  {
    delete item;
  }
}



// Called when media sound finished
void ContactPropertiesDialog::slotSoundFinished()
{
  // When sound finished, change reset the icon
  playButton_->setIcon( KIcon( "media-playback-start" ) );
}



// Use the selected picture as personal image
void ContactPropertiesDialog::slotUsePicture()
{
  // Check if the selection is valid
  const QListWidgetItem *item = pictureList_->currentItem();
  if( item == 0 )
  {
    return;
  }

  // Grep the filename of the picture
  QFile file( item->data( Qt::UserRole ).toString() );
  if( ! file.exists() )
  {
    // The file doesn't exist
    return;
  }

  // Ask confirmation to the user
  int result = KMessageBox::questionYesNo( this,
                                           i18nc( "Dialog box text",
                                                  "Are you sure you want to use the display "
                                                  "picture of this contact?" ),
                                           i18n( "Copy Contact Picture" ),
                                           KStandardGuiItem::yes(),
                                           KStandardGuiItem::no(),
                                           QString( "copyContactPicture" ) );
  if( result == KMessageBox::No )
  {
    return;
  }

  const QFileInfo fileInfo( file );
  CurrentAccount *account = CurrentAccount::instance();
  const QString& accountHandle( account->getHandle() );
  const QString& pictureDirPath( KMessConfig::instance()->getAccountDirectory( accountHandle ) + "/displaypics/" );

  const QString& newFileName( pictureDirPath + fileInfo.fileName() );

  // If the file already exists in the personal picture directory, use it
  // Else copy it from contact directory
  if( ! QFile::exists( newFileName ) )
  {
    // Check if the directory already created ( Note: this check is necessary because
    // if the user doesn't never set one personal image, the directory doesn't exist )
    QDir pictureDir( pictureDirPath );
    if( ! pictureDir.exists() )
    {
      // Create the missing directory
      pictureDir.mkpath( pictureDirPath );
    }
    if( ! file.copy( newFileName ) )
    {
      return;
    }
  }

  // Set the new picture
  account->setPicturePath( newFileName );
}



// Check if the fileformat is supported by Phonon
bool ContactPropertiesDialog::checkNotificationSoundFile( const KUrl &url ) {

#ifdef KMESSDEBUG_CONTACTPROPERTIES
  kDebug() << "Checking filetype";
#endif

  if( Phonon::BackendCapabilities::availableMimeTypes().contains(
        KMimeType::findByUrl( url.url() )->name()) )
  {
    return true;
  }

  KMessageBox::sorry( this, i18n( "The selected filetype is not supported by Phonon." ), i18n( "Unsupported filetype" ) );
  soundSelect_->setUrl( KUrl( contact_->getExtension()->getContactSoundPath() ) );
  return false;
}



#include "contactpropertiesdialog.moc"
