/***************************************************************************
                          contactlist.cpp  -  description
                             -------------------
    begin                : Sun Jan 5 2003
    copyright            : (C) 2003 by Mike K. Bennett
                           (C) 2005 by Diederik van der Boor
    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 "contactlist.h"

#include <kconfig.h>
#include <kdebug.h>
#include <klocale.h>

#include "contact.h"
#include "contactextension.h"
#include "group.h"
#include "../kmessdebug.h"
#include "../specialgroups.h"
#include "../msnobject.h"

#ifdef KMESSDEBUG_CONTACTLIST
  #define KMESSDEBUG_CONTACTLIST_GENERAL
  #define KMESSDEBUG_CONTACTLIST_TOOLTIP
  #define KMESSDEBUG_CONTACTLIST_DISCONNECTION
#endif



// The constructor
ContactList::ContactList()
{
#ifdef KMESSTEST
  ASSERT( contacts_.count() == 0 );
  ASSERT( groups_.count() == 0 );
#endif
}



// The destructor
ContactList::~ContactList()
{
  Contact *contact;
  Group   *group;

  // Delete all contacts
  for ( contact = contacts_.first(); contact; contact = contacts_.next() )
  {
    delete contact;
  }
  contacts_.clear();

  // Delete all groups
  for ( group = groups_.first(); group; group = groups_.next() )
  {
    delete group;
  }
  groups_.clear();

#ifdef KMESSTEST
  ASSERT( contacts_.count() == 0 );
  ASSERT( groups_.count() == 0 );
#endif

#ifdef KMESSDEBUG_CONTACTLIST_GENERAL
  kdDebug() << "DESTROYED ContactList " << endl;
#endif
}



// Add a contact to the contact list
Contact * ContactList::addContact( QString handle, QString friendlyName, int lists, QString groupIds )
{
#ifdef KMESSDEBUG_CONTACTLIST_GENERAL
  kdDebug() << "ContactList::addContact " << handle << endl;
#endif
#ifdef KMESSTEST
  ASSERT( ! handle.isEmpty() );
#endif

  if(getContactByHandle(handle) != 0)
  {
    kdDebug () << "ContactList::addContact - WARNING - attempted to add contact " << handle << " twice." << endl;
    return 0;
  }

  Contact *contact;

  if(handle.isEmpty())
  {
    return 0;
  }

  // Create the contact
  contact = new Contact( handle, friendlyName, lists, groupIds );
  contacts_.append( contact );


  // Attach the signals
  connect( contact, SIGNAL(            contactOffline(Contact*,bool) ),   // Contact went offline
           this,    SLOT  ( slotForwardContactOffline(Contact*,bool) ) );
  connect( contact, SIGNAL(             contactOnline(Contact*,bool) ),   // Contact went online
           this,    SLOT  (  slotForwardContactOnline(Contact*,bool) ) );
  connect( contact, SIGNAL(              changedGroup(Contact*)      ),   // Contact moved
           this,    SLOT  (   slotForwardContactMoved(Contact*)      ) );
  connect( contact, SIGNAL(               changedList(Contact*)      ),   // Contact was added/removed from certain lists
           this,    SLOT  (   slotForwardContactMoved(Contact*)      ) );

  // HACK: changedList() emits an forwardContactMoved, assuming the GUI
  //       uses "groups to sort various lists". It would be nicer if the GUI
  //       desided whether a contact-item should be moved if it's lists changed.

  // Signal the UI
  emit contactAdded(contact);

  return contact;
}



// Add a group to the contact list
Group * ContactList::addGroup( QString groupId, QString groupName )
{
#ifdef KMESSDEBUG_CONTACTLIST_GENERAL
  kdDebug() << "ContactList::addGroup " << groupId << " " << groupName << endl;
#endif

  Group *group;

  // Check if the group already exists
  if ( groupExists( groupId ) )
  {
    kdDebug() << "ContactList - addGroup - Group " << groupId << " already exists." << endl;
    return 0;
  }

  // Create the group
  group = new Group( groupId, groupName  );
  groups_.append( group );

  // Signal the UI
  emit groupAdded(group);

  return group;
}



// Change a contact's status (and update the friendly name)
void ContactList::changeContactStatus( QString handle, QString friendlyName, QString status,
                                       uint capabilities, QString msnObject, bool showBaloon )
{
  // Get the contact
  Contact *contact;
  bool     wasOnline;

  contact = getContactByHandle( handle );
  if ( contact == 0 )
  {
    kdDebug() << "ContactList - changeContactStatus - Couldn't find contact " << handle << " in the contact list." << endl;
    return;
  }


  // Store the old online state to detect a change
  wasOnline = contact->isOnline();

  // Update the settings
  contact->setFriendlyName( friendlyName );
  contact->setStatus( status, showBaloon );
  contact->setCapabilities( capabilities );
  contact->loadMsnObject( msnObject );

  // Emit a signal for the UI
  emit contactChangeStatus(contact);
}



// Return whether or not the contact with the given handle exists
bool ContactList::contactExists( QString handle )
{
  return getContactByHandle( handle ) != 0;
}



// Delete all the contacts
void ContactList::deleteAllContacts()
{
  Contact       *contact;

#ifdef KMESSDEBUG_CONTACTLIST_DISCONNECTION
  kdDebug() << "ContactList - Deleting all contacts." << endl;
#endif

  // Delete all contacts
  for ( contact = contacts_.first(); contact; contact = contacts_.next() )
  {
    delete contact;
  }

  contacts_.clear();
}



// Delete all non-special groups
void ContactList::deleteNonSpecialGroups()
{
  Group     *group;
  QString   id;
  QPtrList<Group> groupsToDelete;

#ifdef KMESSDEBUG_CONTACTLIST_DISCONNECTION
  kdDebug() << "ContactList - Deleting non-special groups." << endl;
#endif

  // Delete all groups
  for ( group = groups_.first(); group; group = groups_.next() )
  {
    id = group->getId();
    if (    ( id != SpecialGroups::ONLINE )
         && ( id != SpecialGroups::OFFLINE )
         && ( id != SpecialGroups::ALLOWED )
         && ( id != SpecialGroups::REMOVED ) )
//         && ( id != SpecialGroups::INDIVIDUALS ) ) // Also not added persistently, so I don't keep it either.
    {
      groupsToDelete.append( group );
    }
  }

  // Remove the groups to delete from the groups
  for ( group = groupsToDelete.first(); group; group = groupsToDelete.next() )
  {
    groups_.remove( group );
    emit groupRemoved( group );
    delete group;
  }

#ifdef KMESSTEST
  ASSERT( groups_.count() == 4 );
#endif
}



// Return the contact with the given handle
Contact* ContactList::getContactByHandle( QString handle ) const
{
  QPtrListIterator<Contact> iterator(contacts_);
  Contact *contact;

  while(iterator.current() != 0)
  {
    contact = iterator.current();
    if(contact->getHandle() == handle)
    {
      return contact;
    }
    ++iterator;
  }

  return 0;
}



// Get the friendly name of the given contact
QString ContactList::getContactFriendlyNameByHandle( const QString& handle ) const
{
#ifdef KMESSDEBUG_CONTACTLIST_GENERAL
  kdDebug() << "Looking for the friendly name of " << handle << endl;
#endif

  QPtrListIterator<Contact> iterator(contacts_);
  Contact *contact;

  while(iterator.current() != 0)
  {
    contact = iterator.current();
    if(contact->getHandle() == handle)
    {
      return contact->getFriendlyName();
    }
    ++iterator;
  }

  return "";
}


// Return the contact last dragged
Contact* ContactList::getContactLastDragged() const
{
  return 0;
}


// Return the list of contacts
const QPtrList<Contact>& ContactList::getContactList() const
{
  return contacts_;
}



// Return the group with the given id
Group* ContactList::getGroupById( QString groupId ) const
{
#ifdef KMESSDEBUG_CONTACTLIST_GENERAL
  kdDebug() << "ContactList::getGroupById(" << groupId << ")" << endl;
#endif
#ifdef KMESSTEST
  bool goodNumber;
  groupId.toInt( &goodNumber );
  ASSERT( goodNumber );
#endif

  QPtrListIterator<Group> iterator(groups_);
  Group *group;

  while(iterator.current() != 0)
  {
    group = iterator.current();
    if ( group->getId() == groupId )
    {
      return group;
    }
    ++iterator;
  }

  return 0;
}



// Return the group with the given sort position
Group* ContactList::getGroupBySortPosition(int sortPosition) const
{
  QPtrListIterator<Group> iterator(groups_);
  Group *group;

  while(iterator.current() != 0)
  {
    group = iterator.current();
    if ( group->getSortPosition() == sortPosition )
    {
      return group;
    }
    ++iterator;
  }

  return 0;
}



// Return the list of groups
const QPtrList<Group>& ContactList::getGroupList() const
{
  return groups_;
}



// Return whether or not a group with the given id exists
bool ContactList::groupExists( QString groupId )
{
  return getGroupById( groupId ) != 0;
}



// Forward from a contact that is has moved
void ContactList::slotForwardContactMoved(Contact *contact)
{
  emit contactMoved(contact);
}



// Forward from a contact that it is offline
void ContactList::slotForwardContactOffline(Contact *contact, bool showBaloon)
{
#ifdef KMESSTEST
  ASSERT( contact != 0 );
#endif

#ifdef KMESSDEBUG_CONTACTLIST_GENERAL
  if ( contact != 0 )
  {
    kdDebug() << "ContactList - Forwarding that " << contact->getFriendlyName() << " went offline." << endl;
  }
#endif

  emit contactOffline( contact, showBaloon );
}



// Forward from a contact that it is online
void ContactList::slotForwardContactOnline(Contact *contact, bool showBaloon)
{
#ifdef KMESSTEST
  ASSERT( contact != 0 );
#endif

#ifdef KMESSDEBUG_CONTACTLIST_GENERAL
  if ( contact != 0 )
  {
    kdDebug() << "ContactList - Forwarding that " << contact->getFriendlyName() << " went online." << endl;
  }
#endif

  emit contactOnline( contact, showBaloon );
}



// Read in account and other properties
void ContactList::readProperties(KConfig */*config*/)
{
}



