/***************************************************************************
                          chatstylepage.cpp -  description
                             -------------------
    begin                : Thu Jan 30 2008
    copyright            : (C) 2008 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 "chatstylepage.h"

#include "../chat/chatmessagestyle.h"
#include "../chat/chatmessageview.h"
#include "../currentaccount.h"
#include "../kmessdebug.h"

#include <QDir>

#include <KFontDialog>
#include <KHTMLView>
#include <KStandardDirs>
#include <knewstuff2/engine.h>



/**
 * The constructor
 */
ChatStylePage::ChatStylePage( QWidget *parent )
: QWidget( parent )
, Ui::ChatStylePage()
{
  setupUi( this );

  newStylesButton_->setIcon( KIcon( "get-hot-new-stuff" ) );

  QPixmap        pixmap;
  const QString& emoticonDir = CurrentAccount::instance()->getEmoticonStyle();
  KStandardDirs *dirs       = KGlobal::dirs();

  // Set up the emoticons which explain what the graphics elements in chat are
  pixmap.load( dirs->findResource( "emoticons", emoticonDir + "/teeth.png" ) );
  pixmapLabel1_->setPixmap( pixmap );
  pixmap.load( dirs->findResource( "emoticons", emoticonDir + "/rainbow.png" ) );
  pixmapLabel2_->setPixmap( pixmap );
  pixmap.load( dirs->findResource( "emoticons", emoticonDir + "/dog.png" ) );
  pixmapLabel3_->setPixmap( pixmap );

  // Connect our signals
  connect( contactFontColorButton_,SIGNAL(               changed(const QColor&)  ),
           this,                   SLOT  (         updatePreview()               ) );
  connect( fontColorButton_,       SIGNAL(               changed(const QColor&)  ),
           this,                   SLOT  (         updatePreview()               ) );
  connect( contactFontColorButton_,SIGNAL(               changed(const QColor&)  ),
           this,                   SLOT  (slotContactFontColorChanged(const QColor&) ) );
  connect( fontColorButton_,       SIGNAL(               changed(const QColor&)  ),
           this,                   SLOT  (slotUserFontColorChanged(const QColor&) ) );
  connect( useContactFontCheckBox_,SIGNAL(               toggled(bool)           ),
           this,                   SLOT  ( useContactFontToggled(bool)           ) );
  connect( fontButton_,            SIGNAL(               clicked()               ),
           this,                   SLOT  (           fontClicked()               ) );
  connect( contactFontButton_,     SIGNAL(               clicked()               ),
           this,                   SLOT  (    contactFontClicked()               ) );
  connect( chatStyle_,             SIGNAL(             activated(const QString&) ) ,
           this,                   SLOT  (  slotChatStyleChanged(const QString&) ) );
  connect( useEmoticonsCheckBox_,  SIGNAL(               toggled(bool)           ),
           this,                   SLOT  (         updatePreview()               ) );
  connect( useFontEffectsCheckBox_,SIGNAL(               toggled(bool)           ),
           this,                   SLOT  (         updatePreview()               ) );
  connect( showTimeCheckbox_,      SIGNAL(               toggled(bool)           ),
           this,                   SLOT  (         updatePreview()               ) );
  connect( showDateCheckbox_,      SIGNAL(               toggled(bool)           ),
           this,                   SLOT  (         updatePreview()               ) );
  connect( showSecondsCheckbox_,   SIGNAL(               toggled(bool)           ),
           this,                   SLOT  (         updatePreview()               ) );
  connect( groupFollowupCheckbox_, SIGNAL(               toggled(bool)           ),
           this,                   SLOT  (         updatePreview()               ) );
  connect( enableChatFormattingCheckBox_, SIGNAL(        toggled(bool)           ),
           this,                   SLOT  (         updatePreview()               ) );
  connect( newStylesButton_,       SIGNAL(               clicked()               ),
           this,                   SLOT  (          getNewThemes()               ) );


  // Configure the preview form of the "chatting" widget

  // Insert a KHTMLPart in the placeholder
  chatMessageView_ = new ChatMessageView( styleTab_, this );

  // Create a layout to maximize the KHTMLPart
  QBoxLayout *layout = new QHBoxLayout( khtmlPlaceholder_ );
  layout->addWidget( chatMessageView_->view() );

  loadStyleList();
}



// Destructor
ChatStylePage::~ChatStylePage()
{
}



// Update the style list
void ChatStylePage::loadStyleList()
{
  // Save the current choice to apply it again, after the list has been reloaded
  const QString text( chatStyle_->currentText() );

  // Remove any previous items, in case they're in
  chatStyle_->clear();

  // Get all available chat styles for the "chatting" widget, avoiding duplicate entries
  const QStringList stylesDirectories( KGlobal::dirs()->findDirs( "data", "kmess/styles" ) );

  foreach( const QString &dir, stylesDirectories )
  {
    const QDir styleDir( dir );
    const QStringList styles( styleDir.entryList( QDir::Dirs, QDir::Name | QDir::IgnoreCase ) );

    foreach( const QString &styleName, styles )
    {
      // e.g. share/apps/kmess/styles/Demo/Demo.xsl
      const QString xslFile( styleDir.path() + '/' + styleName + '/' + styleName + ".xsl" );
      if( QFile::exists( xslFile ) && ( chatStyle_->findText( styleName ) == -1 ) )
      {
        chatStyle_->addItem( styleName );
      }
    }
  }

  // Prepare the XSL transformation
  chatStyleTransform_ = chatMessageView_->getStyle();

  if( ! text.isEmpty() )
  {
    chatStyle_->setCurrentIndex( chatStyle_->findText( text, Qt::MatchExactly ) );
  }
}



