/***************************************************************************
                          addemoticondialog.cpp - shows a dialog to add a custom emoticon
                             -------------------
    begin                : Tue April 10 2007
    copyright            : (C) 2007 by Valerio Pilo
    email                : valerio@kmess.org
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 "addemoticondialog.h"

#include "../utils/kmessconfig.h"
#include "../utils/kmessshared.h"
#include "../emoticonmanager.h"
#include "../kmessdebug.h"

#include <KFileDialog>
#include <KIconLoader>
#include <KMessageBox>


#ifdef KMESSDEBUG_EMOTICONS
  #define KMESSDEBUG_EMOTICONS_ADDING
#endif



/**
 * Constructor
 *
 * The dialog is instantly shown as non-modal when the class is instantiated.
 *
 * @param theme   Theme to add the new emoticon to
 * @param parent  Parent widget
 */
AddEmoticonDialog::AddEmoticonDialog( EmoticonTheme *theme, QWidget *parent )
 : KDialog( parent )
 , Ui::AddEmoticonDialog()
 , theme_(theme)
 , isEditing_(false)
{
  // Set up the interface and the dialog
  setObjectName( "AddEmoticon" );
  QWidget *mainWidget = new QWidget( this );
  setupUi( mainWidget );
  setMainWidget( mainWidget );

  // Configure the dialog
  setWindowModality( Qt::WindowModal );
  setButtons( Help | Ok | Cancel );
  setHelp( "settings-account-emoticons" );
  setDefaultButton( Ok );
  setCaption( i18n("Add New Emoticon") );

  setMainWidget( container_ );

  resize( minimumSizeHint() );
  restoreDialogSize( KMessConfig::instance()->getGlobalConfig( "AddEmoticonDialog" ) );

  // Connect the interface's signals to manage its events
  connect( browseButton_,  SIGNAL(          clicked()               ),
           this,                         SLOT(    choosePicture()               ) );
  connect( pictureEdit_,   SIGNAL(      textChanged(const QString&) ),
           this,                         SLOT( interfaceChanged()               ) );
  connect( shortcutEdit_,  SIGNAL(      textChanged(const QString&) ),
           this,                         SLOT( interfaceChanged()               ) );

  movie_ = new QMovie();
  movie_->setCacheMode ( QMovie::CacheAll );
  
  // Force an update of the widgets (mainly to show the KMess logo as
  // the preview and to disable the Ok button)
  interfaceChanged();

  show();
}



/**
 * Destructor
 */
AddEmoticonDialog::~AddEmoticonDialog()
{
  KConfigGroup group = KMessConfig::instance()->getGlobalConfig( "AddEmoticonDialog" );
  saveDialogSize( group );
  delete movie_;

#ifdef KMESSDEBUG_EMOTICONS_ADDING
  kDebug() << "DESTROYED.";
#endif
}



/**
 * Shows a File Selection dialog to choose an image for the new emoticon
 *
 * Gets the emoticon picture location from the user, using a KFile dialog, putting the pictureEdit_ widget contents
 * as a default, and then replaces the contents of said widget with the result of the file selection dialog.
 */
void AddEmoticonDialog::choosePicture()
{
  KUrl file( pictureEdit_->text() );

  // Choose a file, filtering out all files but the preselected image types
  file = KFileDialog::getImageOpenUrl( file, this );

  if( file.isEmpty() || ! file.isLocalFile() )
  {
    return;
  }

  // Update the text widget, so an update will be issued and the preview will be updated.
  pictureEdit_->setText( file.path() );
}



// Edit the custom emoticon
void AddEmoticonDialog::editEmoticon( const QString &pictureName, const QString &shortcut )
{
  setCaption( i18n("Edit Emoticon") );
  isEditing_ = true;
  preSelect( pictureName, shortcut );
}




/**
 * Controls the OK button by checking the dialog's widgets
 *
 * It's called everytime that a change is made to one of the dialog's widgets, and checks if their contents are valid.
 * Then enables or disables the OK button accordingly.
 */
