/***************************************************************************
                          kmess.cpp  -  description
                             -------------------
    begin                : Sun Jan  5 15:18:36 CST 2003
    copyright            : (C) 2003 by Mike K. Bennett
    email                : mkb137b@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 "kmess.h"

#include "emoticon.h"        // workarround for mingw-g++ compiler bug "extraneous `char' ignored"
#include "emoticontheme.h"   //
#include "emoticonmanager.h" //

#include "chat/chat.h"
#include "chat/chatmaster.h"
#include "contact/contact.h"
#include "contact/group.h"
#include "contact/msnstatus.h"
#include "dialogs/addcontactdialog.h"
#include "dialogs/awaymessagedialog.h"
#include "dialogs/chathistorydialog.h"
#include "dialogs/contactaddeduserdialog.h"
#include "dialogs/listexportdialog.h"
#include "dialogs/transferwindow.h"
#include "model/contactlist.h"
#include "network/msnnotificationconnection.h"
#include "notification/notificationmanager.h"
#include "notification/chatnotification.h"
#include "notification/contactstatusnotification.h"
#include "notification/newemailnotification.h"
#include "notification/systemtraywidget.h"
#include "settings/accountsettingsdialog.h"
#include "utils/idletimer.h"
#include "utils/kmessconfig.h"
#include "utils/kmessdbus.h"
#include "utils/kmessshared.h"
#include "utils/nowlisteningclient.h"
#include "utils/richtextparser.h"
#include "account.h"
#include "accountsmanager.h"
#include "accountaction.h"
#include "currentaccount.h"
#include "emoticonmanager.h"
#include "initialview.h"
#include "kmessdebug.h"
#include "kmessinterface.h"
#include "kmessview.h"

#include <QDir>
#include <QNetworkProxy>

#include <KActionMenu>
#include <KConfig>
#include <KGlobal>
#include <KInputDialog>
#include <KLocale>
#include <KMessageBox>
#include <KMenu>
#include <KProtocolManager>
#include <KSelectAction>
#include <KStandardDirs>
#include <KStatusBar>
#include <KToggleAction>

#ifdef KMESSTEST
  #include "dialogs/networkwindow.h"
#endif

// The constructor
KMess::KMess( QWidget *parent )
 : KMessInterface( parent ),
   chatMaster_(0),
   chatNotification_(0),
   contactStatusNotification_(0),
   newEmailNotification_(0),
   idleTimer_(0),
   initialized_(false),
   initialView_(0),
   nowListeningClient_(0),
   msnNotificationConnection_(0),
   view_(0)
{
}



// The destructor
KMess::~KMess()
{
#ifdef KMESSDEBUG_KMESS
  kDebug() << "shutting down KMess...";
#endif

  /*
   * The destructor is called after closeEvent(),
   * when KMess is quit by the user.
   *
   * If the KDE session ends, the destructor won't be called.
   * only saveProperties() will be called, and the execution ends.
   */


  // Saving properties manually.
  saveProperties();

  // Delete all chat windows
  if(chatMaster_ != 0)
  {
    chatMaster_->disconnected();
    chatMaster_->deleteLater();
    chatMaster_ = 0;
  }

  // Disconnect main connection so views can clean up/save properties.
  if(msnNotificationConnection_->isConnected())
  {
    // Make sure KMess::disconnected() does attempt to update
    // the user interface when we're trying to kill it
    disconnect(msnNotificationConnection_, SIGNAL(disconnected()), this, SLOT(disconnected()) );

    // This also destroys all contacts
    msnNotificationConnection_->closeConnection();
  }

  // Delete other created objects
  delete idleTimer_;
  delete chatNotification_;
  delete contactStatusNotification_;
  delete notificationManager_;
  delete newEmailNotification_;
  delete nowListeningClient_;
  delete msnNotificationConnection_;

  AccountsManager::destroy();
  CurrentAccount::destroy();
  TransferWindow::destroy();
  KMessConfig::destroy();
#ifdef KMESS_NETWORK_WINDOW
  NetworkWindow::destroy();
#endif

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



// Add the account to the GUI
void KMess::slotAccountAdded(Account *account)
{
  // Check if the account is already present in the menus
  if( connectMenuItems_.contains( account ) )
  {
    return;
  }

  AccountAction *accountAction;

  // Make the action for the "Connect..." menu
  accountAction = new AccountAction( account, this );
  connectActionMenu_->addAction( accountAction );
  connectMenuItems_[ account ] = accountAction;

  connect( accountAction, SIGNAL(          activated(Account*) ),
           this,          SLOT  ( connectWithAccount(Account*) ) );

  // If the initial login window is visible, add the new account there if it's not a guestAccount
  if( initialView_ && ! account->isGuestAccount())
  {
    initialView_->addAccount( account );
  }
}



// "Add a new contact" was selected from the menu.
void KMess::addNewContact()
{
#ifdef KMESSTEST
  KMESS_ASSERT( msnNotificationConnection_ != 0 );
#endif

  QStringList groupsId;
  QString newHandle;

  // Launch a modal dialog to get the new contact handle and, optionally, group
  new AddContactDialog( newHandle, groupsId, this );

  // Do nothing if the user has clicked cancel
  if( newHandle.isEmpty() )
  {
    return;
  }
#ifdef KMESSDEBUG_KMESS
  kDebug() << "Adding " << newHandle << "to groups: " << groupsId;
#endif

  // Check if the contact is already in contact list
  Contact *contact = currentAccount_->getContactList()->getContactByHandle( newHandle );

  if( contact != 0 )
  {
    // If the contact is already in friendly list, stop the process
    if( contact->isFriend() )
    {
      QMessageBox::information( this, i18n( "Contact Information" ), i18n( "<html>The contact <b>%1</b> is already in your contact list.</html>", newHandle ) );
      return;
    }
    else
    {
      // The user is adding one deleted contact
      msnNotificationConnection_->addExistingContact( newHandle, groupsId );
      return;
    }
  }

  // Ask the server to add the contact
  msnNotificationConnection_->addNewContact( newHandle, groupsId );
}



// "Add a new group" was selected from the menu.
void KMess::addNewGroup()
{
#ifdef KMESSTEST
  KMESS_ASSERT( msnNotificationConnection_ != 0 );
#endif
#ifdef KMESSDEBUG_KMESS
  kDebug();
#endif
  QString newGroupName;
  bool    okPressed = false;

  // Set a default group name
  newGroupName = i18n( "New Group" );

  // Launch a dialog to get a new group name
  newGroupName = KInputDialog::getText(i18nc( "Dialog box title", "Add a Group" ),
                                       i18n( "Enter a name for the new group:" ),
                                       newGroupName, &okPressed, this);

  if(okPressed)
  {
    // Check if the group already exists.
    Group *group = currentAccount_->getContactList()->getGroupByName( newGroupName );
    if( group != 0 )
    {
      return;
    }

    // Ask the server to add the group
    msnNotificationConnection_->addGroup( newGroupName );
  }
}



// The application is closing, after queryClose() was approved
void KMess::applicationClosing()
{
  // Prepare closing, KMessInterface::queryClose() accepted the close request.
  // After this method returns, KApplication::aboutToQuit() also kicks in, handled by KMessApplication.

  if( chatMaster_ != 0 )
  {
#ifdef KMESSDEBUG_KMESS
    kDebug() << "destroying chat master";
#endif

    // Close the chat windows earlier, so logs are saved.
    chatMaster_->deleteLater();
    chatMaster_ = 0;
  }
}