// The contact font button was pressed.  Show a font dialog to get a new font.
void ChatStylePage::contactFontClicked()
{
  // Get a new font and font family from the user: if they get
  // changed, set the new font to the button
  if( ! getFont( contactFont_ ) )
  {
    return;
  }

  contactFontButton_->setText( contactFont_.family().replace( '&', "&&" ) );
  contactFontButton_->setFont( contactFont_ );
  contactFontButton_->setShortcut( QKeySequence() );

  // Update the preview
  chatStyleTransform_->setContactFont( contactFont_ );
  updatePreview();
}



// The font button was pressed.  Show a font dialog to get a new font.
void ChatStylePage::fontClicked()
{
  // Get a new font and font family from the user: if they get
  // changed, set the new font to the button
  if( ! getFont( userFont_ ) )
  {
    return;
  }

  fontButton_->setText( userFont_.family().replace( '&', "&&" ) );
  fontButton_->setFont( userFont_ );
  fontButton_->setShortcut( QKeySequence() );

  // Update the preview
  // We don't set chatStyleTransform like in contactFontClicked(),
  // because user messages are automatically generated with the currently
  // selected font
  updatePreview();
}



// Get a font and cleaned-up font family from a dialog
bool ChatStylePage::getFont( QFont &font ) const
{
  bool  accepted;
  int   result;
  QFont selection = font;

  result = KFontDialog::getFont( selection );
  accepted = ( result == KFontDialog::Accepted );

  if ( accepted )
  {
    font = selection;
  }

  return accepted;
}



/**
 * @brief Load the widget state from an account
 *
 * Reads an Account's info and sets our internal widgets accordingly
 *
 * @param account The account where to pick settings from
 */
void ChatStylePage::loadSettings( const Account *account )
{
  userFont_    = account->getFont();
  contactFont_ = account->getContactFont();
  userColor_   .setNamedColor( account->getFontColor()        );
  contactColor_.setNamedColor( account->getContactFontColor() );

  fontButton_->setText( userFont_.family().replace( '&', "&&" ) );
  fontButton_->setFont( userFont_ );
  fontButton_->setShortcut( QKeySequence() );
  fontColorButton_->setColor( userColor_ );

  contactFontButton_->setText( contactFont_.family().replace( '&', "&&" ) );
  contactFontButton_->setFont( contactFont_ );
  contactFontButton_->setShortcut( QKeySequence() );
  contactFontColorButton_->setColor( contactColor_ );

  useContactFontCheckBox_->setChecked( account->getUseContactFont() );
  useContactFontToggled( account->getUseContactFont() );
  useEmoticonsCheckBox_->setChecked( account->getUseEmoticons() );
  showWinksCheckBox_->setChecked( account->getShowWinks() );
  showTimeCheckbox_->setChecked( account->getShowMessageTime() );
  showDateCheckbox_->setChecked( account->getTimestampShowDate() );
  showSecondsCheckbox_->setChecked( account->getTimestampShowSeconds() );
  useFontEffectsCheckBox_->setChecked( account->getUseFontEffects() );
  groupFollowupCheckbox_->setChecked( account->getGroupFollowupMessages() );
  tabbedChatMode_->setCurrentIndex( account->getTabbedChatMode() );
  shakeNudgeCheckBox_->setChecked( account->getShakeNudge() );
  enableChatFormattingCheckBox_->setChecked( account->getUseChatFormatting() );
  displayChatUserPictureCheckBox_->setChecked( account->getShowChatUserPicture() );

  // Select the correct chat style,
  int item = chatStyle_->findText( account->getChatStyle(), Qt::MatchExactly | Qt::MatchCaseSensitive );
  if( item == -1 )
  {
    kmWarning() << "Current chat style was not found, attempting to revert to the default setting.";
    item = chatStyle_->findText( "Default", Qt::MatchExactly | Qt::MatchCaseSensitive );
  }

  if( item != -1 )
  {
    chatStyle_->setCurrentIndex( item );
    slotChatStyleChanged( chatStyle_->currentText() );
  }

  // Also load contact font settings into the chatStyle object,
  // other settings are set in updatePreview()
  chatStyleTransform_->setContactFont( contactFont_ );
  chatStyleTransform_->setContactFontColor( contactColor_.name() );
}



/**
 * @brief Save account information from the chat style widget
 *
 * Calls the set-up methods from Account to change its properties, based on how
 * the user has set the widgets of this page.
 */