void AddEmoticonDialog::interfaceChanged()
{
  bool enable = true;
  const QString& currentShortcut    ( shortcutEdit_->text() );
  const QString& currentEmoticonPath( pictureEdit_ ->text() );

  // Check if the current emoticon path is empty.
  if( currentEmoticonPath.isEmpty() )
  {
    // If the path is empty reset the view
    movie_->stop();
    movie_->setFileName( QString() );
  }
  else if( currentEmoticonPath != movie_->fileName() )
  {
    // User updated the current emoticon path
    movie_->stop();
    movie_->setFileName( pictureEdit_->text() );
  }

  // Start the movie only if it's valid
  if( movie_->isValid() )
  {
    // The chosen file is valid, update the preview.

#ifdef KMESSDEBUG_EMOTICONS_ADDING
    kDebug() << "Updated preview.";
#endif

    emoticonPreview_->setMovie( movie_ );
    movie_->start();
  }
  else
  {
    // The chosen file is not valid, reset the preview to the KMess logo.

#ifdef KMESSDEBUG_EMOTICONS_ADDING
    kDebug() << "Resetting preview.";
#endif

    enable = false;
    emoticonPreview_->setPixmap( KIconLoader::global()->loadIcon( "kmess", KIconLoader::Desktop, KIconLoader::SizeHuge ) );

    // Also show why the changes can't be confirmed
    label_->setText( i18n( "Please, select a valid picture file" ) );
  }

  if( currentShortcut.isEmpty() || ! Emoticon::isValidShortcut( currentShortcut ) )
  {
    enable = false;

    // Also show why the changes can't be confirmed
    if( currentShortcut.isEmpty() )
    {
      label_->setText( i18n( "Please, enter a text to associate with this emoticon" ) );
    }
    else
    {
      label_->setText( i18n( "Shortcuts must not:<ul>"
                             "<li>be longer than 7 characters,</li>"
                             "<li>start with \"/\" (they would interfere "
                               "with irc-like commands),</li>"
                             "<li>contain square brackets with text "
                               "within (like [b] or [color], which can be text formatting options)</li>"
                             "</ul>" ) );
    }
  }

  if( enable )
  {
    label_->setText( QString() );
  }
  // Adapt the dialog size to label text changes
  label_->adjustSize();
  adjustSize();
  resize( sizeHint() );

  // Enable the OK button only if both fields have valid contents
  enableButtonOk( enable );
}



/**
 * Preselects a file name and an emoticon shortcut in the dialog
 *
 * @param pictureName  Full path to the file that will be added
 * @param shortcut     Emoticon shortcut to preselect
 */
void AddEmoticonDialog::preSelect( const QString &pictureName, const QString &shortcut )
{
  // Select the image. Disable the field also to avoid messing up with the name.
  pictureEdit_->setText( pictureName );
  pictureEdit_->setEnabled( pictureName.isEmpty() );
  browseButton_->setEnabled( pictureName.isEmpty() );

  // Select the shortcut, and save it so later we can tell everyone we've added this particular emoticon
  preselectedShortcut_ = shortcut;

  // Select the shortcut, decoded from HTML to show it in its original form
  shortcutEdit_->setText( KMessShared::htmlUnescape( shortcut ) );
}




/**
 * Adds the new emoticon to the theme
 *
 * First it copies the user-selected picture to the custom theme's folder, then adds it to the theme itself;
 * finally it closes the dialog.
 */