// An account's settings have been changed
void KMess::slotAccountChanged( Account *account, QString oldHandle, QString oldFriendlyName )
{
  QString newHandle( account->getHandle() );

#ifdef KMESSDEBUG_KMESS
  kDebug() << "Saving UI settings for" << newHandle;
#endif

  if ( msnNotificationConnection_ != 0 )
  {
    // While we're saving stuff, also save the contact list settings
    msnNotificationConnection_->saveProperties();

    bool isCurrentAccount          = ( oldHandle == currentAccount_->getHandle() );
    bool isConnectedCurrentAccount = ( isCurrentAccount && msnNotificationConnection_->isConnected() );

    // If the account matches the current account and the friendly name was changed, ask the server to change it
    if( isConnectedCurrentAccount && account->isVerified() && account->getFriendlyName( STRING_ORIGINAL ) != oldFriendlyName )
    {
      msnNotificationConnection_->changeFriendlyName( account->getFriendlyName( STRING_ORIGINAL ) );
    }
  }

  // Change the account name in the UI when it's been changed
  if( oldHandle == newHandle )
  {
    return;
  }

  // Update it in the initial view's list of accounts
  if( initialView_ )
  {
#ifdef KMESSDEBUG_KMESS
  kDebug() << "Updating initial view";
#endif
    // Notify the initial view when the account handle has changed.
    initialView_->changedAccount( oldHandle, newHandle );
  }

  // Update the menu items
  connectMenuItems_ [ account ]->updateText();
}



// A view pictures mode has been selected from the menu.
void KMess::changedListPictureSize( int mode )
{

  int newMode = 0;
  switch( mode )
  {
    case 0:
      break;
    case 1:
      newMode = 32;
      break;
    case 2:
      newMode = 40;
      break;
    case 3:
      newMode = 48;
      break;

    default:
      break;
  }

  currentAccount_->setListPictureSize( newMode );
}



// A status was selected from the menu.
void KMess::changeStatus( QAction *action )
{
  QString awayMessage;
  Status  newStatus = (Status) action->data().toInt();

  // The user has set Away with Auto Reply, let it choose the message
  if( newStatus == STATUS_AWAY_AUTOREPLY )
  {
    awayMessage = currentAccount_->getAutoreplyMessage();

    // Create and show the away message dialog
    AwayMessageDialog *awayMessageDialog = new AwayMessageDialog( this );
    bool useAutoreply = awayMessageDialog->useMessage( awayMessage );
    if( ! useAutoreply )
    {
      awayMessage.clear();
    }

    delete awayMessageDialog;
  }

  // The user wants to disconnect
  if( newStatus == STATUS_OFFLINE )
  {
    disconnectClicked();
    return;
  }

  changeStatus( newStatus, awayMessage );
}



// We should change status to this right away (can be called by changeStatus() or by other code)
void KMess::changeStatus( Status newStatus, QString autoReplyMessage )
{
#ifdef KMESSDEBUG_KMESS
  kDebug() << "Selected status:" << MsnStatus::getCode( newStatus );
#endif

  // Is the status is set manually, idle is forced.
  isIdleForced_ = true;

  // Set or unset the auto reply message
  if( autoReplyMessage.isEmpty() )
  {
    currentAccount_->setAutoreply( false );
  }
  else
  {
    currentAccount_->setAutoreplyMessage( autoReplyMessage );
    currentAccount_->setAutoreply( true );
  }

  // Change status
  msnNotificationConnection_->changeStatus( newStatus );
}



// The current now listening settings have changed.
void KMess::changedNowListeningSettings()
{
  // Enable now listening if current account is online and likes to enable now listening.
  Status status = currentAccount_->getStatus();

  // Enable only if not offline or invisible
  bool isOnline = ( status != STATUS_OFFLINE && status != STATUS_INVISIBLE );
  nowListeningClient_->setEnabled( currentAccount_->getShowNowListening() && isOnline );

#ifdef KMESSDEBUG_KMESS
  kDebug() << "Now listening setting is now"
           << ( currentAccount_->getShowNowListening() ? "enabled" : "disabled") << endl;
#endif
}



// The currently playing song was changed.
void KMess::changedSong( const QString &artist, const QString &album, const QString &track, bool playing )
{
  if( msnNotificationConnection_->isConnected() )
  {
    if( ! playing )
    {
      // Update notification connection and KMessView.
      msnNotificationConnection_->removeCurrentMedia();
    }
    else
    {
      // Update notification connection.
      QStringList arguments;
      arguments << track << artist << album << "" << "";
      msnNotificationConnection_->changeCurrentMedia( "", "Music", true, "{0} - {1}", arguments );
    }

    // Update KMessView
    if( view_ )
    {
        view_->changedSong( artist, album, track, playing );
    }
  }
}



// The status was changed
void KMess::changedStatus( Account *account )
{
  Status newStatus;

  if( account != 0 )
  {
    newStatus = account->getInitialStatus();
  }
  else if ( currentAccount_ != 0 )
  {
    newStatus = currentAccount_->getStatus();
  }
  else
  {
    kWarning() << "Changed status dropdown without an account set!";
    return;
  }

#ifdef KMESSDEBUG_KMESS
  kDebug() << "Changed status to" << MsnStatus::getCode( newStatus )
           << "Autoreply is" <<  currentAccount_->getAutoreply() << endl;
#endif

  // Make sure the drop down list matches the user's status
  QList<KAction*> menuActions( status_->findChildren<KAction*>() );
  foreach( KAction *action, menuActions )
  {
    if( (Status) action->data().toInt() == newStatus )
    {
      action->setEnabled( false );
    }
    else
    {
      action->setEnabled( true );
    }
  }
}



// A view mode has been selected from the menu.
void KMess::changeViewMode(int mode)
{
#ifdef KMESSTEST
  KMESS_ASSERT( ( mode >= 0 ) || ( mode <= 2 ) );
#endif

  CurrentAccount::instance()->setContactListDisplayMode( (Account::ContactListDisplayMode) mode );
}



// Show a "Contact added you" dialog
void KMess::showContactAddedUserDialog( const QString handle )
{
#ifdef KMESSDEBUG_KMESS
  kDebug() << "Contact" << handle << "added user, showing dialog.";
#endif

  // Show a dialog and let the user choose whether to add, allow, or block the contact.
  ContactAddedUserDialog *dialog = new ContactAddedUserDialog( handle , handle );

  connect( dialog, SIGNAL(                       userChoice(const QString&,const QStringList&,const int) ),
           this,   SLOT  ( slotContactAddedUserDialogChoice(const QString&,const QStringList&,const int) ) );

  dialog->show();
}



// Show the context-sensitive menu item
void KMess::showContextMenu()
{
  if( KMESS_NULL( view_ ) ) return;
  if( KMESS_NULL( connectActionMenu_ ) ) return;
  if( KMESS_NULL( contextMenuAction_ ) ) return;

  // HACK: Get where the triggered action is located, by asking a menu
  QRect actionPos( connectActionMenu_->menu()->actionGeometry( contextMenuAction_ ) );

  view_->showContextMenu( actionPos.bottomRight() ); // Approximate to a side of the action, can't really do more
}



void KMess::showListExportDialog()
{
  ListExportDialog *listExportDialog = new ListExportDialog();
  listExportDialog->show();
}



#ifdef KMESS_NETWORK_WINDOW
// Opens the network window
void KMess::showNetworkWindow()
{
  NetworkWindow::instance()->show();
}
#endif


// Show a dialog before removing the contact
void KMess::showRemoveContactDialog(QString handle)
{
  QString message( i18n( "<qt>Are you sure you want to remove the contact <b>%1</b> from your contact list?</qt>", handle ) );

  int result = KMessageBox::warningYesNoCancel( this, message, i18n( "Remove Contact" )
                                              , KGuiItem(i18n("Remove"), "edit-delete")             // Yes
                                              , KGuiItem(i18n("Remove and Block"), "list-remove")       // No
                                              , KStandardGuiItem::cancel()
                                              , QString()
                                              , KMessageBox::Dangerous
                                              );

  switch( result )
  {
    // User has pressed "Remove and block"
    case KMessageBox::No :
      msnNotificationConnection_->removeContact( handle, true );
      break;

    // User has pressed "Remove only"
    case KMessageBox::Yes :
      msnNotificationConnection_->removeContact( handle, false );
      break;

    // User has canceled the action, no nothing
    case KMessageBox::Cancel :
    default:
      break;
  }

}


