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

#include <kaction.h>
#include <kconfig.h>
#include <kdebug.h>
#include <klistview.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <kpopupmenu.h>
#include <krun.h>
#include <kstandarddirs.h>
#include <kurl.h>

#if KDE_IS_VERSION(3,2,0)
#include <kinputdialog.h>
#else
#include <klineeditdlg.h>
#endif

#include "actions/accountaction.h"
#include "chat/chatmaster.h"
#include "chat/chatwindow.h"
#include "contact/contact.h"
#include "contact/contactlist.h"
#include "contact/group.h"
#include "dialogs/contactaddeduserdialoginterface.h"
#include "dialogs/contactaddeduserdialog.h"
#include "dialogs/awaymessagedialog.h"
#include "dialogs/networkwindow.h"
#include "dialogs/transferwindow.h"
#include "network/msnnotificationconnection.h"
#include "notification/chatnotification.h"
#include "notification/contactofflinenotification.h"
#include "notification/contactonlinenotification.h"
#include "notification/emailnotification.h"
#include "notification/balloonwidget.h"
#include "settings/settingsdialog.h"
#include "account.h"
#include "autologinview.h"
#include "currentaccount.h"
#include "idletimer.h"
#include "initialview.h"
#include "kmessdebug.h"
#include "kmessinterface.h"
#include "kmessview.h"
#include "systemtraywidget.h"