void AddEmoticonDialog::slotButtonClicked( int button )
{
  bool isEditing = isEditing_;
  isEditing_ = false;

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

  QImageReader emoticonData;
  QString shortcut, pictureName;

#ifdef KMESSDEBUG_EMOTICONS_ADDING
  kDebug() << "OK button was pressed!";
#endif

  shortcut = shortcutEdit_->text();
  pictureName = pictureEdit_->text();

  // Check if the shortcut is really changed
  if( preselectedShortcut_ == shortcut && isEditing )
  {
    accept();
    return;
  }

  emoticonData.setFileName( pictureName );
  QString format( emoticonData.format() );

  if( format.isEmpty() || shortcut.isEmpty() )
  {
    kWarning() << "Shortcut or picture is not valid!";
    return;
  }

  // If the emoticon does already exist in the theme, ask if you want to replace it.
  if( theme_->contains( shortcut ) )
  {
#ifdef KMESSDEBUG_EMOTICONS_ADDING
    kDebug() << "Asking for overwrite.";
#endif

    int result = KMessageBox::questionYesNo( this,
                                             i18n( "The emoticon \"%1\" already exists. Do you want to replace it?", shortcut ),
                                             i18n("Add New Emoticon") );
    if ( result == KMessageBox::Yes )
    {
      theme_->removeEmoticon( shortcut );
    }
    else
    {
      return;
    }
  }

  // Create any missing directories.
  QDir dir;
  dir.mkpath( theme_->getThemePath() );

  if( isEditing )
  {
#ifdef KMESSDEBUG_EMOTICONS_ADDING
    kDebug() << "Editing emoticon: shortcut changed from " << preselectedShortcut_ << " to " << shortcut;
#endif
    theme_->renameEmoticon( preselectedShortcut_, shortcut );
    theme_->saveTheme();

    emit changedEmoticon( shortcut );

    accept();
    return;
  }

  // Access the original picture file details
  QFileInfo pictureInfo( pictureName );

  // For the most common image formats, keep the original filetype; otherwise, save the image as a PNG. This increases portability
  // between 3rd-party clients and ensures that MSN will always be able to read our emoticons.
  if( format == "gif" || format == "png" || format == "jpg" )
  {
    // Just copy over the image

    // Get handles for the original picture and the new emoticon
    QFile originalPicture( pictureName );
    QFile newThemePicture( theme_->getThemePath() + pictureInfo.fileName() );

#ifdef KMESSDEBUG_EMOTICONS_ADDING
    kDebug() << "Copying original picture" << pictureName << "to theme as" << newThemePicture.fileName();
#endif

    // Open the two images
    if( ! originalPicture.open( QIODevice::ReadOnly ) )
    {
      kWarning() << "Couldn't open source file: " << originalPicture.fileName();
      return;
    }

    if( ! newThemePicture.open( QIODevice::WriteOnly ) )
    {
      kWarning() << "Couldn't open destination file: " << newThemePicture.fileName();
      return;
    }

    // Copy the file contents
    newThemePicture.write( originalPicture.readAll() );
    newThemePicture.close();
    originalPicture.close();

    // Quick validity check
    if( newThemePicture.size() != originalPicture.size() )
    {
      kWarning() << "File sizes do not match: "
                  <<           originalPicture.fileName() << " (" << originalPicture.size() << ")"
                  << " != " << newThemePicture.fileName() << " (" << newThemePicture.size() << ")" << endl;
      return;
    }
  }
  else
  {
    // Save the loaded picture as a PNG image.

#ifdef KMESSDEBUG_EMOTICONS_ADDING
    kDebug() << "Recoding original picture" << pictureName << "(which format was" << format << ") to theme as" << pictureInfo.completeBaseName() << ".png";
#endif

    emoticonData.read().save( theme_->getThemePath() + pictureInfo.completeBaseName() + ".png", "png" );
  }


#ifdef KMESSDEBUG_EMOTICONS_ADDING
   kDebug() << "Adding new emoticon: " << shortcut << ".";
#endif

  // We've just created a file whose name is like "file.name.png", so give to the theme a name "file.name" so it can guess
  // the file type on its own.
  theme_->addEmoticon( pictureInfo.completeBaseName(), QStringList( shortcut ) );
  theme_->saveTheme();

  // If a shortcut has been preselected, signal that we've added that emoticon.
  // If it hasn't, just send the new shortcut.
  if( ! preselectedShortcut_.isEmpty() )
  {
    shortcut = preselectedShortcut_;
  }

  // Signal to anyone interested that we've added this emoticon
  emit addedEmoticon( shortcut );

  accept();
}



#include "addemoticondialog.moc"