// Show a dialog before removing the group
void KMess::showRemoveGroupDialog(QString groupId)
{
  const ContactList *list = currentAccount_->getContactList();
  Group *group = list->getGroupById( groupId );

  // If the group was not found, bail out
  if(group == 0)
  {
    kDebug() << "Couldn't find a matching group.";
    return;
  }

  // If the group was not empty, bail also out. We would receive an error from the server, otherwise
  if( ! list->isGroupEmpty( groupId ) )
  {
    // TODO Allow the user to choose between: 1) delete or delete+block all contacts in the group;
    // 2) remove all contacts from the group; 3) cancel the operation.
    KMessageBox::error( this,
                        i18nc( "dialog text", "The group <b>%1</b> is not empty! First remove all contacts from it, then try again!",
                              group->getName() ),
                        i18nc( "dialog title", "Group Removal" ) );
    return;
  }

  // Prompt to remove
  QString message( i18nc( "dialog text", "<qt>Are you sure you want to remove the group <b>%1</b> from your contact list?</qt>", group->getName() ) );
  int result = KMessageBox::warningContinueCancel( this, message,
                                                   i18nc( "dialog title", "Group Removal" ),
                                                   KGuiItem( i18nc( "dialog button", "Remove" ), "edit-delete" ),
                                                   KStandardGuiItem::cancel(),
                                                   QString(),
                                                   KMessageBox::Dangerous
                                                  );

  if(result == KMessageBox::Continue)
  {
    msnNotificationConnection_->removeGroup(groupId);
  }
}


// Show a "Rename group" dialog
void KMess::showRenameGroupDialog( QString groupId )
{
  Group  *group = currentAccount_->getContactList()->getGroupById(groupId);

  // If the group was found
  if ( group == 0 )
  {
    kDebug() << "Couldn't find a matching group.";
    return;
  }

  // If the group is not a special group...
  if( group->isSpecialGroup() )
  {
    // Show a message that it can't be deleted
    KMessageBox::error( 0, i18n("This is a special group, which cannot be changed.") );
    return;
  }
  else
  {
    bool okPressed = false;

    // Otherwise, get the new group name..
    const QString& currentName( group->getName() );

    // Launch a dialog to get a new group name
    const QString& newGroupName( KInputDialog::getText(i18n( "Rename Group" ),
                                 i18n( "Enter a new name for this group:" ),
                                 currentName, &okPressed, this ) );

    if( okPressed && currentName != newGroupName )
    {
      // Request that the group be renamed
      msnNotificationConnection_->renameGroup(groupId, newGroupName);
    }
  }
}



// Show the chat history dialog and, if requested, that of a specific contact
void KMess::showChatHistory( const QString &handle )
{
  ChatHistoryDialog *dialog = new ChatHistoryDialog( this );

  dialog->setContact( handle );
  dialog->show();
}



// Opens the transfer manager
void KMess::showTransferWindow()
{
  TransferWindow *transferWindow = TransferWindow::getInstance();
  transferWindow->show();
}


// Autologin with the first account that has autologin enabled
// This method is also called when restoring the session
void KMess::checkAutologin(QString handle)
{
  Account *loginAccount = 0;

  // If no handle was given, find the default account to connect with.
  if ( handle.isEmpty() )
  {
#ifdef KMESSDEBUG_KMESS
    kDebug() << "No handle was given, find an account that uses autologin.";
#endif

    const QList<Account*> accountsList = AccountsManager::instance()->getAccounts();
    foreach( Account *account, accountsList )
    {
      // We are searching for a saved account with the autologin flag on
      if( account->isGuestAccount() || ! account->getUseAutologin() )
      {
        continue;
      }

      loginAccount = account;
      break;
    }
  }
  else
  {
#ifdef KMESSDEBUG_KMESS
  kDebug() << "finding account: " << handle << ".";
#endif
    loginAccount = AccountsManager::instance()->getAccountByHandle(handle);
  }

  bool doAutoLogin = ( loginAccount != 0 );

  // Wait for the user password if needed
  AccountsManager::instance()->readPasswords( doAutoLogin );

  if( doAutoLogin )
  {
    if( loginAccount->getPassword().isEmpty() )
    {
      KMessageBox::error( this,
                          i18nc( "dialog text",
                                 "<p>Cannot login automatically with account <b>%1</b>:<br/>"
                                 "you must first save the account password!</p>",
                                 loginAccount->getHandle() ),
                          i18nc( "dialog title", "Autologin Failed" ) );
      return;
    }

    // Set the login info in the initial view and connect
    // Using the reconnect() method here instead of startConnecting()
    // so KMess waits until the network is back up.
    // Also connect immediately if possible.
    initialView_->reconnect( loginAccount->getHandle(), true );

    // FIXME for 2.1: when using --autologin, don't display ".. to reconnect" but "to connect".
  }
}



// A connection has been made with the notification server.
void KMess::connected()
{
  // Enable/disable the menus
  enableMenus( true );

  // Set up the toggle and view mode tools to match the account settings
  showAllowedAction_   ->setChecked( currentAccount_->getShowAllowedContacts() );
  showOfflineAction_   ->setChecked( currentAccount_->getShowOfflineContacts() );
  showRemovedAction_   ->setChecked( currentAccount_->getShowRemovedContacts() );
  showEmptyAction_     ->setChecked( currentAccount_->getShowEmptyGroups    () );
  showHistoryBoxAction_->setChecked( currentAccount_->getShowHistoryBox     () );

  // Set the pictures mode
  int newMode = 0;
  switch( currentAccount_->getListPictureSize() )
  {
    case 0:
      break;
    case 32:
      newMode = 1;
      break;
    case 40:
      newMode = 2;
      break;
    case 48:
      newMode = 3;
      break;

    default:
      break;
  }

  listPictureSize_->setCurrentItem(  newMode  );

  // Set the list's groups and contacts display mode
  viewMode_->setCurrentItem( currentAccount_->getContactListDisplayMode() );

  // Check the 'Show search bar' option if the user has left it open the last login
  showSearchAction_->setChecked( currentAccount_->getShowSearchBar() );

  // Show the connected message
  statusMessage( i18n("Connected"), false );

  // The connection was successful, this means the supplied
  // email and password were correct: save the settings now
  if( ! currentAccount_->getSavePassword() )
  {
    // we reset the password here if it shouldn't be saved (so accountsmanager removes it)
#ifdef KMESSDEBUG_KMESS
    kDebug() << "Resetting password in currentAccount because it shouldn't be saved.";
#endif
    currentAccount_->setPassword( "" );
  }
  saveProperties();

  // Notify ChatMaster we're connected
  chatMaster_->connected();

  // Enable showing the settings for this account now that we are connected
  showSettingsAction_->setEnabled( true );

  // Load and show the contact list
  switchViewToContactList();

  // Set the caption
  setCaptionToUser();

  // Show the status bar (if it's enabled) now that we're connected
  showStatusBar();
}



// Connect to the server with the given account
void KMess::connectWithAccount(Account *account)
{
  if(KMESS_NULL(account)) return;

#ifdef KMESSDEBUG_KMESS
  kDebug() << "Connecting account:" << account->getHandle();
#endif

  startConnection( account );
}