// Remove a group
void ContactList::removeGroup( QString groupId )
{
  Group *group;

  group = getGroupById( groupId );
  if ( group == 0 )
  {
    kdDebug() << "ContactList - removeGroup - Group " << groupId << " doesn't exist in the contact list." << endl;
    return;
  }

  // Remove the group
  groups_.remove(group);
  emit groupRemoved(group); // Signal the UI

  delete group;
}



// Change a contact's name
void ContactList::renameContact( QString handle, QString newName )
{
  Contact *contact;

  contact = getContactByHandle( handle );
  if ( contact == 0 )
  {
    kdDebug() << "ContactList - renameContact - Couldn't find contact " << handle << " in the contact list." << endl;
    return;
  }

  contact->setFriendlyName( newName );
}



// Change a group's name
void ContactList::renameGroup( QString groupId, QString groupName )
{
  Group *group;

  group = getGroupById( groupId );
  if ( group == 0 )
  {
    kdDebug() << "ContactList - changeGroupName - Group " << groupId << " doesn't exist in the contact list." << endl;
    return;
  }

  group->setName( groupName );
}


// Restore the orginal state of the contact list (empty it)
void ContactList::reset()
{
  deleteAllContacts();
  deleteNonSpecialGroups();
}


// Save group properties
void ContactList::saveProperties(KConfig *config)
{
#ifdef KMESSDEBUG_CONTACTLIST_GENERAL
  kdDebug() << "ContactList - saving properties" << endl;
#endif

  Group   *group;
  Contact *contact;

  // Save all groups
  for ( group = groups_.first(); group; group = groups_.next() )
  {
#ifdef KMESSDEBUG_CONTACTLIST_GENERAL
    kdDebug() << "ContactList - saving group " << group->getName() << endl;
#endif

    group->saveProperties( config );
  }

  // Save contact extensions
  for ( contact = contacts_.first(); contact != 0; contact = contacts_.next() )
  {
     contact->getExtension()->saveProperties( config );
  }
}



// Set a contact to offline
void ContactList::setContactOffline( QString handle )
{
  Contact *contact;

  contact = getContactByHandle( handle );
  if ( contact == 0 )
  {
    kdDebug() << "ContactList - contactOffline - Couldn't find contact " << handle << " in the contact list." << endl;
    return;
  }

  contact->setStatus( "FLN" );
}

#include "contactlist.moc"
