/*
 *
 *  This file is part of libtdepim.
 *
 *  Copyright (c) 2003 Zack Rusin <zack@kde.org>
 *  Copyright (c) 2003 Aaron J. Seigo <aseigo@kde.org>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public License
 *  along with this library; see the file COPYING.LIB.  If not, write to
 *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 *  Boston, MA 02110-1301, USA.
 *
 */

#include "addressesdialog.h"
#include "addresspicker.h"
#include "ldapsearchdialog.h"

#include <config.h>

#include <libemailfunctions/email.h>

#ifdef TDEPIM_NEW_DISTRLISTS
#include "distributionlist.h"
#include <tderesources/selectdialog.h>
#include <tdeabc/resource.h>
#else
#include <tdeabc/distributionlist.h>
#endif

#include <tdeabc/stdaddressbook.h>
#include <tdeapplication.h>
#include <kdebug.h>
#include <tdeglobal.h>
#include <kiconloader.h>
#include <kinputdialog.h>
#include <klineedit.h>
#include <tdelocale.h>
#include <tdemessagebox.h>
#include <kprocess.h>
#include <kpushbutton.h>
#include <krun.h>
#include <kstandarddirs.h>

#include <tqdict.h>
#include <tqlayout.h>
#include <tqvbox.h>
#include <tqwidget.h>

namespace KPIM {

// private start :
struct AddresseeViewItem::AddresseeViewItemPrivate {
  TDEABC::Addressee               address;
  AddresseeViewItem::Category   category;
  TDEABC::Addressee::List         addresses;
};

struct AddressesDialog::AddressesDialogPrivate {
  AddressesDialogPrivate() :
    ui(0), personal(0), recent(0),
    toItem(0), ccItem(0), bccItem(0),
    ldapSearchDialog(0)
  {}

  AddressPickerUI             *ui;

  AddresseeViewItem           *personal;
  AddresseeViewItem           *recent;
  AddresseeViewItem           *topdist;
  TQPtrList<AddresseeViewItem> dists;

  AddresseeViewItem           *toItem;
  AddresseeViewItem           *ccItem;
  AddresseeViewItem           *bccItem;

  TQDict<AddresseeViewItem>     groupDict;