// Connect to the server with the given account, possibly temporary or new.
void KMess::connectWithAccount(QString handle, QString password, bool rememberAccount, bool rememberPassword, bool autologin, Status initialStatus )
{
#ifdef KMESSDEBUG_KMESS
  kDebug() << "Connecting with handle " << handle;
#endif

  // Try to find an existing account
  Account *account = AccountsManager::instance()->getAccountByHandle(handle);
  if( account != 0 )
  {
    // Found an existing account.
    // The user may enter a new password at the login dialog.
    // When the login succeeds, save the new temporary password as new account password.
    account->setTemporaryPassword( password );
    account->setSavePassword( rememberPassword );
    account->setInitialStatus( initialStatus );

    if( rememberAccount && rememberPassword && autologin )
    {
      // Mark this account as the auto-login one
      const QList<Account*> accountsList = AccountsManager::instance()->getAccounts();
      foreach( Account *accountItem, accountsList )
      {
        // We are searching for a saved account with the autologin flag on
        if( accountItem->getUseAutologin() )
        {
          accountItem->setUseAutologin( false );
        }
      }

      account->setUseAutologin( true );
    }

    startConnection( account );
    return;
  }

  // No account found, create a new account.
  account = new Account();
  account->setLoginInformation( handle, handle, QString() );
  account->setTemporaryPassword( password );
  account->setGuestAccount( ! rememberAccount );
  account->setSavePassword( rememberPassword );
  account->setInitialStatus( initialStatus );
  AccountsManager::instance()->addAccount( account );

  if( rememberAccount && rememberPassword && autologin )
  {
    // Mark this account as the auto-login one
    const QList<Account*> accountsList = AccountsManager::instance()->getAccounts();
    foreach( Account *accountItem, accountsList )
    {
      // We are searching for a saved account with the autologin flag on
      if( accountItem->getUseAutologin() )
      {
        accountItem->setUseAutologin( false );
      }
    }

    account->setUseAutologin( true );
  }

  // Connect it
  startConnection( account );
}



// The user wants to reconnect
void KMess::reconnect()
{
#ifdef KMESSDEBUG
  kDebug() << "Reconnecting";
#endif
  connectWithAccount( currentAccount_ );
}



// Create the program's default directories in .kde/share/apps/
bool KMess::createDirectories()
{
#ifdef KMESSDEBUG_KMESS
  kDebug() << "Creating default directories.";
#endif
  KStandardDirs *dirs   = KGlobal::dirs();
  QString        localKdeDir;
  QDir           appsDir, kmessDir;

  localKdeDir = dirs->localkdedir();
#ifdef KMESSDEBUG_KMESS
  kDebug() << "Local KDE dir is " << localKdeDir << ".";
#endif
  appsDir.setPath( localKdeDir + "/share/apps" );

  if ( appsDir.exists() )
  {
    kmessDir.setPath( appsDir.absolutePath() + "/kmess" );
#ifdef KMESSDEBUG_KMESS
    kDebug() << "kmess dir should be at " << kmessDir.absolutePath() << ".";
#endif
    if ( !kmessDir.exists() )
    {
#ifdef KMESSDEBUG_KMESS
      kDebug() << "Creating kmess dir.";
#endif
      appsDir.mkdir( kmessDir.absolutePath() );
    }
  }
#ifdef KMESSDEBUG_KMESS
  else
  {
    kDebug() << "" << appsDir.absolutePath() << " doesn't exist!";
  }
#endif
#ifdef KMESSTEST
  KMESS_ASSERT( kmessDir.exists() );
#endif

  return true;
}



// "Add new account" has been selected from the menu.
void KMess::createNewAccount()
{
  AccountsManager::instance()->showAccountSettings();
}



// Delete the given account from the UI
void KMess::slotAccountDeleted( Account *account )
{
  AccountAction *accountAction;
  if( KMESS_NULL(account) ) return;

  // Remove it from the initial screen's list
  if( initialView_ )
  {
    initialView_->deleteAccount( account );
  }

  // Remove from connect menu.
  accountAction = connectMenuItems_[ account ];
  if( ! KMESS_NULL(accountAction) )
  {
    connectActionMenu_->removeAction( accountAction );
    delete connectMenuItems_.take( account );
  }
}



// The user was presented the "contact added user" dialog and has made a choice
void KMess::slotContactAddedUserDialogChoice( const QString &handle, const QStringList &groupIds, const int code )
{
#ifdef KMESSDEBUG_KMESS
  kDebug() << "Contact" << handle << "added user, who has choosen option:" << code
           << "Group list:" << groupIds;
#endif

  switch( (ContactAddedUserDialog::ReturnCode) code )
  {
    case ContactAddedUserDialog::ADD:
      // Add the contact to the allowed and friends lists
      msnNotificationConnection_->addNewContact( handle, groupIds );
      break;

    case ContactAddedUserDialog::ALLOW:
      // Add the contact to the allowed list
      msnNotificationConnection_->allowContact( handle );
      break;

    case ContactAddedUserDialog::BLOCK:
      // Add the contact to the blocked list.
      msnNotificationConnection_->blockContact( handle );
      break;
  }
}

/**
 * @brief Update the UI when the network connection status changes
 *
 * This method receives KDE's Solid network connection status changes, and updates the view's widgets
 * to reflect it.
 *
 * @param newStatus  The new status of the network connection
 */
void KMess::slotConnectionStatusChanged( Solid::Networking::Status newStatus )
{
#ifdef KMESSDEBUG_KMESSINTERFACE
  kDebug() << "Solid status changed to " << newStatus;
#endif

  // If Solid is unable to retrieve the status, assume we're connected
  bool isNetConnected = ( newStatus == Solid::Networking::Connected || newStatus == Solid::Networking::Unknown );

  // Do not disable the connection menu if we're connected already
  if( isConnected() )
  {
    // Do update the status message if the network is down
    if( newStatus == Solid::Networking::Unconnected )
    {
      // TODO: this slightly interferes with the "Pings lost..." message, and doesn't recover directly if solid indicates so.
      statusMessage( i18n("Connection could be down..."), false );
    }

    return;
  }

  // Update the menu
  connectActionMenu_->setEnabled( isNetConnected );
}



// Disconnect was selected from the menu.
void KMess::disconnectClicked()
{
#ifdef KMESSTEST
  KMESS_ASSERT( msnNotificationConnection_ != 0 );
#endif
#ifdef KMESSDEBUG_KMESSINTERFACE
  kDebug() << "Disconnecting.";
#endif
  msnNotificationConnection_->closeConnection();
}



// The program is not connected (initially) or no longer connected (after a disconnect) to the notification server.
void KMess::disconnected()
{
#ifdef KMESSTEST
  KMESS_ASSERT( chatMaster_ != 0 );
#endif
#ifdef KMESSDEBUG_KMESS
  kDebug() << "Disconnected.";
#endif

  // this method is also called when initializing the GUI, avoid mangling the settings at that point.
  if( initialized_ )
  {
    // ContactList is saved by the MsnNotificationConnection already.
    // A nice alternative would be currentAccount_->disconnected() saving the list,
    // however CurrentAccount has a const reference to the list
    // because MsnNotificationConnection maintains the list instead.

    // Save the account. If it is not found, it's the initial disconnect call.
    Account *account = AccountsManager::instance()->getAccountByHandle( currentAccount_->getHandle() );
    if( account )
    {
      if( account->isDeleted() )
      {
#ifdef KMESSDEBUG_KMESS
        kDebug() << "Account" << account->getHandle() << "was deleted, removing menu items.";
#endif
        slotAccountDeleted( account );
      }
      else
      {
#ifdef KMESSDEBUG_KMESS
        kDebug() << "Saving the CurrentAccount.";
#endif

        // Transfer account settings
        account->copyAccount( currentAccount_ );
      }
    }
  }

  // Update the GUI
  disconnect_->setEnabled( false );
  enableMenus(false);
  statusMessage( i18nc( "Status bar message", "Disconnected"), true );
  showSearchAction_->setChecked( false );
  showSettingsAction_->setEnabled( false );

  // Hide the status bar now that we're not connected
  if( showStatusBar_->isChecked() )
  {
    statusBar()->hide();
  }

  // Inform the chat master that we're disconnected
  chatMaster_->disconnected();

  // Update the current account
  currentAccount_->setStatus( STATUS_OFFLINE );
  currentAccount_->setAutoreply( false );
#ifdef KMESSDEBUG_KMESS
  kDebug() << "Swap views.";
#endif

  // Show the screen where to insert login details to log back in
  switchViewToInitialScreen();

#ifdef KMESSDEBUG_KMESS
  kDebug() << "Blank the caption.";
#endif

  // Set the caption
  setCaption( QString::null );
}