void ChatStylePage::saveSettings( Account *account )
{
  account->setChatInformation( useContactFontCheckBox_->isChecked(),
                               useEmoticonsCheckBox_->isChecked(),
                               showWinksCheckBox_->isChecked(),
                               useFontEffectsCheckBox_->isChecked(),
                               enableChatFormattingCheckBox_->isChecked(),
                               shakeNudgeCheckBox_  ->isChecked(),
                               showTimeCheckbox_->isChecked(),
                               showDateCheckbox_->isChecked(),
                               showSecondsCheckbox_->isChecked(),
                               groupFollowupCheckbox_->isChecked(),
                               tabbedChatMode_->currentIndex(),
                               chatStyle_->currentText() );
  account->setShowChatUserPicture( displayChatUserPictureCheckBox_->isChecked() );

  // After setChatInformation, due the way ChatView changes updates in font and chat.
  // If the ordering is changed, it will re-generate all messages twice;
  // once for the changedFontSettings(), and once for the changedChatStyleSettings() signal.
  account->setFontInformation( userFont_,    userColor_   .name(),
                               contactFont_, contactColor_.name() );
}



// The chat style was changed.
void ChatStylePage::slotChatStyleChanged(const QString &style)
{
  // Update the chat style object
  chatStyleTransform_->setStyle( style );

  // Update the preview
  updatePreview();
}



// Update the contacts font color
void ChatStylePage::slotContactFontColorChanged( const QColor &color )
{
  contactColor_ = color;
}



// Update the user font color
void ChatStylePage::slotUserFontColorChanged( const QColor &color )
{
  userColor_ = color;
}



// Force the page onto a specific tab
void ChatStylePage::switchToTab( int tabIndex )
{
  if( ( tabIndex < 0 ) || tabIndex > tabWidget_->count() )
  {
    kmWarning() << "Invalid tab index" << tabIndex << "selected!";
    return;
  }

  tabWidget_->setCurrentIndex( tabIndex );
}



// Update the chat style preview
void ChatStylePage::updatePreview()
{
  if( chatStyleTransform_ == 0 || ! chatStyleTransform_->hasStyle() )
  {
    // Don't bother updating the preview for every event fired by the load...Settings() methods.
    return;
  }

  // Update the boolean settings (other settings are updated in their slots)
  chatStyleTransform_->setContactFont     ( contactFont_                               );
  chatStyleTransform_->setContactFontColor( contactColor_.name()                       );
  chatStyleTransform_->setShowTime        ( showTimeCheckbox_->isChecked()             );
  chatStyleTransform_->setShowDate        ( showDateCheckbox_->isChecked()             );
  chatStyleTransform_->setShowSeconds     ( showSecondsCheckbox_->isChecked()          );
  chatStyleTransform_->setUseContactFont  ( useContactFontCheckBox_->isChecked()       );
  chatStyleTransform_->setUseEmoticons    ( useEmoticonsCheckBox_->isChecked()         );
  chatStyleTransform_->setUseFontEffects  ( useFontEffectsCheckBox_->isChecked()       );
  chatStyleTransform_->setUseFormatting   ( enableChatFormattingCheckBox_->isChecked() );
  chatStyleTransform_->setGroupFollowupMessages( groupFollowupCheckbox_->isChecked()   );

  // Create three fake messages
  ChatMessage fake1( ChatMessage::TYPE_INCOMING, ChatMessage::CONTENT_MESSAGE, true, i18n("Hi, how are you doing? :)"), "test@kmess.org",
                     i18n("Stacy") );
  ChatMessage fake2( ChatMessage::TYPE_OUTGOING, ChatMessage::CONTENT_MESSAGE, false, i18n("Great!"), previewHandle_,
                     previewUsername_, previewPicture_, userFont_, userColor_.name() );
  ChatMessage fake3( ChatMessage::TYPE_OUTGOING, ChatMessage::CONTENT_MESSAGE, false, i18n("I /just/ got back from my vacation in Italy!"), previewHandle_,
                     previewUsername_, previewPicture_, userFont_, userColor_.name() );

  // Remove any old chat samples
  chatMessageView_->clearView( true );

  // Display the sample chat
  chatMessageView_->showMessage( fake1 );
  chatMessageView_->showMessage( fake2 );
  chatMessageView_->showMessage( fake3 );
}



// Update the data shown in the message preview
void ChatStylePage::updatePreviewDetails( const QString &handle, const QString &name, const QString &picture )
{
  previewHandle_   = handle;
  previewPicture_  = picture;

  previewUsername_ = ( name.isEmpty() ? handle : name );
}



// Enable or disable the contact font and color selectors
void ChatStylePage::useContactFontToggled( bool checked )
{
  // Enable/disable buttons
  contactFontButton_->setEnabled( checked );
  contactFontColorButton_->setEnabled( checked );

  // Update preview
  chatStyleTransform_->setUseContactFont( checked );
  updatePreview();
}



// Open a dialog for KDE New Stuff Chat Themes
void ChatStylePage::getNewThemes()
{
  KNS::Engine engine( this );
  engine.init( "kmesschatstyles.knsrc" );
  KNS::Entry::List entries = engine.downloadDialogModal( this );

  loadStyleList();
}



#include "chatstylepage.moc"