  TDEABC::Addressee::List       recentAddresses;
  LDAPSearchDialog            *ldapSearchDialog;
};
// privates end

AddresseeViewItem::AddresseeViewItem( AddresseeViewItem *parent, const TDEABC::Addressee& addr,
                                      int emailIndex )
  : TQObject( 0 ), TDEListViewItem( parent, addr.realName(),
                               ( emailIndex == 0 ? addr.preferredEmail() : addr.emails()[ emailIndex ] ) )
{
  d = new AddresseeViewItemPrivate;
  d->address = addr;
  d->category = Entry;

  if ( text( 0 ).stripWhiteSpace().isEmpty() )
    setText( 0, addr.preferredEmail() );

  if ( addr.photo().url().isEmpty() ) {
    if ( addr.photo().data().isNull() )
      setPixmap( 0, TDEGlobal::iconLoader()->loadIcon( "preferences-desktop-personal", TDEIcon::Small ) );
    else
      setPixmap( 0, addr.photo().data().smoothScale( 16, 16 ) );
  } else {
    setPixmap( 0, TDEGlobal::iconLoader()->loadIcon( addr.photo().url(), TDEIcon::Small ) );
  }
}

AddresseeViewItem::AddresseeViewItem( TDEListView *lv, const TQString& name, Category cat )
  : TQObject(0), TDEListViewItem( lv, name )
{
  d = new AddresseeViewItemPrivate;
  d->category = cat;
}

AddresseeViewItem::AddresseeViewItem(  AddresseeViewItem *parent, const TQString& name,
                                       const TDEABC::Addressee::List &lst )
  : TQObject(0), TDEListViewItem( parent, name, i18n("<group>") )
{
  d = new AddresseeViewItemPrivate;
  d->category = FilledGroup;
  d->addresses = lst;
}

AddresseeViewItem::AddresseeViewItem(  AddresseeViewItem *parent, const TQString& name )
  : TQObject(0), TDEListViewItem( parent, name, i18n("<group>") )
{
  d = new AddresseeViewItemPrivate;
  d->category = DistList;

  setPixmap( 0, TDEGlobal::iconLoader()->loadIcon( "tdmconfig", TDEIcon::Small ) );
}

AddresseeViewItem::~AddresseeViewItem()
{
  delete d;
  d = 0;
}

TDEABC::Addressee
AddresseeViewItem::addressee() const
{
  return d->address;
}

TDEABC::Addressee::List
AddresseeViewItem::addresses() const
{
  return d->addresses;
}

AddresseeViewItem::Category
AddresseeViewItem::category() const
{
  return d->category;
}

TQString
AddresseeViewItem::name() const
{
  return text(0);
}

TQString
AddresseeViewItem::email() const
{
  return text(1);
}

bool AddresseeViewItem::matches(const TQString& txt) const
{
    return d->address.realName().contains(txt, false) || d->address.preferredEmail().contains(txt, false);
}

void AddresseeViewItem::setSelected(bool selected)
{
    if (selected == isSelected())
    {
      return;
    }

  emit addressSelected( this, selected );
  TQListViewItem::setSelected(selected);
}

int
AddresseeViewItem::compare( TQListViewItem * i, int col, bool ascending ) const
{
  if ( category() == Group || category() == Entry )
    return TDEListViewItem::compare( i , col, ascending );

  AddresseeViewItem *item = static_cast<AddresseeViewItem*>( i );
  int a = static_cast<int>( category() );
  int b = static_cast<int>( item->category() );

  if ( ascending )
    if ( a < b )
      return -1;
    else
      return 1;
  else
    if ( a < b )
      return 1;
    else
      return -1;
}

AddressesDialog::AddressesDialog( TQWidget *widget, const char *name )
  : KDialogBase( widget, name, true, i18n("Address Selection"),
                 Ok|Cancel, Ok, true )
{
  TQVBox *page = makeVBoxMainWidget();
  d = new AddressesDialogPrivate;
  d->ui = new AddressPickerUI( page );

  TDEABC::StdAddressBook::self( true );
  updateAvailableAddressees();
  initConnections();

  d->ui->mAvailableView->setFocus();

  setMainWidget( page );
  page->setMinimumSize( 750, 400 );
}

AddressesDialog::~AddressesDialog()
{
  delete d;
  d = 0;
}

AddresseeViewItem* AddressesDialog::selectedToItem()
{
  if ( !d->toItem )
  {
    d->toItem = new AddresseeViewItem( d->ui->mSelectedView, i18n("To"), AddresseeViewItem::To );
    connect(d->toItem, TQ_SIGNAL(addressSelected(AddresseeViewItem*, bool)),
            this, TQ_SLOT(selectedAddressSelected(AddresseeViewItem*, bool)));
  }
  return d->toItem;
}

AddresseeViewItem* AddressesDialog::selectedCcItem()
{
  if ( !d->ccItem )
  {
    d->ccItem = new AddresseeViewItem( d->ui->mSelectedView, i18n("CC"), AddresseeViewItem::CC );
    connect(d->ccItem, TQ_SIGNAL(addressSelected(AddresseeViewItem*, bool)),
            this, TQ_SLOT(selectedAddressSelected(AddresseeViewItem*, bool)));
  }
  return d->ccItem;
}

AddresseeViewItem* AddressesDialog::selectedBccItem()
{
  if ( !d->bccItem )
  {
    d->bccItem = new AddresseeViewItem( d->ui->mSelectedView, i18n("BCC"), AddresseeViewItem::BCC );
    connect(d->bccItem, TQ_SIGNAL(addressSelected(AddresseeViewItem*, bool)),
            this, TQ_SLOT(selectedAddressSelected(AddresseeViewItem*, bool)));
  }
  return d->bccItem;
}

void
AddressesDialog::setSelectedTo( const TQStringList& l )
{
  TQString name, email;
  for ( TQStringList::ConstIterator it = l.begin(); it != l.end(); ++it ) {
    TDEABC::Addressee addr;
    TDEABC::Addressee::parseEmailAddress( *it, name, email );
    addr.setNameFromString( name );
    addr.insertEmail( email );
    addAddresseeToSelected( addr, selectedToItem() );
  }
}

void
AddressesDialog::setSelectedCC( const TQStringList& l )
{
  TQString name, email;
  for ( TQStringList::ConstIterator it = l.begin(); it != l.end(); ++it ) {
    TDEABC::Addressee addr;
    TDEABC::Addressee::parseEmailAddress( *it, name, email );
    addr.setNameFromString( name );
    addr.insertEmail( email );
    addAddresseeToSelected( addr, selectedCcItem() );
  }
}

void
AddressesDialog::setSelectedBCC( const TQStringList& l )
{
  TQString name, email;
  for ( TQStringList::ConstIterator it = l.begin(); it != l.end(); ++it ) {
    TDEABC::Addressee addr;
    TDEABC::Addressee::parseEmailAddress( *it, name, email );
    addr.setNameFromString( name );
    addr.insertEmail( email );
    addAddresseeToSelected( addr, selectedBccItem() );
  }
}

void
AddressesDialog::setRecentAddresses( const TDEABC::Addressee::List& list )
{
  d->recentAddresses = list;

  updateRecentAddresses();

  checkForSingleAvailableGroup();
}

void
AddressesDialog::updateRecentAddresses()
{
  static const TQString &recentGroup = TDEGlobal::staticQString( i18n( "Recent Addresses" ) );

  if ( !d->recent ) {
    d->recent = new AddresseeViewItem( d->ui->mAvailableView, recentGroup );
    connect(d->recent, TQ_SIGNAL(addressSelected(AddresseeViewItem*, bool)),
            this, TQ_SLOT(availableAddressSelected(AddresseeViewItem*, bool)));
    d->recent->setVisible( false );
    d->groupDict.insert( recentGroup, d->recent );
  }

  TDEABC::Addressee::List::ConstIterator it;
  for ( it = d->recentAddresses.begin(); it != d->recentAddresses.end(); ++it )
    addAddresseeToAvailable( *it, d->recent );

  if ( d->recent->childCount() > 0 ) {
    d->recent->setOpen( true );
    d->recent->setVisible( true );
  }
}

void
AddressesDialog::setShowCC( bool b )
{
  d->ui->mCCButton->setShown( b );
}

void
AddressesDialog::setShowBCC( bool b )
{
  d->ui->mBCCButton->setShown( b );
}

TQStringList
AddressesDialog::to() const
{
  TQStringList emails = allDistributionLists( d->toItem );
  TDEABC::Addressee::List l = toAddresses();
  emails += entryToString( l );

  return emails;
}

TQStringList
AddressesDialog::cc() const
{
  TQStringList emails = allDistributionLists( d->ccItem );
  TDEABC::Addressee::List l = ccAddresses();
  emails += entryToString( l );

  return emails;
}

TQStringList
AddressesDialog::bcc() const
{
  TQStringList emails = allDistributionLists( d->bccItem );

  TDEABC::Addressee::List l = bccAddresses();
  emails += entryToString( l );

  return emails;
}

TDEABC::Addressee::List
AddressesDialog::toAddresses()  const
{
  return allAddressee( d->toItem );
}
TDEABC::Addressee::List
AddressesDialog::allToAddressesNoDuplicates()  const
{
  TDEABC::Addressee::List aList = allAddressee( d->toItem );
  const TQStringList dList = toDistributionLists();
  TDEABC::AddressBook* abook = TDEABC::StdAddressBook::self( true );
#ifdef TDEPIM_NEW_DISTRLISTS
  for ( TQStringList::ConstIterator it = dList.begin(); it != dList.end(); ++it ) {
    const TQValueList<KPIM::DistributionList::Entry> eList
      = KPIM::DistributionList::findByName(abook, *it).entries(abook);
    TQValueList<KPIM::DistributionList::Entry>::ConstIterator eit;
    for( eit = eList.begin(); eit != eList.end(); ++eit ) {
      TDEABC::Addressee a = (*eit).addressee;
      if ( !a.preferredEmail().isEmpty() && aList.find( a ) == aList.end() ) {
          aList.append( a ) ;
      }
    }
  }
#else
  TDEABC::DistributionListManager manager( abook );
  manager.load();
  for ( TQStringList::ConstIterator it = dList.begin(); it != dList.end(); ++it ) {
    const TQValueList<TDEABC::DistributionList::Entry> eList = manager.list( *it )->entries();
    TQValueList<TDEABC::DistributionList::Entry>::ConstIterator eit;
    for( eit = eList.begin(); eit != eList.end(); ++eit ) {
      TDEABC::Addressee a = (*eit).addressee;
      if ( !a.preferredEmail().isEmpty() && aList.find( a ) == aList.end() ) {
          aList.append( a ) ;
      }
    }
  }
#endif
  return aList;
}

TDEABC::Addressee::List
AddressesDialog::ccAddresses()  const
{
  return allAddressee( d->ccItem );
}

TDEABC::Addressee::List
AddressesDialog::bccAddresses()  const
{
  return allAddressee( d->bccItem );
}


TQStringList
AddressesDialog::toDistributionLists() const
{
  return allDistributionLists( d->toItem );
}

TQStringList
AddressesDialog::ccDistributionLists() const
{
  return allDistributionLists( d->ccItem );
}

TQStringList
AddressesDialog::bccDistributionLists() const
{
  return allDistributionLists( d->bccItem );
}

void
AddressesDialog::updateAvailableAddressees()
{
  d->ui->mAvailableView->clear();
  d->groupDict.clear();

  static const TQString &personalGroup = TDEGlobal::staticQString( i18n( "Other Addresses" ) );
  d->ui->mAvailableView->setRootIsDecorated( true );
  d->personal = new AddresseeViewItem( d->ui->mAvailableView, personalGroup );
  //connect(d->personal, TQ_SIGNAL(addressSelected(AddresseeViewItem*, bool)),
  //        this, TQ_SLOT(selectedAddressSelected(AddresseeViewItem*, bool)));
  d->personal->setVisible( false );
  d->groupDict.insert( personalGroup, d->personal );

  TDEABC::AddressBook *addressBook = TDEABC::StdAddressBook::self( true );
  for( TDEABC::AddressBook::Iterator it = addressBook->begin();
       it != addressBook->end(); ++it ) {
    addAddresseeToAvailable( *it, d->personal );
  }

  d->recent = 0;
  updateRecentAddresses();

  d->topdist = 0;
  addDistributionLists();
  if ( d->personal->childCount() > 0 ) {
    d->personal->setOpen( true );
    d->personal->setVisible( true );
  }

  checkForSingleAvailableGroup();
}

void AddressesDialog::checkForSingleAvailableGroup()
{
  TQListViewItem* item = d->ui->mAvailableView->firstChild();
  TQListViewItem* firstGroup = 0;
  int found = 0;
  while (item)
  {
    if (item->isVisible())
    {
      if (!firstGroup && static_cast<AddresseeViewItem*>(item)->category() != AddresseeViewItem::Entry)
      {
        firstGroup = item;
      }
      ++found;
    }
    item = item->nextSibling();
  }

  if (found == 1 && firstGroup)
  {
    firstGroup->setOpen(true);
  }
}

void
AddressesDialog::availableSelectionChanged()
{
  bool selection = !selectedAvailableAddresses.isEmpty();
  d->ui->mToButton->setEnabled(selection);
  d->ui->mCCButton->setEnabled(selection);
  d->ui->mBCCButton->setEnabled(selection);
}

void
AddressesDialog::selectedSelectionChanged()
{
  bool selection = !selectedSelectedAddresses.isEmpty();
  d->ui->mRemoveButton->setEnabled(selection);
}

void
AddressesDialog::availableAddressSelected( AddresseeViewItem* item, bool selected )
{
  if (selected)
  {
    selectedAvailableAddresses.append(item);
  }
  else
  {
    selectedAvailableAddresses.remove(item);
  }
}

void
AddressesDialog::selectedAddressSelected( AddresseeViewItem* item, bool selected )
{
  // we have to avoid that a parent and a child is selected together
  // because in this case we get a double object deletion ( program crashes )
  // when removing the selected items from list
  AddresseeViewItem* parent = static_cast<AddresseeViewItem*>(((TQListViewItem*)item)->parent());
  if ( parent  && selected )
    parent->setSelected( false );
  if (selected)
  {
    selectedSelectedAddresses.append(item);
  }
  else
  {
    selectedSelectedAddresses.remove(item);
  }
  if ( selected ) {
    AddresseeViewItem* child = static_cast<AddresseeViewItem*>(item->firstChild());
    while (child) {
      child->setSelected( false );
      child = static_cast<AddresseeViewItem*>(child->nextSibling());
    }
  }
}

void
AddressesDialog::initConnections()
{
  connect( d->ui->mFilterEdit, TQ_SIGNAL(textChanged(const TQString &)),
           TQ_SLOT(filterChanged(const TQString &)) );
  connect( d->ui->mToButton, TQ_SIGNAL(clicked()),
           TQ_SLOT(addSelectedTo()) );
  connect( d->ui->mCCButton, TQ_SIGNAL(clicked()),
           TQ_SLOT(addSelectedCC()) );
  connect( d->ui->mBCCButton, TQ_SIGNAL(clicked()),
           TQ_SLOT(addSelectedBCC())  );
  connect( d->ui->mSaveAs, TQ_SIGNAL(clicked()),
           TQ_SLOT(saveAs())  );
  connect( d->ui->mLdapSearch, TQ_SIGNAL(clicked()),
           TQ_SLOT(searchLdap())  );
  connect( d->ui->mRemoveButton, TQ_SIGNAL(clicked()),
           TQ_SLOT(removeEntry()) );
  connect( d->ui->mAvailableView, TQ_SIGNAL(selectionChanged()),
           TQ_SLOT(availableSelectionChanged())  );
  connect( d->ui->mAvailableView, TQ_SIGNAL(doubleClicked(TQListViewItem*)),
           TQ_SLOT(addSelectedTo()) );
  connect( d->ui->mSelectedView, TQ_SIGNAL(selectionChanged()),
           TQ_SLOT(selectedSelectionChanged()) );
  connect( d->ui->mSelectedView, TQ_SIGNAL(doubleClicked(TQListViewItem*)),
           TQ_SLOT(removeEntry()) );

#ifndef TDEPIM_NEW_DISTRLISTS
  connect( TDEABC::DistributionListWatcher::self(), TQ_SIGNAL( changed() ),
           this, TQ_SLOT( updateAvailableAddressees() ) );
#endif

  connect( TDEABC::StdAddressBook::self( true ), TQ_SIGNAL( addressBookChanged(AddressBook*) ),
           this, TQ_SLOT( updateAvailableAddressees() ) );
}

void
AddressesDialog::addAddresseeToAvailable( const TDEABC::Addressee& addr, AddresseeViewItem* defaultParent, bool useCategory )
{
  if ( addr.preferredEmail().isEmpty() )
    return;

  if ( useCategory ) {
    TQStringList categories = addr.categories();

    for ( TQStringList::Iterator it = categories.begin(); it != categories.end(); ++it ) {
      if ( !d->groupDict[ *it ] ) {  //we don't have the category yet
        AddresseeViewItem* category = new AddresseeViewItem( d->ui->mAvailableView, *it );
        d->groupDict.insert( *it,  category );
      }

      for ( uint i = 0; i < addr.emails().count(); ++i ) {
        AddresseeViewItem* addressee = new AddresseeViewItem( d->groupDict[ *it ], addr, i );
        connect(addressee, TQ_SIGNAL(addressSelected(AddresseeViewItem*, bool)),
                this, TQ_SLOT(availableAddressSelected(AddresseeViewItem*, bool)));
      }
    }
  }

  bool noCategory = false;
  if ( useCategory ) {
    if ( addr.categories().isEmpty() )
      noCategory = true;
  } else
    noCategory = true;

  if ( defaultParent && noCategory ) { // only non-categorized items here
    AddresseeViewItem* addressee = new AddresseeViewItem( defaultParent, addr );
    connect(addressee, TQ_SIGNAL(addressSelected(AddresseeViewItem*, bool)),
            this, TQ_SLOT(availableAddressSelected(AddresseeViewItem*, bool)));
  }
}

void
AddressesDialog::addAddresseeToSelected( const TDEABC::Addressee& addr, AddresseeViewItem* defaultParent )
{
  if ( addr.preferredEmail().isEmpty() )
    return;

  if ( defaultParent ) {
    AddresseeViewItem *myChild = static_cast<AddresseeViewItem*>( defaultParent->firstChild() );
    while( myChild ) {
      if ( myChild->addressee().preferredEmail() == addr.preferredEmail() )
        return;//already got it
      myChild = static_cast<AddresseeViewItem*>( myChild->nextSibling() );
    }
    AddresseeViewItem* addressee = new AddresseeViewItem( defaultParent, addr );
    connect(addressee, TQ_SIGNAL(addressSelected(AddresseeViewItem*, bool)),
            this, TQ_SLOT(selectedAddressSelected(AddresseeViewItem*, bool)));
    defaultParent->setOpen( true );
  }

  d->ui->mSaveAs->setEnabled(true);
}

void
AddressesDialog::addAddresseesToSelected( AddresseeViewItem *parent,
                                          const TQPtrList<AddresseeViewItem>& addresses )
{
  Q_ASSERT( parent );

  TQPtrListIterator<AddresseeViewItem> itr( addresses );

  if (itr.current())
  {
    d->ui->mSaveAs->setEnabled(true);
  }

  while ( itr.current() ) {
    AddresseeViewItem* address = itr.current();
    ++itr;

    if (selectedToAvailableMapping.find(address) != 0)
    {
      continue;
    }

    AddresseeViewItem* newItem = 0;
    if (address->category() == AddresseeViewItem::Entry)
    {
      newItem = new AddresseeViewItem( parent, address->addressee() );
    }
    else if (address->category() == AddresseeViewItem::DistList)
    {
      newItem = new AddresseeViewItem( parent, address->name() );
    }
    else
    {
      newItem = new AddresseeViewItem( parent, address->name(), allAddressee( address ) );
    }

    address->setSelected( false );
    address->setVisible( false );
    selectedToAvailableMapping.insert(address, newItem);
    selectedToAvailableMapping.insert(newItem, address);
    connect(newItem, TQ_SIGNAL(addressSelected(AddresseeViewItem*, bool)),
            this, TQ_SLOT(selectedAddressSelected(AddresseeViewItem*, bool)));
  }

  parent->setOpen( true );
}

TQStringList
AddressesDialog::entryToString( const TDEABC::Addressee::List& l ) const
{
  TQStringList entries;

  for( TDEABC::Addressee::List::ConstIterator it = l.begin(); it != l.end(); ++it ) {
    entries += (*it).fullEmail();
  }
  return entries;
}

void
AddressesDialog::addSelectedTo()
{
  if ( !d->toItem )
  {
    d->toItem = new AddresseeViewItem( d->ui->mSelectedView, i18n("To"), AddresseeViewItem::To );
    connect(d->toItem, TQ_SIGNAL(addressSelected(AddresseeViewItem*, bool)),
            this, TQ_SLOT(selectedAddressSelected(AddresseeViewItem*, bool)));
  }

  addAddresseesToSelected( d->toItem, selectedAvailableAddresses );
  selectedAvailableAddresses.clear();

  if ( d->toItem->childCount() > 0 ) {
    d->toItem->setVisible( true );
  } else {
    delete d->toItem;
    d->toItem = 0;
  }
}

void
AddressesDialog::addSelectedCC()
{
  if ( !d->ccItem )
  {
    d->ccItem = new AddresseeViewItem( d->ui->mSelectedView, i18n("CC"), AddresseeViewItem::CC );
    connect(d->ccItem , TQ_SIGNAL(addressSelected(AddresseeViewItem*, bool)),
            this, TQ_SLOT(selectedAddressSelected(AddresseeViewItem*, bool)));
  }

  addAddresseesToSelected( d->ccItem, selectedAvailableAddresses );
  selectedAvailableAddresses.clear();

  if ( d->ccItem->childCount() > 0 ) {
    d->ccItem->setVisible( true );
  } else {
    delete d->ccItem;
    d->ccItem = 0;
  }
}

void
AddressesDialog::addSelectedBCC()
{
  if ( !d->bccItem )
  {
    d->bccItem = new AddresseeViewItem( d->ui->mSelectedView, i18n("BCC"), AddresseeViewItem::BCC );
    connect(d->bccItem , TQ_SIGNAL(addressSelected(AddresseeViewItem*, bool)),
            this, TQ_SLOT(selectedAddressSelected(AddresseeViewItem*, bool)));
  }

  addAddresseesToSelected( d->bccItem, selectedAvailableAddresses );
  selectedAvailableAddresses.clear();

  if ( d->bccItem->childCount() > 0 ) {
    d->bccItem->setVisible( true );
  } else {
    delete d->bccItem;
    d->bccItem = 0;
  }
}

void AddressesDialog::unmapSelectedAddress(AddresseeViewItem* item)
{
  AddresseeViewItem* correspondingItem = selectedToAvailableMapping[item];
  if (correspondingItem)
  {
    correspondingItem->setVisible( true );
    selectedToAvailableMapping.remove( item );
    selectedToAvailableMapping.remove( correspondingItem );
  }

  AddresseeViewItem* child = static_cast<AddresseeViewItem*>(item->firstChild());
  while (child)
  {
    unmapSelectedAddress(child);
    child = static_cast<AddresseeViewItem*>(child->nextSibling());
  }
}

void
AddressesDialog::removeEntry()
{
  TQPtrList<AddresseeViewItem> lst;
  bool resetTo  = false;
  bool resetCC  = false;
  bool resetBCC = false;

  lst.setAutoDelete( false );
  TQPtrListIterator<AddresseeViewItem> it( selectedSelectedAddresses );
  while ( it.current() ) {
    AddresseeViewItem* item = it.current();
    ++it;
    if ( d->toItem == item )
      resetTo = true;
    else if ( d->ccItem == item )
      resetCC = true;
    else if( d->bccItem == item )
      resetBCC = true;
    // we may only append parent items
    unmapSelectedAddress(item);
    lst.append( item );
  }
  selectedSelectedAddresses.clear();
  lst.setAutoDelete( true );
  lst.clear();
  if ( resetTo )
    d->toItem  = 0;
  else if ( d->toItem && d->toItem->childCount() == 0 )
  {
    delete d->toItem;
    d->toItem = 0;
  }
  if ( resetCC )
    d->ccItem = 0;
  else if ( d->ccItem && d->ccItem->childCount() == 0 )
  {
    delete d->ccItem;
    d->ccItem = 0;
  }
  if ( resetBCC )
    d->bccItem  = 0;
  else if ( d->bccItem && d->bccItem->childCount() == 0 )
  {
    delete d->bccItem;
    d->bccItem = 0;
  }
  d->ui->mSaveAs->setEnabled(d->ui->mSelectedView->firstChild() != 0);
}

#ifdef TDEPIM_NEW_DISTRLISTS

// copied from tdeabcore.cpp :(
// KDE4: should be in libtdeabc I think
static TDEABC::Resource *requestResource( TDEABC::AddressBook* abook, TQWidget *parent )
{
  TQPtrList<TDEABC::Resource> tdeabcResources = abook->resources();

  TQPtrList<KRES::Resource> kresResources;
  TQPtrListIterator<TDEABC::Resource> resIt( tdeabcResources );
  TDEABC::Resource *resource;
  while ( ( resource = resIt.current() ) != 0 ) {
    ++resIt;
    if ( !resource->readOnly() ) {
      KRES::Resource *res = static_cast<KRES::Resource*>( resource );
      if ( res )
        kresResources.append( res );
    }
  }

  KRES::Resource *res = KRES::SelectDialog::getResource( kresResources, parent );
  return static_cast<TDEABC::Resource*>( res );
}
#endif

void
AddressesDialog::saveAs()
{
#ifndef TDEPIM_NEW_DISTRLISTS
  TDEABC::DistributionListManager manager( TDEABC::StdAddressBook::self( true ) );
  manager.load();
#endif

  if ( !d->ui->mSelectedView->firstChild() ) {
    KMessageBox::information( 0,
                              i18n("There are no addresses in your list. "
                                   "First add some addresses from your address book, "
                                   "then try again.") );
    return;
  }

  bool ok = false;
  TQString name = KInputDialog::getText( i18n("New Distribution List"),
                                        i18n("Please enter name:"),
                                        TQString(), &ok,
                                        this );
  if ( !ok || name.isEmpty() )
    return;

  bool alreadyExists = false;
#ifdef TDEPIM_NEW_DISTRLISTS
  TDEABC::AddressBook* abook = TDEABC::StdAddressBook::self( true );
  KPIM::DistributionList dlist = KPIM::DistributionList::findByName( abook, name );
  alreadyExists = !dlist.isEmpty();
#else
  alreadyExists = manager.list( name );
#endif

  if ( alreadyExists ) {
    KMessageBox::information( 0,
                              i18n( "<qt>Distribution list with the given name <b>%1</b> "
                                    "already exists. Please select a different name.</qt>" )
                              .arg( name ) );
    return;
  }

#ifdef TDEPIM_NEW_DISTRLISTS
  TDEABC::Resource* resource = requestResource( abook, this );
  if ( !resource )
    return;

  dlist.setResource( resource );
  dlist.setName( name );
  TDEABC::Addressee::List addrl = allAddressee( d->ui->mSelectedView, false );
  for ( TDEABC::Addressee::List::iterator itr = addrl.begin();
        itr != addrl.end(); ++itr ) {
    dlist.insertEntry( *itr );
  }
  abook->insertAddressee( dlist );
#else
  TDEABC::DistributionList *dlist = new TDEABC::DistributionList( &manager, name );
  TDEABC::Addressee::List addrl = allAddressee( d->ui->mSelectedView, false );
  for ( TDEABC::Addressee::List::iterator itr = addrl.begin();
        itr != addrl.end(); ++itr ) {
    dlist->insertEntry( *itr );
  }

  manager.save();
#endif
}

void
AddressesDialog::searchLdap()
{
    if ( !d->ldapSearchDialog ) {
      d->ldapSearchDialog = new LDAPSearchDialog( this );
      connect( d->ldapSearchDialog, TQ_SIGNAL( addresseesAdded() ),
               TQ_SLOT(ldapSearchResult() ) );
    }
    d->ldapSearchDialog->show();
}

void
AddressesDialog::ldapSearchResult()
{
  TQStringList emails = KPIM::splitEmailAddrList( d->ldapSearchDialog->selectedEMails() );
  TQStringList::iterator it( emails.begin() );
  TQStringList::iterator end( emails.end() );
  for ( ; it != end; ++it ){
      TQString name;
      TQString email;
      KPIM::getNameAndMail( (*it), name, email );
      TDEABC::Addressee ad;
      ad.setNameFromString( name );
      ad.insertEmail( email );
      addAddresseeToSelected( ad, selectedToItem() );
  }
}

void
AddressesDialog::launchAddressBook()
{
  kapp->startServiceByDesktopName( "kaddressbook", TQString() );
}

void
AddressesDialog::filterChanged( const TQString& txt )
{
  TQListViewItemIterator it( d->ui->mAvailableView );
  bool showAll = false;

  if ( txt.isEmpty() )
    showAll = true;

  int personalVisible = 0;
  int recentVisible = 0;
  while ( it.current() ) {
    AddresseeViewItem* item = static_cast<AddresseeViewItem*>( it.current() );
    ++it;

    if ( showAll ) {
      item->setOpen( true );
      item->setVisible( true );
      // allen: I do not like the following behavior. comment out and see if anyone screams
      //if ( item->category() == AddresseeViewItem::Group )
      //  item->setOpen( false );//close to not have too many entries
      continue;
    }

    if ( item->category() == AddresseeViewItem::Entry ) {
      bool matches = item->matches( txt );
      item->setVisible( matches );
      TQListViewItem *parent = static_cast<TQListViewItem*>( item )->parent();
      if ( matches && parent ) {
        if ( parent == d->personal ) {
          personalVisible++;
        } else if ( parent == d->recent ) {
          recentVisible++;
        }
      }
    }
    if ( item->category() == AddresseeViewItem::Group ) {
      item->setOpen( true );
      item->setVisible( true );
    }
  }

  if ( !showAll && personalVisible == 0 ) {
    d->personal->setOpen( false );
    d->personal->setVisible( false );
  }
  if ( !showAll && recentVisible == 0 ) {
    d->recent->setOpen( false );
    d->recent->setVisible( false );
  }

  int distlistgroupVisible = 0;
  if ( !showAll ) {
    TQPtrListIterator<AddresseeViewItem> it( d->dists );
    for ( ; it.current(); ++it ) {
      TQListViewItem *p = *it;
      p->setVisible( true );
      AddresseeViewItem *p2 = static_cast<AddresseeViewItem*>( p->firstChild() );
      int pcount = 0;
      while ( p2 ) {
        if ( p2->matches( txt ) ) {
          p2->setVisible( true );
          pcount++;
        } else {
          p2->setVisible( false );
        }
        p2 = static_cast<AddresseeViewItem*>( p2->nextSibling() );
      }
      if ( !pcount && !p->text( 0 ).contains( txt, false ) ) {
        p->setVisible( false );
      }
      distlistgroupVisible += pcount;
      if ( p->text( 0 ).contains( txt, false ) ) {
        distlistgroupVisible++;
      }
    }
  }
  if ( d->topdist ) {
    if ( showAll || distlistgroupVisible > 0 ) {
      d->topdist->setOpen( true );
    } else {
      d->topdist->setOpen( false );
      d->topdist->setVisible( false );
    }
  }
}

TDEABC::Addressee::List
AddressesDialog::allAddressee( TDEListView* view, bool onlySelected ) const
{
  TDEABC::Addressee::List lst;
  TQListViewItemIterator it( view );
  while ( it.current() ) {
    AddresseeViewItem* item = static_cast<AddresseeViewItem*>( it.current() );
    if ( !onlySelected || item->isSelected() ) {
      if ( item->category() != AddresseeViewItem::Entry  ) {
        AddresseeViewItem *myChild = static_cast<AddresseeViewItem*>( item->firstChild() );
        while( myChild ) {
          lst.append( myChild->addressee() );
          myChild = static_cast<AddresseeViewItem*>( myChild->nextSibling() );
        }
      } else {
        lst.append( item->addressee() );
      }
    }
    ++it;
  }

  return lst;
}

TDEABC::Addressee::List
AddressesDialog::allAddressee( AddresseeViewItem* parent ) const
{
  TDEABC::Addressee::List lst;

  if ( !parent ) return lst;

  if ( parent->category() == AddresseeViewItem::Entry )
  {
    lst.append( parent->addressee() );
    return lst;
  }

  AddresseeViewItem *myChild = static_cast<AddresseeViewItem*>( parent->firstChild() );
  while( myChild ) {
    if ( myChild->category() == AddresseeViewItem::FilledGroup )
      lst += myChild->addresses();
    else if ( !myChild->addressee().isEmpty() )
      lst.append( myChild->addressee() );
    myChild = static_cast<AddresseeViewItem*>( myChild->nextSibling() );
  }

  return lst;
}

TQStringList
AddressesDialog::allDistributionLists( AddresseeViewItem* parent ) const
{
  TQStringList lists;

  if ( !parent )
    return TQStringList();

  AddresseeViewItem *item = static_cast<AddresseeViewItem*>( parent->firstChild() );
  while ( item ) {
    if ( item->category() == AddresseeViewItem::DistList && !item->name().isEmpty() )
      lists.append( item->name() );

    item = static_cast<AddresseeViewItem*>( item->nextSibling() );
  }

  return lists;
}

void
AddressesDialog::addDistributionLists()
{
  TDEABC::AddressBook* abook = TDEABC::StdAddressBook::self( true );

#ifdef TDEPIM_NEW_DISTRLISTS
  const TQValueList<KPIM::DistributionList> distLists =
    KPIM::DistributionList::allDistributionLists( abook );
#else
  TDEABC::DistributionListManager manager( abook );
  manager.load();

  TQStringList distLists = manager.listNames();
#endif

  if ( distLists.isEmpty() )
    return;

  if ( !d->topdist ) {
    d->topdist = new AddresseeViewItem( d->ui->mAvailableView, i18n( "Distribution Lists" ) );
  }

#ifdef TDEPIM_NEW_DISTRLISTS
  TQValueList<KPIM::DistributionList>::ConstIterator listIt;
#else
  TQStringList::Iterator listIt;
#endif
  int total = 0;
  for ( listIt = distLists.begin(); listIt != distLists.end(); ++listIt ) {
#ifdef TDEPIM_NEW_DISTRLISTS
    KPIM::DistributionList dlist = *listIt;
    KPIM::DistributionList::Entry::List entries = dlist.entries(abook);
#else
    TDEABC::DistributionList& dlist = *manager.list( *listIt );
    TDEABC::DistributionList::Entry::List entries = dlist.entries();
#endif

    AddresseeViewItem *item = new AddresseeViewItem( d->topdist, dlist.name() );
    d->dists.append( item );
    connect( item, TQ_SIGNAL( addressSelected( AddresseeViewItem*, bool ) ),
             this, TQ_SLOT( availableAddressSelected( AddresseeViewItem*, bool ) ) );

#ifdef TDEPIM_NEW_DISTRLISTS
    KPIM::DistributionList::Entry::List::Iterator itemIt;
#else
    TDEABC::DistributionList::Entry::List::Iterator itemIt;
#endif
    for ( itemIt = entries.begin(); itemIt != entries.end(); ++itemIt ) {
      addAddresseeToAvailable( (*itemIt).addressee, item, false );
    }
    if ( item->childCount() > 0 ) {
      item->setOpen( true );
      item->setVisible( true );
    }
    total += item->childCount();
  }
  if ( total > 0 ) {
    d->topdist->setOpen( true );
    d->topdist->setVisible( true );
  }
}

} // namespace

#include "addressesdialog.moc"