// Initialize the class
bool KMess::initialize()
{
  if ( initialized_ )
  {
    kDebug() << "Already initialized.";
    return false;
  }

#ifdef KMESSDEBUG_KMESS
  kDebug() << "initializing main class.";
#endif

  // Initialize the Rich Text parser engine
  RichTextParser::initialize();

  // Create current account instance
  currentAccount_ = CurrentAccount::instance();
  if ( currentAccount_ == 0 )
  {
    kDebug() << "Couldn't get a pointer to the instance of the current account.";
    return false;
  }
  if ( !KMessInterface::initialize() )
  {
    kDebug() << "Couldn't initialize ancestor.";
    return false;
  }
  if ( !initMsnNotificationConnection() )
  {
    kDebug() << "Couldn't initialize the MsnNotificationConnection class.";
    return false;
  }
  if ( !initIdleTimer() )
  {
    kDebug() << "Couldn't initialize the IdleTimer class.";
    return false;
  }
  if ( !initChatMaster() )
  {
    kDebug() << "Couldn't initialize the Chat master.";
    return false;
  }
  if ( !initNotifications() )
  {
    kDebug() << "Couldn't initialize the notifications.";
    return false;
  }
  if ( !initNowListening() )
  {
    kDebug() << "Couldn't initialize the now listening support.";
    return false;
  }
  if ( !initEmoticonManager() )
  {
    kDebug() << "Couldn't initialize the emoticon manager.";
    return false;
  }
  if ( !initProxy() )
  {
    kDebug() << "Couldn't initialize the proxy support.";
    return false;
  }
  if ( !createDirectories() )
  {
    kDebug() << "Couldn't create local KDE directories.";
    return false;
  }

  // Use Solid to find out whether we're connected or not, and disable the Connect menu if we are not
  connect( Solid::Networking::notifier(), SIGNAL(               statusChanged(Solid::Networking::Status) ),
           this,                          SLOT  ( slotConnectionStatusChanged(Solid::Networking::Status) ) );
  slotConnectionStatusChanged( Solid::Networking::status() );

  // Connect current account signals for Now Listening
  connect( currentAccount_, SIGNAL(            accountInvisible() ),
           this,            SLOT  ( changedNowListeningSettings() ));
  connect( currentAccount_, SIGNAL(              accountOffline() ),
           this,            SLOT  ( changedNowListeningSettings() ));
  connect( currentAccount_, SIGNAL(               accountOnline() ),
           this,            SLOT  ( changedNowListeningSettings() ));

  // Connect current account
  connect( currentAccount_, SIGNAL(         changedFriendlyName() ),
           this,            SLOT  (            setCaptionToUser() ));
  connect( currentAccount_, SIGNAL( changedNowListeningSettings() ),
           this,            SLOT  ( changedNowListeningSettings() ));
  connect( currentAccount_, SIGNAL(               changedStatus() ),
           this,            SLOT  (               changedStatus() ));
  connect( currentAccount_, SIGNAL(            changedMsnObject() ),
           msnNotificationConnection_, SLOT(   changedMsnObject() ));
  connect( currentAccount_, SIGNAL(     changedEmoticonSettings() ),
           EmoticonManager::instance(),SLOT(slotChangedEmoticonSettings() ) );

  // Connect the account management signals
  AccountsManager *accountsManager = AccountsManager::instance();
  connect( accountsManager, SIGNAL(       accountAdded(Account*)                 ),
           this,            SLOT  (   slotAccountAdded(Account*)                 ) );
  connect( accountsManager, SIGNAL(     accountChanged(Account*,QString,QString) ),
           this,            SLOT  ( slotAccountChanged(Account*,QString,QString) ) );
  connect( accountsManager, SIGNAL(     accountDeleted(Account*)                 ),
           this,            SLOT  ( slotAccountDeleted(Account*)                 ) );
  // Fill up the account lists in the UI
  const QList<Account*> list( accountsManager->getAccounts() );
  foreach( Account *account, list )
  {
    slotAccountAdded( account );
  }

  // Connect the action to show the connected account settings
  connect( showSettingsAction_, SIGNAL(                     triggered(bool) ),
           this,                SLOT  ( showSettingsForCurrentAccount()     ) );

  // Read the accounts and properties
  readProperties();

  // Start disconnected.
  // Added debug here because this has a potential to cause problems..
  // It would be better if each class got things right from it's construtor or initialize method.
#ifdef KMESSDEBUG_KMESS
  kDebug() << "faking disconnect signal.";
#endif
  disconnected();

  // Setting up DBus
  dbus_ = new KMessDBus( this );

  // All done, mark as initialized
#ifdef KMESSDEBUG_KMESS
  kDebug() << "main class is initialized.";
#endif

  initialized_ = true;
  return true;
}



// Initialize the chat master
bool KMess::initChatMaster()
{
#ifdef KMESSTEST
  KMESS_ASSERT( chatMaster_ == 0 );
#endif
  if ( msnNotificationConnection_ == 0 )
  {
    kDebug() << "The notification connection must be initialized before the chat master.";
    return false;
  }
  chatMaster_ = new ChatMaster( this );
  if ( chatMaster_ == 0 )
  {
    kDebug() << "Couldn't create the chat master.";
    return false;
  }
  if ( !chatMaster_->initialize() )
  {
    kDebug() << "Couldn't initialize the chat master.";
    return false;
  }

  // Make the chat master's connections
  connect( chatMaster_,                SIGNAL( requestSwitchboard(QString,ChatInformation::ConnectionType) ),
           msnNotificationConnection_, SLOT  ( requestSwitchboard(QString,ChatInformation::ConnectionType) ));
  connect( chatMaster_,                SIGNAL(       blockContact(QString) ),
           msnNotificationConnection_, SLOT  (       blockContact(QString) ) );
  connect( chatMaster_,                SIGNAL(     unblockContact(QString) ),
           msnNotificationConnection_, SLOT  (     unblockContact(QString) ) );
  connect( chatMaster_,                SIGNAL(         addContact(QString) ),
           msnNotificationConnection_, SLOT  (      addNewContact(QString) ) );
  connect( chatMaster_,                SIGNAL(      removeContact(QString,bool) ),
           msnNotificationConnection_, SLOT  (      removeContact(QString,bool) ) );
  connect( chatMaster_,                SIGNAL(       allowContact(QString) ),
           msnNotificationConnection_, SLOT  (       allowContact(QString) ) );

  connect( msnNotificationConnection_, SIGNAL(   startSwitchboard(const ChatInformation&) ),
           chatMaster_,                SLOT  (   startSwitchboard(const ChatInformation&) ) );
  connect( msnNotificationConnection_, SIGNAL(     offlineMessage(const ChatMessage&) ),
           chatMaster_,                SLOT  ( showSpecialMessage(const ChatMessage&) ));
  connect( msnNotificationConnection_, SIGNAL(           pingSent() ),
           chatMaster_,                SLOT  (        timedUpdate() ));

  connect( chatMaster_,                SIGNAL(          reconnect() ),
           this,                       SLOT  (          reconnect() ));

#ifdef KMESSTEST
  KMESS_ASSERT( chatMaster_ != 0 );
#endif
  return true;
}