// The constructor
KMess::KMess(QWidget *parent, const char *name)
 : KMessInterface(parent, name),
   autologinView_(0),
   chatMaster_(0),
   chatNotification_(0),
   contactOfflineNotification_(0),
   contactOnlineNotification_(0),
   emailNotification_(0),
   idleTimer_(0),
   initialized_(false),
   initialView_(0),
   msnNotificationConnection_(0),
   view_(0)
{
#ifdef KMESS_NETWORK_WINDOW
  networkWindow_ = 0;
#endif
}



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

  Account *account;

  /*
   * 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( kapp->config() );

  // 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 view_;
  delete initialView_;
  delete autologinView_;
  delete balloonWidget_;
  delete chatNotification_;
  delete contactOfflineNotification_;
  delete contactOnlineNotification_;
  delete emailNotification_;
  delete systemTrayWidget_;
  delete msnNotificationConnection_;
  CurrentAccount::destroy();

  // Delete the accounts
  for ( account = accounts_.first(); account; account = accounts_.next() )
  {
    delete account;
  }
  // Delete the unsaved accounts
  for ( account = unsavedAccounts_.first(); account; account = unsavedAccounts_.next() )
  {
    delete account;
  }
#ifdef KMESSDEBUG_KMESS
  kdDebug() << "DESTROYED KMess" << endl;
#endif
}



// The application is closing, after queryExit() was approved
void KMess::applicationClosing()
{
  if(chatMaster_ != 0)
  {
    // Close the chat windows earlier, so logs are saved.
    chatMaster_->disconnected();
    chatMaster_->deleteLater();
    chatMaster_ = 0;
  }
}



// Add an account to the settings and connect menus
void KMess::addAccountToMenus(Account *account)
{
  AccountAction *accountAction;

  // Make the action for the "Connect..." menu
  accountAction = new AccountAction( account, this, account->getFriendlyName() );

  // Connect the action to the slot it needs to call, namely
  //  one that needs to start a connection to the server using
  //  this account.
  connect ( accountAction, SIGNAL(          activated(Account*) ),
            this,          SLOT  ( connectWithAccount(Account*) ) );
  // Insert the action into the "Connect" menu
  connectActionMenu_->insert( accountAction );

  // Make the action for the "Settings..." menu
  accountAction = new AccountAction( account, this,  account->getFriendlyName() );

  // Connect the action to the slot it needs to call, namely
  //  one that needs to start a connection to the server using
  //  this account.
  connect ( accountAction, SIGNAL(              activated(Account*) ),
            this,          SLOT  ( showSettingsForAccount(Account*) ) );
  // Insert the action into the "Connect" menu
  settingsActionMenu_->insert( accountAction );

  // Add the account to the initial view's dropdown list.
  initialView_->addAccount( account );
}



// "Add a new contact" was selected from the menu.
void KMess::addNewContact()
{
#ifdef KMESSTEST
  ASSERT( msnNotificationConnection_ != 0 );
#endif
#ifdef KMESSDEBUG_KMESS
  kdDebug() << "KMess::addNewContact()" << endl;
#endif
  QString newHandle;
  bool    okPressed = false;

  // Launch a dialog to get a new group name
#if KDE_IS_VERSION(3,2,0)
  newHandle = KInputDialog::getText(i18n("Add a contact"),
                                    i18n("Enter the email address of the person you wish to add:"),
                                    newHandle, &okPressed, this);
#else
  newHandle = KLineEditDlg::getText(i18n("Add a contact"),
                                    i18n("Enter the email address of the person you wish to add:"),
                                    newHandle, &okPressed, this);
#endif

  if(okPressed)
  {
    // Ask the server to add the contact
    msnNotificationConnection_->addNewContact(newHandle);
  }
}



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

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

  // Launch a dialog to get a new group name
#if KDE_IS_VERSION(3,2,0)
  newGroupName = KInputDialog::getText(i18n("Add a group"),
                                       i18n("Enter the new name for this group:"),
                                       newGroupName, &okPressed, this);
#else
  newGroupName = KLineEditDlg::getText(i18n("Add a group"),
                                       i18n("Enter the new name for this group:"),
                                       newGroupName, &okPressed, this);
#endif

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



// A status was selected from the menu.
void KMess::changeStatus(const QString &statusName)
{
  QString            newStatus = "";
  bool               useAutoreply;
  AwayMessageDialog *awayMessageDialog;
  QString            awayMessage;

#ifdef KMESSDEBUG_KMESS
  kdDebug() << "Selected status " << statusName << endl;
#endif
  currentAccount_->setAutoreply( false );
  if ( statusName == i18n("Away") )
  { // Change status to AWAY
    newStatus = "AWY";
  }
  else if ( statusName == i18n("Away with auto-reply") )
  { // Change status to AWAY
    newStatus = "AWY";
    // Create the away message dialog
    awayMessageDialog = new AwayMessageDialog( this, "awaymessagedialog" );
    // Confirm the auto-away message with the user
    awayMessage = CurrentAccount::instance()->getAutoreplyMessage();
    useAutoreply = false;
    useAutoreply = awayMessageDialog->useMessage( awayMessage );
    if ( useAutoreply )
    {
      // Tell the account to respond with an autoreply message
      CurrentAccount::instance()->setAutoreplyMessage( awayMessage );
      CurrentAccount::instance()->setAutoreply( true );
    }
    delete awayMessageDialog;
  }
  else if ( statusName == i18n("Be Right Back") )
  { // Change status to BE RIGHT BACK
    newStatus = "BRB";
  }
  else if ( statusName == i18n("Busy") )
  { // Change status to BUSY
    newStatus = "BSY";
  }
  else if ( statusName == i18n("Invisible") )
  { // Change status to INVISIBLE
    newStatus = "HDN";
  }
  else if ( statusName == i18n("Out to Lunch") )
  { // Change status to OUT TO LUNCH
    newStatus = "LUN";
  }
  else if ( statusName == i18n("Online") )
  { // Change status to ONLINE
    newStatus = "NLN";
  }
  else if ( statusName == i18n("On the Phone") )
  { // Change status to ON THE PHONE
    newStatus = "PHN";
  }
  if ( newStatus != "" )
  {
    msnNotificationConnection_->changeStatus( newStatus );
  }
}



// The status was changed
void KMess::changedStatus()
{
  int item = -1;
  QString newStatus;

  if ( currentAccount_ != 0 )
  {
    newStatus = currentAccount_->getStatus();
  }
#ifdef KMESSDEBUG_KMESS
  kdDebug() << "KMess - Changed status to " << newStatus << ".  Autoreply is " <<  currentAccount_->getAutoreply() << endl;
#endif

  // Make sure the drop down list matches the user's status
  if ( newStatus == "AWY" )
  {
    if ( !currentAccount_->getAutoreply() )
    {
      // Change status to AWAY
      item = 1;
    }
    else
    {
      // Change status to AWAY with autoreply
      item = 2;
    }
  }
  else if ( newStatus == "BRB" )
  { // Change status to BE RIGHT BACK
    item = 3;
  }
  else if ( newStatus == "BSY" )
  { // Change status to BUSY
    item = 4;
  }
  else if ( newStatus == "HDN" )
  { // Change status to INVISIBLE
    item = 7;
  }
  else if ( newStatus == "LUN" )
  { // Change status to OUT TO LUNCH
    item = 5;
  }
  else if ( newStatus == "NLN" )
  { // Change status to ONLINE
    item = 0;
  }
  else if ( newStatus == "PHN" )
  { // Change status to ON THE PHONE
    item = 6;
  }
#ifdef KMESSDEBUG_KMESS
  kdDebug() << "KMess - Changed the select status to item " << item << endl;
#endif
  if ( item >= 0 )
  {
    status_->setCurrentItem( item );
  }
}



// A view mode has been selected from the menu.
void KMess::changeViewMode(int mode)
{
#ifdef KMESSTEST
  ASSERT( ( mode == 0 ) || ( mode == 1 ) );
#endif
  if ( mode == 0 )
  {
    CurrentAccount::instance()->setShowContactsByGroup( true );
  }
  else if ( mode == 1 )
  {
    CurrentAccount::instance()->setShowContactsByGroup( false );
  }
  else
  {
    kdDebug() << "KMess - changeViewMode() - WARNING - Invalid view mode = " << mode << endl;
  }
}



// Show a "Contact added you" dialog
void KMess::showContactAddedUserDialog(const Contact *contact)
{
  // Note that this function isn't well tested because the case where
  //  somebody adds you to their contact list doesn't occur very often,
  //  not when you've been using messenger for a while.
  QString                 handle;
  ContactAddedUserDialog *dialog;
  int                     returnValue;

  handle = contact->getHandle();
  dialog = new ContactAddedUserDialog();

#ifdef KMESSDEBUG_KMESS
  kdDebug() << "Contact added user" << endl;
#endif

  // Show a dialog and let the user choose whether to add, allow,
  //  or block the contact.
  returnValue = dialog->launch(handle, contact->getFriendlyName());
  delete dialog;

  if ( returnValue == ContactAddedUserDialog::ADD )
  {
    // Add the contact to the allowed and friends lists
    msnNotificationConnection_->addNewContact(handle);
  }
  else if ( returnValue == ContactAddedUserDialog::ALLOW )
  {
    // Add the contact to the allowed list
    msnNotificationConnection_->allowContact(handle);
  }
  else if ( returnValue == ContactAddedUserDialog::BLOCK )
  {
    // Add the contact to the blocked list.
    msnNotificationConnection_->blockContact(handle);
  }
}


#ifdef KMESS_NETWORK_WINDOW
// Opens the network window
void KMess::showNetworkWindow()
{
  networkWindow_->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>" )
                    .arg(handle);

  int result = KMessageBox::warningContinueCancel( this, message, i18n( "Remove contact" ),
                                                   KGuiItem(i18n("Remove"), "editdelete"), 0
#if KDE_IS_VERSION(3,2,0)
                                                   , KMessageBox::Dangerous
#endif
                                                  );

  if(result == KMessageBox::Continue)
  {
    // TODO: include a checkbox with the option "Also block this contact".
    msnNotificationConnection_->removeContact(handle, false);
  }
}


// Show a dialog before removing the group
void KMess::showRemoveGroupDialog(QString groupId)
{
  Group *group = msnNotificationConnection_->getContactList()->getGroupById(groupId);
 
  // If the group was found
  if(group == 0)
  {
    kdDebug() << "KMess::showRemoveGroupDialog() - Couldn't find a matching group." << endl;
    return;
  }

  QString message = i18n( "<qt>Are you sure you want to remove the group <b>%1</b> from your contact list?</qt>" )
                    .arg(group->getName());

  int result = KMessageBox::warningContinueCancel( this, message, i18n( "Remove group" ),
                                                   KGuiItem(i18n("Remove"), "editdelete"), 0
#if KDE_IS_VERSION(3,2,0)
                                                   , KMessageBox::Dangerous
#endif
                                                  );

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


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

  // If the group was found
  if ( group == 0 )
  {
    kdDebug() << "KMess::showRenameGroupDialog() - Couldn't find a matching group." << endl;
    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 and can't be changed.") );
    return;
  }
  else
  {
    // Otherwise, get the new group name..
    newGroupName = group->getName();

    // Launch a dialog to get a new group name
#if KDE_IS_VERSION(3,2,0)
  newGroupName = KInputDialog::getText(i18n("Rename group"),
                                       i18n("Enter the new name for this group:"),
                                       newGroupName, &okPressed, this);
#else
  newGroupName = KLineEditDlg::getText(i18n("Rename group"),
                                       i18n("Enter the new name for this group:"),
                                       newGroupName, &okPressed, this);
#endif

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


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


// Autologin with the first account that has autologin enabled
void KMess::checkAutologin(QString handle)
{
#ifdef KMESSDEBUG_KMESS
  kdDebug() << "KMess - Check autologin - given handle is '" << handle << "'  Length is " << handle.length() << ".  Handle.isEmpty() = " << handle.isEmpty() << "." << endl;
#endif
  Account *loginAccount = 0;
 // If no handle was given...
  if ( handle.isEmpty() )
  {
#ifdef KMESSDEBUG_KMESS
    kdDebug() << "KMess - No handle was given.  Find an account that uses autologin." << endl;
#endif
    for ( Account *account = accounts_.first(); account; account = accounts_.next() )
    {
#ifdef KMESSDEBUG_KMESS
      kdDebug() << "KMess - Account " << account->getHandle() << " useAutologin = " << account->getUseAutologin() << "." << endl;
#endif
      if ( account->getUseAutologin() )
      {
#ifdef KMESSDEBUG_KMESS
        kdDebug() << "KMess - Autologin." << endl;
#endif
        loginAccount = account;
        break;
      }
    }
  }
  else
  {
#ifdef KMESSDEBUG_KMESS
    kdDebug() << "KMess - A handle was given.  Find the account." << endl;
#endif
    // Find the account with the given handle
    for ( Account *account = accounts_.first(); account; account = accounts_.next() )
    {
#ifdef KMESSDEBUG_KMESS
      kdDebug() << "KMess - Does " << account->getHandle() << " = " << handle << "?" << endl;
#endif
      if ( account->getHandle() == handle )
      {
#ifdef KMESSDEBUG_KMESS
        kdDebug() << "KMess - It does.  Connect." << endl;
#endif
        loginAccount = account;
        break;
      }
    }
  }
  if ( loginAccount != 0 )
  {
    // Use the autologin view as the main view
    //initialView_->hide();
    //autologinView_->show();
    setCentralWidget( autologinView_ );
    connectWithAccount( loginAccount );
  }
}



// A connection has been made with the notification server.
void KMess::connected()
{
#ifdef KMESSTEST
  ASSERT( view_ != 0 );
  ASSERT( initialView_ != 0 );
  ASSERT( autologinView_ != 0 );
#endif
  // Disable the disconnected button
  // 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() );
  if ( currentAccount_->getShowContactsByGroup() )
  {
    viewMode_->setCurrentItem( 0 );
  }
  else
  {
    viewMode_->setCurrentItem( 1 );
  }
  // Show the connected message
  statusMessage( i18n("Connected"), 1 );
  // Give the view widget some info
  view_->connected();
  // Swap the initial and main views
  initialView_->hide();
  autologinView_->hide();
  view_->show();
  // Set it as the application's main widget
  setCentralWidget( view_ );
  // Set the caption
  setCaptionToUser();
}



// Connect to the server with the given account
void KMess::connectWithAccount(Account *account)
{
  bool            connected;
  QString         errorMessage;

  if ( account == 0 )
  {
    kdDebug() << "KMess - connectWithAccount() - Got null account" << endl;
    return;
  }

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

  initialView_->hide();
  autologinView_->show();
  setCentralWidget( autologinView_ );

  // Copy the account to the current account
  currentAccount_->copyAccount( account );
  // Set disconnect true so that the user can cancel an attempted sign in.
  disconnect_->setEnabled( true  );
  // Connect to the server.
  connected = msnNotificationConnection_->openConnection();
  if ( !connected )
  {
    // Notify the user.
    errorMessage = i18n("The connection to the server failed.") + "\r\n";
    errorMessage += i18n("You may not be connected to the internet.");
    KMessageBox::error( 0, errorMessage );
  }
}



// Connect to the server with the given, possibly temporary, new account
void KMess::connectWithNewAccount(Account *account, bool doSave)
{
  if ( account != 0 )
  {
    // Add the account to the menus
    addAccountToMenus( account );
    // If the account is to be saved, at it to the list of accounts
    if ( doSave )
    {
      accounts_.append( account );
    }
    else
    {
      // If not, add it to the list of unsaved accounts
      unsavedAccounts_.append( account );
    }
    // Connect with the account
    connectWithAccount( account );
  }
}



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

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

  if ( appsDir.exists() )
  {
    kmessDir.setPath( appsDir.absPath() + "/kmess" );
#ifdef KMESSDEBUG_KMESS
    kdDebug() << "KMess - kmess dir should be at " << kmessDir.absPath() << "." << endl;
#endif
    if ( !kmessDir.exists() )
    {
#ifdef KMESSDEBUG_KMESS
      kdDebug() << "KMess - Creating kmess dir." << endl;
#endif
      appsDir.mkdir( kmessDir.absPath(), true );
    }
    // Create the pics and emoticons directories
    emoticonsDir.setPath( kmessDir.absPath() + "/emoticons" );
#ifdef KMESSDEBUG_KMESS
    kdDebug() << "KMess - emoticons dir should be at " << emoticonsDir.absPath() << "." << endl;
#endif
    if ( !emoticonsDir.exists() )
    {
#ifdef KMESSDEBUG_KMESS
      kdDebug() << "KMess - Creating emoticons dir." << endl;
#endif
      kmessDir.mkdir( emoticonsDir.absPath(), true );
    }
    picsDir.setPath( kmessDir.absPath() + "/pics" );
#ifdef KMESSDEBUG_KMESS
    kdDebug() << "KMess - pics dir should be at " << picsDir.absPath() << "." << endl;
#endif
    if ( !picsDir.exists() )
    {
#ifdef KMESSDEBUG_KMESS
      kdDebug() << "KMess - Creating pics dir." << endl;
#endif
      kmessDir.mkdir( picsDir.absPath(), true );
    }
  }
#ifdef KMESSDEBUG_KMESS
  else
  {
    kdDebug() << "KMess - " << appsDir.absPath() << " doesn't exist!" << endl;
  }
#endif
#ifdef KMESSTEST
  ASSERT(     kmessDir.exists() );
  ASSERT( emoticonsDir.exists() );
  ASSERT(      picsDir.exists() );
#endif
  return true;
}



// "Add new account" has been selected from the menu.
void KMess::createNewAccount()
{
  Account *account;
  // If there are no profiles, create a "default" account
  // Create an account to store the profile information
  account = new Account();

  // Show the settings dialog for the new account
  showSettingsForAccount( account );

  // Make sure the account handle was changed off the default
  if ( account->getHandle() != i18n("you@hotmail.com") )
  {
    // Add the account to the account list.
    accounts_.append(account);

    // Add the account to the necessary menus
    addAccountToMenus(account);
  }
  else
  {
    // The account wasn't changed, so delete it.
    delete account;
  }
}



// The current account changed its name, so set the corresponding account's name
void KMess::currentAccountChangedName()
{
  // Find the account that the current account represents
  for ( Account* account = accounts_.first(); account; account = accounts_.next() )
  {
    // If the account is the current account...
    if ( account->getHandle() == currentAccount_->getHandle() )
    {
      // Set the account's name to the current account's name
      account->setFriendlyName( currentAccount_->getFriendlyName() );
    }
  }
  // Set the caption to the account's new name
  setCaptionToUser();
}



// Delete the given account
void KMess::deleteAccount(Account *account)
{
#ifdef KMESSTEST
  ASSERT( account != 0 );
#endif
  QString groupName;
  bool    groupDeleted;

  if ( account == 0 )
  {
    kdDebug() << "KMess - WARNING - Trying to delete null account." << endl;
    return;
  }
#ifdef KMESSDEBUG_KMESS
  kdDebug() << "KMess - Deleting account for " << account->getHandle() << "." << endl;
#endif
#ifdef KMESSTEST
  uint oldCount = accounts_.count();
#endif
  // Remove the account from the list of accounts
  accounts_.remove( account );
#ifdef KMESSTEST
  ASSERT( accounts_.count() == ( oldCount - 1 ) );
#endif

  // Remove the account's information from the config file
  groupName = "Profile_" + account->getHandle();
#ifdef KMESSDEBUG_KMESS
  kdDebug() << "KMess - Deleting group " << groupName << " from the config file." << endl;
#endif

  groupDeleted = kapp->config()->deleteGroup( groupName );
  if ( !groupDeleted )
  {
    kdDebug() << "KMess - WARNING - " << account->getHandle() << "'s group wasn't deleted from the config file." << endl;
  }
  // Notify the user.
  KMessageBox::information( this, i18n("Changes will take place when the program is restarted."));
}



// Disconnect was selected from the menu.
void KMess::disconnectClicked()
{
#ifdef KMESSTEST
  ASSERT( msnNotificationConnection_ != 0 );
#endif
#ifdef KMESSDEBUG_KMESSINTERFACE
  kdDebug() << "KMess: Disconnecting." << endl;
#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
  ASSERT( view_ != 0 );
  ASSERT( chatMaster_ != 0 );
  ASSERT( initialView_ != 0 );
  ASSERT( autologinView_ != 0 );
#endif
#ifdef KMESSDEBUG_KMESS
  kdDebug() << "KMess - Disconnected." << endl;
#endif
  // Disable the disconnected button
  disconnect_->setEnabled( false );
  // Disable/enable the menus
  enableMenus(false);
  // Show the disconnected message
  statusMessage( i18n("Disconnected"), 3 );
  view_->disconnected();
  chatMaster_->disconnected();
  currentAccount_->setStatus( "FLN" );
#ifdef KMESSDEBUG_KMESS
  kdDebug() << "KMess - Swap views." << endl;
#endif
  // If the autologin screen isn't being shown..
  //if ( !autologinView_->isVisible() )
  //{
    // Swap the initial and main views
    initialView_->show();
    view_->hide();
    autologinView_->hide();
    // Set it as the application's main widget
    setCentralWidget( initialView_ );
  //}
#ifdef KMESSDEBUG_KMESS
  kdDebug() << "KMess - Blank the caption." << endl;
#endif
  // Set the caption
  setCaption( "" );
}



// Initialize the class
bool KMess::initialize()
{
  if ( initialized_ )
  {
    kdDebug() << "KMess - Already initialized." << endl;
    return false;
  }
  if ( !KMessInterface::initialize() )
  {
    kdDebug() << "KMess - Couldn't initialize ancestor." << endl;
    return false;
  }
  if ( !initMsnNotificationConnection() )
  {
    kdDebug() << "KMess - Couldn't initialize the MsnNotificationConnection class." << endl;
    return false;
  }
  if ( !initIdleTimer() )
  {
    kdDebug() << "KMess - Couldn't initialize the IdleTimer class." << endl;
    return false;
  }
  if ( !initAutologinView() )
  {
    kdDebug() << "KMess - Couldn't initialize the AutologinView widget." << endl;
    return false;
  }
  if ( !initInitialView() )
  {
    kdDebug() << "KMess - Couldn't initialize the InitialView widget." << endl;
    return false;
  }
  if ( !initKMessView() )
  {
    kdDebug() << "KMess - Couldn't initialize the KMessView widget." << endl;
    return false;
  }
  if ( !initSystemTrayWidget() )
  {
    kdDebug() << "KMess - Couldn't initialize the system tray widget." << endl;
    return false;
  }
  if ( !initChatMaster() )
  {
    kdDebug() << "KMess - Couldn't initialize the Chat master." << endl;
    return false;
  }
  if ( !initNotifications() )
  {
    kdDebug() << "KMess - Couldn't initialize the notifications." << endl;
    return false;
  }
  if ( !createDirectories() )
  {
    kdDebug() << "KMess - Couldn't create local KDE directories." << endl;
    return false;
  }


  // Start reading the properties
  readProperties( kapp->config() );


#ifdef KMESS_NETWORK_WINDOW
  // Create our network window (parented to us, for QT autodelete goodness)
  // Then bind it to signals. We like signals.
  networkWindow_= new NetworkWindow(this, "network window");
  connect(msnNotificationConnection_, SIGNAL(messageSent(const QString &)),
          networkWindow_, SLOT(addOutgoingServerMessage(const QString &)));
  connect(msnNotificationConnection_, SIGNAL(messageReceived(const QString &)),
          networkWindow_, SLOT(addIncomingServerMessage(const QString &)));
#endif

  currentAccount_ = CurrentAccount::instance();
  if ( currentAccount_ == 0 )
  {
    kdDebug() << "KMess - Couldn't get a pointer to the instance of the current account." << endl;
    return false;
  }
  connect( currentAccount_, SIGNAL(       changedFriendlyName() ),
           this,            SLOT  ( currentAccountChangedName() ) );
  connect( currentAccount_, SIGNAL(             changedStatus() ),
           this,            SLOT  (             changedStatus() ) );
  connect( currentAccount_, SIGNAL(          changedMsnObject() ),
           msnNotificationConnection_, SLOT( changedMsnObject() ) );

  // Start disconnected
  disconnected();

  // connect a shutDown() signal
  connect( KApplication::kApplication(),  SIGNAL(    shutDown() ),
           this,                          SLOT  (    shutDown() ) );

  initialized_ = true;
  return true;
}



// Initialize the autologin view
bool KMess::initAutologinView()
{
  autologinView_ = new AutologinView( this, "autologinView" );
  if ( autologinView_ == 0 )
  {
    kdDebug() << "KMess - Couldn't create the autologin view" << endl;
    return false;
  }
  autologinView_->hide();
  return true;
}



// Initialize the chat master
bool KMess::initChatMaster()
{
#ifdef KMESSTEST
  ASSERT( chatMaster_ == 0 );
#endif
  if ( msnNotificationConnection_ == 0 )
  {
    kdDebug() << "KMess - The notification connection must be initialized before the chat master." << endl;
    return false;
  }
  chatMaster_ = new ChatMaster( this );
  if ( chatMaster_ == 0 )
  {
    kdDebug() << "KMess - Couldn't create the chat master." << endl;
    return false;
  }
  if ( !chatMaster_->initialize() )
  {
    kdDebug() << "KMess - Couldn't initialize the chat master." << endl;
    return false;
  }
  // Make the chat master's connections
  connect( msnNotificationConnection_, SIGNAL( startChat( ChatInformation* ) ),
           chatMaster_,                SLOT  ( startChat( ChatInformation* ) ) );

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



// Initialize the idle timer
bool KMess::initIdleTimer()
{
#ifdef KMESSTEST
  ASSERT( idleTimer_ == 0 );
#endif
  idleTimer_ = new IdleTimer();
  if ( idleTimer_ == 0 )
  {
    kdDebug() << "KMess - Couldn't create the idle timer." << endl;
    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
  ASSERT( idleTimer_ != 0 );
#endif
  return true;
}



// Initialize the initial view
bool KMess::initInitialView()
{
  initialView_ = new InitialView( this, "initialview" );
  initialView_->show();

  connect( initialView_, SIGNAL(        connectWithAccount(Account*)       ),
           this,         SLOT  (        connectWithAccount(Account*)       ) );
  connect( initialView_, SIGNAL( connectWithAndSaveAccount(Account*, bool) ),
           this,         SLOT  (     connectWithNewAccount(Account*, bool) ) );

  return true;
}



// Initialize the main view
bool KMess::initKMessView()
{
  bool initialized;

  if ( msnNotificationConnection_ == 0 )
  {
    kdDebug() << "KMess - MsnNotificationConnection must be initialized before KMessView" << endl;
    return false;
  }

  view_        = new KMessView(this, "kmessview");

  initialized  = view_->initialize( msnNotificationConnection_->getContactList() );
  if ( !initialized )
  {
    kdDebug() << "KMess - Couldn't initialize KMessView." << endl;
    return false;
  }


  // Connect other signals to msnNotificationConnection
  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(   moveContact(QString, QString, QString) ),   // Move the contact
           msnNotificationConnection_, SLOT  (   moveContact(QString, QString, QString) ) );
  connect( view_,                      SIGNAL(     startChat(QString)                   ),   // Start a chat
           msnNotificationConnection_, SLOT  (   requestChat(QString)                   ) );
  connect( view_,                      SIGNAL( removeContact(QString)                   ),   // Remove the contact
           this,                       SLOT  (showRemoveContactDialog(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(unblockContact(QString)                   ),   // Unblock the contact
           msnNotificationConnection_, SLOT  (unblockContact(QString)                   ) );

  view_->hide();
  return true;
}



// Initialize the MSN notification connection
bool KMess::initMsnNotificationConnection()
{
  bool initialized;
  
  msnNotificationConnection_ = new MsnNotificationConnection();

  initialized = msnNotificationConnection_->initialize();
  if ( !initialized )
  {
    kdDebug() << "KMess - Couldn't initialize MsnNotificationConnection." << endl;
    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 Contact*)          ),   // Contact added user
           this,                       SLOT  (showContactAddedUserDialog(const Contact*)) );
  connect( msnNotificationConnection_, SIGNAL( statusMessage(QString, int)              ),   // Display a status message
           this,                       SLOT  ( statusMessage(QString, int)              ) );

  return true;
}



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

  ASSERT( contactOfflineNotification_ == 0 );
  ASSERT( contactOnlineNotification_  == 0 );
  ASSERT( emailNotification_          == 0 );
#endif

  const ContactList *contactList;
  

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


  contactList = msnNotificationConnection_->getContactList();

  balloonWidget_ = new BalloonWidget();
  balloonWidget_->setAnchorWidget( systemTrayWidget_ );


  // Connect the "Chat notification" signals
  chatNotification_           = new ChatNotification(balloonWidget_);
  connect( chatMaster_,                 SIGNAL(        newChat(const Contact*, QString, ChatWindow*) ),
           chatNotification_,           SLOT  (        newChat(const Contact*, QString, ChatWindow*) ) );

  // Connect the "offline notification" signals
  contactOfflineNotification_ = new ContactOfflineNotification(balloonWidget_);
  connect( contactList,                 SIGNAL( contactOffline(Contact*,bool)  ),
           contactOfflineNotification_, SLOT  ( contactOffline(Contact*,bool)  ) );

  // Connect the "online notification" signals
  contactOnlineNotification_  = new ContactOnlineNotification(balloonWidget_);
  connect( contactList,                 SIGNAL(  contactOnline(Contact*,bool)  ),
           contactOnlineNotification_,  SLOT  (  contactOnline(Contact*,bool)  ) );
  connect( contactOnlineNotification_,  SIGNAL(      startChat(QString)        ),
           msnNotificationConnection_,  SLOT  (    requestChat(QString)        ) );

  // Connect the "email notification" signals
  emailNotification_          = new EmailNotification(balloonWidget_);
  connect( msnNotificationConnection_,  SIGNAL(       newEmail(QString, QString, bool, QString, QString, QString) ),
           emailNotification_,          SLOT  (       newEmail(QString, QString, bool, QString, QString, QString) ) );

#ifdef KMESSTEST
  ASSERT( contactOfflineNotification_ != 0 );
  ASSERT( contactOnlineNotification_  != 0 );
  ASSERT( emailNotification_          != 0 );
#endif

  return true;
}



// Initialize the system tray widget
bool KMess::initSystemTrayWidget()
{
  bool initialized;

  // Create the widget
  systemTrayWidget_ = new SystemTrayWidget( this, "systemtraywidget" ) ;
  if ( systemTrayWidget_ == 0 )
  {
    kdDebug() << "KMess - Couldn't create system tray widget." << endl;
    return false;
  }

  // Initialize the widget
  initialized = systemTrayWidget_->initialize();
  if ( !initialized )
  {
    kdDebug() << "KMess - Couldn't initialize system tray widget." << endl;
    return false;
  }

  // Plug some of the menu actions into the system tray widget's menu.
  connectActionMenu_-> plug( systemTrayWidget_->menu() );
  disconnect_->        plug( systemTrayWidget_->menu() );
  systemTrayWidget_->  menu()->insertSeparator();
  status_->            plug( systemTrayWidget_->menu() );
  systemTrayWidget_->  menu()->insertSeparator();
  settingsActionMenu_->plug( systemTrayWidget_->menu() );

  // Make the connections for the system tray widget
  connect( systemTrayWidget_, SIGNAL( quitSelected() ),
           this,              SLOT  (     menuQuit() ) );

  systemTrayWidget_->show();
#ifdef KMESSTEST
  ASSERT( systemTrayWidget_ != 0 );
  ASSERT( systemTrayWidget_->isVisible() );
#endif
  return true;
}



// Read in account and other properties
void KMess::readProperties(KConfig *config)
{
  KMessInterface::readProperties(config);

#ifdef KMESSDEBUG_KMESS
  kdDebug() << "KMess - reading properties" << endl;
#endif

  QStringList  accountList;
  Account     *account;

  // Get a list of the profiles stored in the configuration file
  config->setGroup("Profiles");
  accountList = config->readListEntry( "Profiles" );

  // For each listed account, create an account and have it read in its settings
  for ( unsigned int i = 0; i < accountList.count(); i++ )
  {
    account = new Account();
    account->readProperties( config, accountList[i] );
    // Add the account to the list of stored accounts
    accounts_.append( account );
    // Add the account to the settings and other menus
    addAccountToMenus( account );
  }
}



// Save account and other properties
void KMess::saveProperties(KConfig *config)
{
#ifdef KMESSDEBUG_KMESS
  if(config != kapp->config())
  {
    kdDebug() << "For some reason saveProperties(config) != kapp->config(), using kapp->config()." << endl;
  }
#endif

  config = kapp->config();   // Hack, but it seams to work
  KMessInterface::saveProperties(config);

#ifdef KMESSDEBUG_KMESS
  kdDebug() << "KMess - saving properties" << endl;
#endif

  QStringList  accountList;
  Account *account;

  // Have all the accounts save their properties
  for ( account = accounts_.first(); account; account = accounts_.next() )
  {
    // If the account is the current account...
    if ( account->getHandle() == currentAccount_->getHandle() )
    {
      // Move some settings from the current account to the account that are set by the user interface
      account->copyAccountUISettings( currentAccount_ );
    }
    account->saveProperties( config );
    accountList.append( account->getHandle() );
  }

  // Save the list of profiles
  config->setGroup("Profiles");
  config->writeEntry( "Profiles", accountList );

  // Save the the contact list properties
  // (getContactList returns a "const ContactList", which is a good thing!)
  msnNotificationConnection_->saveProperties( config );

  // Write data now!
  config->sync();
}



// Set the caption
void KMess::setCaptionToUser()
{
  if ( currentAccount_ != 0 )
  {
    // Set the caption
    setCaption( currentAccount_->getFriendlyName() );
  }
}



// Show the settings dialog for a given account
void KMess::showSettingsForAccount(Account *account)
{
#ifdef KMESSTEST
  ASSERT( msnNotificationConnection_ != 0 );
#endif
  QString oldFriendlyName;
  bool     isConnectedCurrentAccount = false;

  if ( msnNotificationConnection_ != 0 )
  {
    if ( ( account->getHandle() == currentAccount_->getHandle() ) && ( msnNotificationConnection_->isConnected() ) )
    {
      isConnectedCurrentAccount = true;
    }
  }
  // Save the account's friendly name
  oldFriendlyName = account->getFriendlyName();

  // Show the settings dialog
  SettingsDialog *settingsDialog = new SettingsDialog(this, "settings");
  connect( settingsDialog, SIGNAL( deleteAccount(Account*) ),
           this,           SLOT  ( deleteAccount(Account*) ) );
  // If it's the current account, copy some UI settings from the current account to the account
  account->copyAccountUISettings( currentAccount_ );

  settingsDialog->changeAccountSettings( account, isConnectedCurrentAccount );
  // If the account matches the current account..
  if ( account->getHandle() == currentAccount_->getHandle() )
  {
    // If the friendly name was changed, ask the server to change it
    if ( account->getFriendlyName() != oldFriendlyName )
    {
      msnNotificationConnection_->changeFriendlyName( account->getHandle(), account->getFriendlyName() );
    }
    // Copy the account settings to the current account
    currentAccount_->copyAccount( account );
  }
  // If the account is set to use autologin, make sure that all other accounts are not
  if ( account->getUseAutologin() )
  {
    QPtrListIterator<Account>  it(accounts_);
    while ( it.current() != 0 )
    {
      // For each account that is not the account just changed...
      if ( it.current() != account )
      {
        it.current()->setUseAutologin( false );
      }
      ++it;
    }
  }
}



// Show the user's MSN profile.
void KMess::showUserProfile()
{
  QString       urlPath;
  KURL          *url;
  KRun          *run;

  if ( currentAccount_ != 0 )
  {
    // Set a path to the msn profile page.  Unfortunately, this isn't
    //  internationalized.
    urlPath = "http://members.msn.com/default.msnw?mem=" + currentAccount_->getHandle();
    // Create a URL to the given path
    url = new KURL( urlPath );
    // Launch the default html program for the given URL
    run = new KRun( *url );
  }
}



// We're about to shutdown, apparently
//  make sure we die, rather than just close themain window
void KMess::shutDown()
{
  KApplication::kApplication()->quit();
}



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



// The user has gone idle
void KMess::userIsIdle()
{
#ifdef KMESSDEBUG_KMESS
    kdDebug() << "KMess::userIsIdle()" << endl;
#endif
  if ( ( currentAccount_ != 0 ) && ( msnNotificationConnection_ != 0 ) )
  {
#ifdef KMESSDEBUG_KMESS
    kdDebug() << "KM:   Current status is " << currentAccount_->getStatus() << "." << endl;
#endif
    // Only change the state if the user is currently set as online
    if ( currentAccount_->getStatus() == "NLN" )
    {
#ifdef KMESSDEBUG_KMESS
      kdDebug() << "KM:   Change status to IDL." << endl;
#endif
      // Request a status change to "IDL"
      msnNotificationConnection_->changeStatus("IDL");
    }
  }
#ifdef KMESSDEBUG_KMESS
    kdDebug() << "KM:   Done userIsIdle()." << endl;
#endif
}



// The user is no longer idle
void KMess::userIsNotIdle()
{
#ifdef KMESSDEBUG_KMESS
    kdDebug() << "KMess::userIsNotIdle()" << endl;
#endif
  if ( ( currentAccount_ != 0 ) && ( msnNotificationConnection_ != 0 ) )
  {
#ifdef KMESSDEBUG_KMESS
    kdDebug() << "KM:   User is no longer idle, current status is " << currentAccount_->getStatus() << endl;
#endif
    // Only change the state if the user is currently set as idle
    if ( currentAccount_->getStatus() == "IDL" )
    {
#ifdef KMESSDEBUG_KMESS
      kdDebug() << "KM:   Change status to NLN" << endl;
#endif
      // Request a status change to "NLN"
      msnNotificationConnection_->changeStatus("NLN");
    }
  }
#ifdef KMESSDEBUG_KMESS
    kdDebug() << "KM:   Done userIsNotIdle()." << endl;
#endif
}

#include "kmess.moc"