// Initialize the emoticon manager
bool KMess::initEmoticonManager()
{
  if ( msnNotificationConnection_ == 0 )
  {
    kDebug() << "MsnNotificationConnection must be initialized before EmoticonManager";
    return false;
  }

  EmoticonManager *manager = EmoticonManager::instance();

  connect( msnNotificationConnection_, SIGNAL(     connected() ),
           manager,                    SLOT  (     connected() ) );
  connect( msnNotificationConnection_, SIGNAL(  disconnected() ),
           manager,                    SLOT  (  disconnected() ) );

  return true;
}



// Initialize the idle timer
bool KMess::initIdleTimer()
{
#ifdef KMESSTEST
  KMESS_ASSERT( idleTimer_ == 0 );
#endif
  idleTimer_ = new IdleTimer();
  if ( idleTimer_ == 0 )
  {
    kDebug() << "Couldn't create the idle timer.";
    return false;
  }
  // Connect the timer to signal when the user is away.
  connect( idleTimer_, SIGNAL( timeout()       ),
           this,       SLOT  ( userIsIdle() )    );
  connect( idleTimer_, SIGNAL( activity()      ),
           this,       SLOT  ( userIsNotIdle() ) );
#ifdef KMESSTEST
  KMESS_ASSERT( idleTimer_ != 0 );
#endif
  return true;
}



// Initialize the MSN notification connection
bool KMess::initMsnNotificationConnection()
{
  bool initialized;

  msnNotificationConnection_ = new MsnNotificationConnection();

  initialized = msnNotificationConnection_->initialize();
  if ( !initialized )
  {
    kDebug() << "Couldn't initialize MsnNotificationConnection.";
    return false;
  }

  // Connect the signals
  connect( msnNotificationConnection_, SIGNAL(                  connected()               ),   // Connected to msn
           this,                       SLOT  (                  connected()               ) );
  connect( msnNotificationConnection_, SIGNAL(               disconnected()               ),   // Disconnected from msn
           this,                       SLOT  (               disconnected()               ) );
  connect( msnNotificationConnection_, SIGNAL(           contactAddedUser(const QString) ),   // Contact added user
           this,                       SLOT  ( showContactAddedUserDialog(const QString) ) );
  connect( msnNotificationConnection_, SIGNAL(              statusMessage(QString,bool)   ),   // Display a status message
           this,                       SLOT  (              statusMessage(QString,bool)   ) );

  return true;
}



// Initialize notification objects
bool KMess::initNotifications()
{
#ifdef KMESSTEST
  KMESS_ASSERT( currentAccount_             != 0 );
  KMESS_ASSERT( systemTrayWidget_           != 0 );
  KMESS_ASSERT( msnNotificationConnection_  != 0 );

  KMESS_ASSERT( chatNotification_           == 0 );
  KMESS_ASSERT( contactStatusNotification_  == 0 );
  KMESS_ASSERT( newEmailNotification_       == 0 );
#endif

  const ContactList *contactList;

  if ( systemTrayWidget_ == 0 )
  {
    kDebug() << "The system tray widget must be initialized before the notifications!";
    return false;
  }
  if ( msnNotificationConnection_ == 0 )
  {
    kDebug() << "The notification connection must be initialized before the notifications!";
    return false;
  }


  // Add check whether the 'eventsrc' file can be found.
  // The warning is a bit obtruisive, but should help to user to fix the problem.
  if( KGlobal::dirs()->findResource( "data", "kmess/kmess.notifyrc" ).isNull() )
  {
    QString dirs( KGlobal::dirs()->findDirs( "data", QString() ).join("/kmess<br>") );
    if( ! dirs.isEmpty() )
    {
      dirs = "<br>" + dirs + "/kmess";
      dirs = dirs.replace( "//kmess", "/kmess" );  // possibly added by /kmess suffix.
      dirs = i18nc( "Paragraph to be added to the text of a message dialog box, but only when KDE gives a list of "
                    "folders where to search for an application file",
                    "<p>KMess has searched for it in the following folders:<br>%1</p>",
                    dirs );
    }

    // Always show warning at the console.
    kWarning() << "Sounds and notifications will not be available, the file 'kmess/kmess.notifyrc' could not be found.";

    // Show the message in the GUI as well.
    // Allow to user to choose "don't show this message again", in case he/she doesn't know how to fix it.
    KMessageBox::information( this,
                              i18nc( "Text for a message dialog box; %1 is an explanation about the list of folders "
                                     "where the file was searched for, which is only shown if any folders are found",
                                     "<html><p>KMess will not be able to play sounds nor show notifications.</p>"
                                     "<p>The required file 'kmess.notifyrc' could not be found in any application folder.</p>"
                                     "%1"
                                     "<p>Please verify your installation.</p>"
                                     "</html>",
                                     dirs ),
                              i18nc( "Message box title", "Error With Notifications" ),
                              "eventsrcNotFound" );
  }


  contactList = currentAccount_->getContactList();
  notificationManager_ = NotificationManager::instance();
  notificationManager_->setTrayObject( systemTrayWidget_ );

  // Create the notifiers
  chatNotification_          = new ChatNotification         ( notificationManager_ );
  contactStatusNotification_ = new ContactStatusNotification( notificationManager_ );
  newEmailNotification_      = new NewEmailNotification     ( notificationManager_ );

    // Connect the chat messages notification signals
  connect( chatMaster_,                 SIGNAL( newChatMessage(const ChatMessage&, Chat*)                   ),
           chatNotification_,           SLOT  (         notify(const ChatMessage&, Chat*)                   ) );
  connect( chatNotification_,           SIGNAL(      raiseChat(Chat*,bool)                                  ),
           chatMaster_,                 SLOT  (      raiseChat(Chat*,bool)                                  ) );

    // Connect the status change notification signals
  connect( contactList,                 SIGNAL(        contactOnline(Contact*,bool)                          ),
           contactStatusNotification_,  SLOT  (               notify(Contact*,bool)                          ) );
  connect( contactList,                 SIGNAL(       contactOffline(Contact*,bool)                          ),
           contactStatusNotification_,  SLOT  (               notify(Contact*,bool)                          ) );
  connect( contactList,                 SIGNAL( contactChangedStatus(Contact*,bool)                          ),
           contactStatusNotification_,  SLOT  (               notify(Contact*,bool)                          ) );

    // Connect the "Email notification" signals
  connect( msnNotificationConnection_,  SIGNAL( newEmail(QString, QString, bool, QString, QString, QString) ),
           newEmailNotification_,       SLOT  (   notify(QString, QString, bool, QString, QString, QString) ) );

  // Connect the "Chat request" signals
  connect( contactStatusNotification_,  SIGNAL(   startChat(QString) ),
                          chatMaster_,  SLOT  ( requestChat(QString) ) );


#ifdef KMESSTEST
  KMESS_ASSERT( chatNotification_          != 0 );
  KMESS_ASSERT( contactStatusNotification_ != 0 );
  KMESS_ASSERT( newEmailNotification_      != 0 );
#endif

  return true;
}



// Initialize the now listening support.
bool KMess::initNowListening()
{
  if ( msnNotificationConnection_ == 0 )
  {
    kDebug() << "The notification connection must be initialized before the now listening support!";
    return false;
  }

  // Create and connect client.
  nowListeningClient_ = new NowListeningClient();
  connect( nowListeningClient_, SIGNAL( changedSong( const QString&, const QString&, const QString&, bool ) ),
           this,                SLOT  ( changedSong( const QString&, const QString&, const QString&, bool ) ));

  return true;
}



// Initialize the proxy support.
bool KMess::initProxy()
{
  // NOTE: Due to the fact that KDE proxy settings do not have a signal to notify proxy
  // config changes, KMess will need to be restarted if the user changes them.

  // No proxy, done already!
  if( ! KProtocolManager::useProxy() )
  {
#ifdef KMESSDEBUG_KMESS
    kDebug() << "No proxy support is needed (kde's proxy is turned off).";
#endif
    return true;
  }

  QString proxyAddress( KProtocolManager::proxyForUrl( KUrl( "https://www.kmess.org/" ) ) );

  // No need to use the proxy for HTTPS traffic
  if( proxyAddress.isEmpty() || proxyAddress == "DIRECT" )
  {
#ifdef KMESSDEBUG_KMESS
    kDebug() << "No proxy support is needed (no proxy is needed for HTTPS traffic).";
#endif
    return true;
  }

#ifdef KMESSDEBUG_KMESS
  kDebug() << "Proxy URL:" << proxyAddress;
#endif

  KUrl proxyUrl( proxyAddress );
  QNetworkProxy::ProxyType proxyType = QNetworkProxy::NoProxy;

  // Configure the proxy
  if( proxyUrl.protocol() == "socks" )
  {
    proxyType = QNetworkProxy::Socks5Proxy;
  }
  else
  {
    proxyType = QNetworkProxy::HttpProxy;
  }

  // Set the default proxy for the whole application
  QNetworkProxy appProxy( proxyType,
                          proxyUrl.host(),
                          (quint16) proxyUrl.port(),
                          proxyUrl.user(),
                          proxyUrl.pass() );
  QNetworkProxy::setApplicationProxy( appProxy );

  return true;
}



// Check if the connection to the server is active
bool KMess::isConnected()
{
  if( KMESS_NULL(msnNotificationConnection_) )
  {
    return false;
  }

  return msnNotificationConnection_->isConnected();
}


// Load the application's global state
void KMess::readGlobalProperties( KConfig *sessionConfig )
{
#ifdef KMESSDEBUG_KMESS
  kDebug() << "Loading current state...";
#endif

  KConfigGroup settings( sessionConfig->group( "state" ) );

  // If the list window was visible, show it again
  setVisible( settings.readEntry( "isVisible", false ) );

  // If an account was connected, try connecting it again
  QString connectedAccount( settings.readEntry( "connectedAccount", QString() ) );

  // Delete the setting, if needed it'll be added again on session close
  settings.deleteEntry( "connectedAccount" );

  checkAutologin( connectedAccount );
}


// Save the application's global state
void KMess::saveGlobalProperties( KConfig *sessionConfig )
{
#ifdef KMESSDEBUG_KMESS
  kDebug() << "Saving current state...";
#endif

  KConfigGroup settings( sessionConfig->group( "state" ) );

  QString connectedAccount;
  if( isConnected() )
  {
    connectedAccount = currentAccount_->getHandle();
  }

  settings.writeEntry( "isVisible",        isVisible()      );
  settings.writeEntry( "connectedAccount", connectedAccount );
}



// Read in account and other properties
void KMess::readProperties( const KConfigGroup &config )
{
#ifdef KMESSDEBUG_KMESS
  kDebug() << "Reading properties";
#endif

  KConfigGroup group;

  // Choose the config group if it was not given
  if( &config == 0 )
  {
    group = KMessConfig::instance()->getGlobalConfig( "General" );
  }
  else
  {
    group = config;
  }

  KMessInterface::readProperties( group );
}



// Save account and other properties
void KMess::saveProperties( KConfigGroup &config )
{
  KConfigGroup group;

  // Choose the config group if it was not given
  if( &config == 0 )
  {
    group = KMessConfig::instance()->getGlobalConfig( "General" );
  }
  else
  {
    group = config;
  }

  // Save the UI configuration
  KMessInterface::saveProperties( group );

#ifdef KMESSDEBUG_KMESS
  kDebug() << "saving properties";
#endif

  // Have all the accounts save their properties
  const QList<Account*> accountsList = AccountsManager::instance()->getAccounts();
  foreach( Account *account, accountsList )
  {
    // Avoid saving guest accounts
    if( account->isGuestAccount() || account->isDeleted() )
    {
      continue;
    }

    // If the account is the current account...
    if ( account->getHandle() == currentAccount_->getHandle() )
    {
      // Move the settings from the current account to the account
#ifdef KMESSDEBUG_KMESS
      kDebug() << "copying settings from CurrentAccount to the Account object.";
#endif
      account->copyAccount( currentAccount_ );
    }

    // Save properties of accounts
    account->saveProperties();
  }

  // Save the accounts' passwords (blocking, or KMess may quit before it's done)
  AccountsManager::instance()->savePasswords( true );

  // Write data now!
  group.sync();
}



// Set the caption
void KMess::setCaptionToUser()
{
  // On start, use the default caption
  if( currentAccount_ == 0 )
  {
    setCaption( QString() );
    return;
  }

  // Get the friendly name, but avoid newlines which cause problems to the titlebar layout
  QString friendlyName( currentAccount_->getFriendlyName().replace( "\n", " " ) );

  // Set a standard caption if there is no friendly name yet (ie. first connection)
  if( friendlyName.isEmpty() )
  {
    setCaption( QString() );
    return;
  }

  // Set the caption
  setPlainCaption( i18nc( "Main window caption: switched order to easily distinguish it from chats", "KMess - %1", friendlyName ) );
}



// Show the settings dialog for a given account
void KMess::showSettingsForAccount( Account *account )
{
  AccountsManager::instance()->showAccountSettings( account );
}



// Show the settings dialog for the current account.
void KMess::showSettingsForCurrentAccount()
{
  // Use the Account object so it's consistent with the other AccountAction events.
  Account *accountData = AccountsManager::instance()->getAccountByHandle( CurrentAccount::instance()->getHandle() );
  if( KMESS_NULL(accountData) ) return;
  showSettingsForAccount( accountData );
}



// Show the user's MSN profile.
void KMess::showUserProfile()
{
  if( currentAccount_ == 0 )
  {
    return;
  }

  const QString url( currentAccount_->getUrlInformation().value( "PROFILE" ) );
  KMessShared::openBrowser( KUrl( url ) );
}



// Initiate a connection to the server. Use connectWithAccount() to connect, not this method.
void KMess::startConnection( Account *account )
{
  if( KMESS_NULL(account) ) return;

  // First disconnect, to unregister all contacts
  if( msnNotificationConnection_->isConnected() )
  {
    msnNotificationConnection_->closeConnection();
  }

  // Update the initial view with account details and start the connection if everything is ok
  if( ! initialView_->startConnecting( account->getHandle(), false /* emitConnectionSignal */ ) )
  {
    show();
    return;
  }

  // Update the main view's controls to match this account's details.
  changedStatus( account );

  // Copy the account to the current account
#ifdef KMESSDEBUG_KMESS
  kDebug() << "copying data of account" << account->getHandle() << "to the CurrentAccount object.";
#endif
  currentAccount_->copyAccount( account );

  // Allow users to cancel the login attempt
  disconnect_->setEnabled( true );

  // Set UI state of any open chat tabs to "Connecting"
  chatMaster_->connecting();

  // Connect to the server
  msnNotificationConnection_->openConnection();
}



// Switch the view to the contact list
void KMess::switchViewToContactList()
{
  // Clean up views beforehand.
  // Whatever was the previous centralWidget will be deleted below. We mark
  // any objects that still exist here, for deletion, and set them to zero.
  // See also ::switchViewToInitialScreen
  if( view_ )
  {
    view_->deleteLater();
    view_ = 0;
  }
  if( initialView_ )
  {
    initialView_->deleteLater();
    initialView_ = 0;
  }

#ifdef KMESSTEST
  KMESS_ASSERT( msnNotificationConnection_ != 0 );
#endif

  // Create and initialize the view's widgets
  view_ = new KMessView( this );
  view_->initialize( msnNotificationConnection_->getContactListModel() );

  // Connect other signals to msnNotificationConnection
  connect( msnNotificationConnection_, SIGNAL(    disconnected()                        ),   // The connection was closed
           view_,                      SLOT  (slotDisconnected()                        ) );
  connect( view_,                      SIGNAL(    addContact(QString)                   ),   // Add a contact
           msnNotificationConnection_, SLOT  (addExistingContact(QString)               ) );
  connect( view_,                      SIGNAL(  allowContact(QString)                   ),   // Allow the contact
           msnNotificationConnection_, SLOT  (  allowContact(QString)                   ) );
  connect( view_,                      SIGNAL(  blockContact(QString)                   ),   // Block the contact
           msnNotificationConnection_, SLOT  (  blockContact(QString)                   ) );
  connect( view_,                      SIGNAL( changeFriendlyName(QString)              ),   // Change the friendly name
           msnNotificationConnection_, SLOT  ( changeFriendlyName(QString)              ) );
  connect( view_,                      SIGNAL( changePersonalMessage(QString)           ),   // Change the personal message.
           msnNotificationConnection_, SLOT  ( changePersonalMessage(QString)           ) );
  connect( view_,                      SIGNAL(       copyContact(QString, QString)      ),
           msnNotificationConnection_, SLOT  ( addContactToGroup(QString, QString)      ) );
  connect( view_,                      SIGNAL( moveContact(QString, QString, QString)   ),   // Move the contact
           msnNotificationConnection_, SLOT  ( moveContact(QString, QString, QString)   ) );
  connect( view_,                      SIGNAL( showSettings()                           ),   // Show settings
           this,                       SLOT  ( showSettingsForCurrentAccount()          ) );
  connect( view_,                      SIGNAL(requestChat( QString )                    ),   // Start a chat
           chatMaster_,                SLOT  (requestChat( QString )                    ) );
  connect( view_,                      SIGNAL( removeContact(QString)                   ),   // Remove the contact
           this,                       SLOT  ( showRemoveContactDialog(QString)         ) );
  connect( view_,                      SIGNAL( removeContactFromGroup(QString, QString) ),
           msnNotificationConnection_, SLOT  ( removeContactFromGroup(QString, QString) ) );
  connect( view_,                      SIGNAL(   removeGroup(QString)                   ),   // Remove the group
           this,                       SLOT  ( showRemoveGroupDialog(QString)           ) );
  connect( view_,                      SIGNAL(   renameGroup(QString)                   ),   // Rename the group
           this,                       SLOT  ( showRenameGroupDialog(QString)           ) );
  connect( view_,                      SIGNAL( selectionChanged(const QItemSelection&)  ),   // The selection has changed
           this,                       SLOT  (updateContextMenu(const QItemSelection&)  ) );
  connect( view_,                      SIGNAL( unblockContact(QString)                  ),   // Unblock the contact
           msnNotificationConnection_, SLOT  ( unblockContact(QString)                  ) );

  // this is horrible. i'm glad CurrentAccount is gone in 2.1!
  Account *realAccount = AccountsManager::instance()->getAccountByHandle( currentAccount_->getHandle() );
  connect( realAccount, SIGNAL( changedEmoticonSettings() ), view_, SLOT( slotUpdateUserStatus() ) );

  // Set it as the application's main widget
  setCentralWidget( view_ );
}



// Switch the view to the initial login screen
void KMess::switchViewToInitialScreen()
{
  // Just use the same initialView_ if it's already there
  if( initialView_ )
  {
    initialView_->reset();
    return;
  }

  // Clean up view_ if it exists
  if( view_ )
  {
    view_->deleteLater();
    view_ = 0;
  }

  // Create and set up the new widget
  initialView_ = new InitialView( this );

  // Connect it so it triggers the login process
  connect( initialView_, SIGNAL( connectWithAccount(QString,QString,bool,bool,bool,Status) ),
           this,         SLOT  ( connectWithAccount(QString,QString,bool,bool,bool,Status) ) );
  connect( initialView_, SIGNAL(                                  disconnectClicked() ),
           this,         SLOT  (                                  disconnectClicked() ) );

  // Show settings
  connect( initialView_, SIGNAL( showSettings(Account*)                               ),
           this,         SLOT  ( showSettingsForAccount(Account*)                     ) );

  connect( msnNotificationConnection_, SIGNAL(     reconnect(QString)      ),
           initialView_,               SLOT  (     reconnect(QString)      ) );
  connect( msnNotificationConnection_, SIGNAL( statusMessage(QString,bool) ),
           initialView_,               SLOT  ( statusMessage(QString)      ) );


  // Set it as the window's main widget
  setCentralWidget( initialView_ );
}



// The "show allowed contacts" menu item has been toggled.
void KMess::toggleShowAllowed(bool show)
{
  currentAccount_->setShowAllowedContacts( show );
}



// The "show empty groups" menu item has been toggled.
void KMess::toggleShowEmpty( bool show )
{
  currentAccount_->setShowEmptyGroups( show );
}



// The "show history box" menu item has been toggled.
void KMess::toggleShowHistoryBox(bool show)
{
  if( view_ != 0 )
  {
    view_->toggleShowHistoryBox( show );
  }
}



// The "show search in contact list" menu item has been toggled.
void KMess::toggleShowSearchFrame(bool show)
{
  if( view_ != 0 )
  {
    view_->toggleShowSearchFrame( show );
  }
}


// The "show offline contacts" menu item has been toggled.
void KMess::toggleShowOffline(bool show)
{
  currentAccount_->setShowOfflineContacts( show );
}



// The "show removed contacts" menu item has been toggled.
void KMess::toggleShowRemoved(bool show)
{
  currentAccount_->setShowRemovedContacts( show );
}



// Show the context-sensitive menu item
void KMess::updateContextMenu( const QItemSelection &selection )
{
  contextMenuAction_->setEnabled( ! selection.indexes().isEmpty() );
}



// The user has gone idle
void KMess::userIsIdle()
{
#ifdef KMESSDEBUG_IDLETIMER
    kDebug();
#endif
  if ( ( currentAccount_ != 0 ) && ( msnNotificationConnection_ != 0 ) )
  {
#ifdef KMESSDEBUG_IDLETIMER
    kDebug() << "Current status is " << currentAccount_->getStatus() << ".";
#endif
    // Only change the state if the user is currently set as online
    if ( currentAccount_->getStatus() == STATUS_ONLINE )
    {
#ifdef KMESSDEBUG_IDLETIMER
      kDebug() << "Change status to Idle.";
#endif
      // Request a status change to Idle
      msnNotificationConnection_->changeStatus( STATUS_IDLE );
      isIdleForced_ = false;
    }
  }
}



// The user is no longer idle
void KMess::userIsNotIdle()
{
#ifdef KMESSDEBUG_IDLETIMER
    kDebug();
#endif
  if ( ( currentAccount_ != 0 ) && ( msnNotificationConnection_ != 0 ) )
  {
#ifdef KMESSDEBUG_IDLETIMER
    kDebug() << "User is no longer idle, current status is " << currentAccount_->getStatus();
#endif
    // Only change the state if the user is currently set as idle AND the idle status was not forced by the user
    if ( currentAccount_->getStatus() == STATUS_IDLE && ! isIdleForced_ )
    {
#ifdef KMESSDEBUG_IDLETIMER
      kDebug() << "Change status to Online";
#endif
      // Request a status change to Online
      msnNotificationConnection_->changeStatus( STATUS_ONLINE );
    }
  }
#ifdef KMESSDEBUG_IDLETIMER
    kDebug() << "Done userIsNotIdle().";
#endif
}


// return NS Connection
// HACK: Remove after 2.0-final.
MsnNotificationConnection *KMess::getNsConnection()
{
  return msnNotificationConnection_;
}
#include "kmess.moc"
