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

/*
  shows a user tree of friends per server
  begin:     Sam Aug 31 2002
  copyright: (C) 2002 by Dario Abatianni
  email:     eisfuchs@tigress.com
*/

#include "nicksonline.h"
#include "channel.h"
#include "server.h"
#include "konversationapplication.h"
#include "connectionmanager.h"
#include "images.h"
#include "query.h"
#include "linkaddressbook/linkaddressbookui.h"
#include "linkaddressbook/addressbook.h"
#include "linkaddressbook/nicksonlinetooltip.h"
#include "konversationmainwindow.h"
#include "viewcontainer.h"
#include "nicksonlineitem.h"

#include <tqlayout.h>
#include <tqstringlist.h>
#include <tqhbox.h>
#include <tqpushbutton.h>
#include <tqlabel.h>
#include <tqpopupmenu.h>
#include <tqptrlist.h>
#include <tqwhatsthis.h>


#include <kdebug.h>
#include <tdelocale.h>
#include <kdialog.h>
#include <tdelistview.h>
#include <kiconloader.h>
#include <kprocess.h>
#include <tdemessagebox.h>


NicksOnline::NicksOnline(TQWidget* parent): ChatWindow(parent)
{
    setName(i18n("Watched Nicks Online"));
    setType(ChatWindow::NicksOnline);

    m_nickListView=new TDEListView(this);

    // Set to false every 8 seconds to permit a whois on watched nicks lacking information.
    // Remove when server or addressbook does this automatically.
    m_whoisRequested = true;

    m_nickListView->addColumn(i18n("Network/Nickname/Channel"));
    m_tdeabcIconSet = TDEGlobal::iconLoader()->loadIconSet("kaddressbook",TDEIcon::Small);
    m_nickListView->addColumn(i18n("Additional Information"));
    m_nickListView->setFullWidth(true);
    m_nickListView->setRootIsDecorated(true);
    m_nickListView->setShowToolTips(false);
    m_nickListView->setShadeSortColumn(true);
    m_nickListView->setShowSortIndicator(true);

    TQString nickListViewWT = i18n(
        "<p>These are all the nicknames on your Nickname Watch list, listed under the "
        "server network they are connected to.  The list also includes the nicknames "
        "in KAddressBook associated with the server network.</p>"
        "<p>The <b>Additional Information</b> column shows the information known "
        "for each nickname.</p>"
        "<p>The channels the nickname has joined are listed underneath each nickname.</p>"
        "<p>Nicknames appearing under <b>Offline</b> are not connected to any of the "
        "servers in the network.</p>"
        "<p>Right-click with the mouse on a nickname to perform additional functions.</p>");
    TQWhatsThis::add(m_nickListView, nickListViewWT);

    m_tooltip = new Konversation::KonversationNicksOnlineToolTip(m_nickListView->viewport(), this);

    setMargin(margin());
    setSpacing(spacing());

    TQHBox* buttonBox=new TQHBox(this);
    buttonBox->setSpacing(spacing());
    TQPushButton* editButton=new TQPushButton(i18n("&Edit Watch List..."),
        buttonBox,"edit_notify_button");
    TQString editButtonWT = i18n(
        "Click to edit the list of nicknames that appear on this screen.");
    TQWhatsThis::add(editButton, editButtonWT);

    connect(editButton, TQ_SIGNAL(clicked()), TQ_SIGNAL(editClicked()) );
    connect(m_nickListView, TQ_SIGNAL(doubleClicked(TQListViewItem*)),
        this,TQ_SLOT(processDoubleClick(TQListViewItem*)));

    TQLabel* addressbookLabel = new TQLabel(i18n("Address book:"),
        buttonBox, "nicksonline_addressbook_label");
    TQString addressbookLabelWT = i18n(
        "When you select a nickname in the list above, the buttons here are used "
        "to associate the nickname with an entry in KAddressBook.");
    TQWhatsThis::add(addressbookLabel, addressbookLabelWT);
    addressbookLabel->setAlignment(TQt::AlignRight | TQt::AlignVCenter);
    m_editContactButton = new TQPushButton(i18n("Edit C&ontact..."),
        buttonBox, "nicksonline_editcontact_button");
    TQString editContactButtonWT = i18n(
        "Click to create, view, or edit the KAddressBook entry associated with the nickname "
        "selected above.");
    TQWhatsThis::add(m_editContactButton, editContactButtonWT);
    m_editContactButton->setIconSet(m_tdeabcIconSet);
    m_changeAssociationButton = new TQPushButton(i18n("&Change Association..."),
        buttonBox, "nicksonline_changeassociation_button");
    TQString changeAssociationButtonWT = i18n(
        "Click to associate the nickname selected above with an entry in KAddressBook.");
    TQWhatsThis::add(m_changeAssociationButton, changeAssociationButtonWT);
    m_changeAssociationButton->setIconSet(m_tdeabcIconSet);
    m_deleteAssociationButton = new TQPushButton(i18n("&Delete Association"),
        buttonBox, "nicksonline_deleteassociation_button");
    TQString deleteAssociationButtonWT = i18n(
        "Click to remove the association between the nickname selected above and a "
        "KAddressBook entry.");
    TQWhatsThis::add(m_deleteAssociationButton, deleteAssociationButtonWT);
    m_deleteAssociationButton->setIconSet(m_tdeabcIconSet);

    connect(m_editContactButton, TQ_SIGNAL(clicked()),
        this, TQ_SLOT(slotEditContactButton_Clicked()));
    connect(m_changeAssociationButton, TQ_SIGNAL(clicked()),
        this, TQ_SLOT(slotChangeAssociationButton_Clicked()));
    connect(m_deleteAssociationButton, TQ_SIGNAL(clicked()),
        this, TQ_SLOT(slotDeleteAssociationButton_Clicked()));
    connect(m_nickListView, TQ_SIGNAL(selectionChanged()),
        this, TQ_SLOT(slotNickListView_SelectionChanged()));

    setupAddressbookButtons(nsNotANick);

    // Create context menu.  Individual menu entries are created in rightButtonClicked slot.
    m_popupMenu = new TQPopupMenu(this,"nicksonline_context_menu");
    connect(m_nickListView, TQ_SIGNAL(rightButtonClicked(TQListViewItem *, const TQPoint &, int )),
        this, TQ_SLOT(slotNickListView_RightButtonClicked(TQListViewItem*, const TQPoint &)));
    connect(m_popupMenu, TQ_SIGNAL(activated(int)),
        this, TQ_SLOT(slotPopupMenu_Activated(int)));

    // Display info for all currently-connected servers.
    refreshAllServerOnlineLists();

    // Connect and start refresh timer.
    m_timer = new TQTimer(this, "nicksOnlineTimer");
    connect(m_timer, TQ_SIGNAL (timeout()), this, TQ_SLOT(timerFired()));
    // TODO: User preference for refresh interval.
    m_timer->start(8000);
}

NicksOnline::~NicksOnline()
{
    m_timer->stop();
    delete m_timer;
    delete m_nickListView;
}

TDEListView* NicksOnline::getNickListView()
{
    return m_nickListView;
}

/**
 * Returns the named child of parent item in a NicksOnlineItem
 * @param parent            Pointer to a NicksOnlineItem.
 * @param name              The name in the desired child TQListViewItem, must be in column 0.
 * @param type              The type of entry to be found
 * @return                  Pointer to the child TQListViewItem or 0 if not found.
 */
TQListViewItem* NicksOnline::findItemChild(const TQListViewItem* parent, const TQString& name, NicksOnlineItem::NickListViewColumn type)
{
    if (!parent) return 0;
    TQListViewItem* child;
    for (child = parent->firstChild(); (child) ; child = child->nextSibling())
    {
        if(static_cast<NicksOnlineItem*>(child)->type() == type && child->text(0) == name) return child;
    }
    return 0;
}

/**
 * Returns the first occurrence of a child item of a given type in a NicksOnlineItem
 * @param parent            Pointer to a NicksOnlineItem.
 * @param type              The type of entry to be found
 * @return                  Pointer to the child TQListViewItem or 0 if not found.
 */
TQListViewItem* NicksOnline::findItemType(const TQListViewItem* parent, NicksOnlineItem::NickListViewColumn type)
{
    if (!parent) return 0;
    TQListViewItem* child;
    for (child = parent->firstChild(); (child) ; child = child->nextSibling())
    {
        if(static_cast<NicksOnlineItem*>(child)->type() == type) return child;
    }
    return 0;
}

/**
 * Returns a pointer to the network TQListViewItem with the given name.
 * @param name              The name of the network, assumed to be in column 0 of the item.
 * @return                  Pointer to the TQListViewItem or 0 if not found.
 */
TQListViewItem* NicksOnline::findNetworkRoot(const TQString& name)
{
    TQListViewItem* child;
    for (child = getNickListView()->firstChild(); (child) ; child = child->nextSibling())
    {
        if (child->text(0) == name) return child;
    }
    return 0;
}

/**
 * Return a string containing formatted additional information about a nick.
 * @param nickInfo          A pointer to NickInfo structure for the nick.  May be Null.
 * @param addressee         Addressbook entry for the nick.  May be empty.
 * @return                  A string formatted for display containing the information
 *                          about the nick.
 * @return needWhois        True if a WHOIS needs to be performed on the nick
 *                          to get additional information.
 */
TQString NicksOnline::getNickAdditionalInfo(NickInfoPtr nickInfo, TDEABC::Addressee addressee,
bool& needWhois)
{
    TQString info;
    if (!addressee.isEmpty())
    {
        if (addressee.fullEmail().isEmpty())
            info += addressee.realName();
        else
            info += addressee.fullEmail();
    }
    TQString niInfo;
    if (nickInfo)
    {
        if (nickInfo->isAway())
        {
            niInfo += i18n("Away");
            if (!nickInfo->getAwayMessage().isEmpty())
                niInfo += '(' + nickInfo->getAwayMessage() + ')';
        }
        if (!nickInfo->getHostmask().isEmpty())
            niInfo += ' ' + nickInfo->getHostmask();
        if (!nickInfo->getRealName().isEmpty())
            niInfo += " (" + nickInfo->getRealName() + ')';
        if (!nickInfo->getNetServer().isEmpty())
        {
            niInfo += i18n( " online via %1" ).arg( nickInfo->getNetServer() );
            if (!nickInfo->getNetServerInfo().isEmpty())
                niInfo += " (" + nickInfo->getNetServerInfo() + ')';
        }
        if (!nickInfo->getOnlineSince().isNull())
            niInfo += i18n( " since %1" ).arg( nickInfo->getPrettyOnlineSince() );
    }
    needWhois = niInfo.isEmpty();
    if (!info.isEmpty() && !needWhois) info += ' ';
    return info + niInfo;
}

/**
 * Refresh the nicklistview for a single server.
 * @param server            The server to be refreshed.
 */
void NicksOnline::updateServerOnlineList(Server* servr)
{
    bool newNetworkRoot = false;
    TQString serverName = servr->getServerName();
    TQString networkName = servr->getDisplayName();
    TQListViewItem* networkRoot = findNetworkRoot(networkName);
    // If network is not in our list, add it.
    if (!networkRoot)
    {
        networkRoot = new NicksOnlineItem(NicksOnlineItem::NetworkRootItem,m_nickListView,networkName);
        newNetworkRoot = true;
    }
    // Store server name in hidden column.
    // Note that there could be more than one server in the network connected,
    // but it doesn't matter because all the servers in a network have the same
    // watch list.
    networkRoot->setText(nlvcServerName, serverName);
    // Update list of servers in the network that are connected.
    TQStringList serverList = TQStringList::split(",", networkRoot->text(nlvcAdditionalInfo));
    if (!serverList.contains(serverName)) serverList.append(serverName);
    networkRoot->setText(nlvcAdditionalInfo, serverList.join(","));
    // Get item in nicklistview for the Offline branch.
    TQListViewItem* offlineRoot = findItemType(networkRoot, NicksOnlineItem::OfflineItem);
    if (!offlineRoot)
    {
        offlineRoot = new NicksOnlineItem(NicksOnlineItem::OfflineItem,networkRoot,i18n("Offline"));
        offlineRoot->setText(nlvcServerName, serverName);
    }

    // Get watch list.
    TQStringList watchList = servr->getWatchList();
    TQStringList::iterator itEnd = watchList.end();
    TQString nickname;

    for (TQStringList::iterator it = watchList.begin(); it != itEnd; ++it)
    {
        nickname = (*it);
        NickInfoPtr nickInfo = getOnlineNickInfo(networkName, nickname);

        if (nickInfo && nickInfo->getPrintedOnline())
        {
            // Nick is online.
            // Which server did NickInfo come from?
            Server* server=nickInfo->getServer();
            // Get addressbook entry (if any) for the nick.
            TDEABC::Addressee addressee = nickInfo->getAddressee();
            // Construct additional information string for nick.
            bool needWhois = false;
            TQString nickAdditionalInfo = getNickAdditionalInfo(nickInfo, addressee, needWhois);
            // Remove from offline branch if present.
            TQListViewItem* item = findItemChild(offlineRoot, nickname, NicksOnlineItem::NicknameItem);
            if (item) delete item;
            // Add to network if not already added.
            TQListViewItem* nickRoot = findItemChild(networkRoot, nickname, NicksOnlineItem::NicknameItem);
            if (!nickRoot) nickRoot = new NicksOnlineItem(NicksOnlineItem::NicknameItem,networkRoot, nickname, nickAdditionalInfo);
            nickRoot->setText(nlvcAdditionalInfo, nickAdditionalInfo);
            nickRoot->setText(nlvcServerName, serverName);
            // If no additional info available, request a WHOIS on the nick.
            if (!m_whoisRequested)
            {
                if (needWhois)
                {
                    requestWhois(networkName, nickname);
                    m_whoisRequested = true;
                }
            }
            // Set Kabc icon if the nick is associated with an addressbook entry.
            if (!addressee.isEmpty())
                nickRoot->setPixmap(nlvcKabc, m_tdeabcIconSet.pixmap(
                    TQIconSet::Small, TQIconSet::Normal, TQIconSet::On));
            else
                nickRoot->setPixmap(nlvcKabc, m_tdeabcIconSet.pixmap(
                    TQIconSet::Small, TQIconSet::Disabled, TQIconSet::Off));

            TQStringList channelList = server->getNickChannels(nickname);
            TQStringList::iterator itEnd2 = channelList.end();

            for (TQStringList::iterator it2 = channelList.begin(); it2 != itEnd2; ++it2)
            {
                // Known channels where nickname is online and mode in each channel.
                // FIXME: If user connects to multiple servers in same network, the
                // channel info will differ between the servers, resulting in inaccurate
                // mode and led info displayed.

                TQString channelName = (*it2);

                ChannelNickPtr channelNick = server->getChannelNick(channelName, nickname);
                TQString nickMode;
                if (channelNick->hasVoice()) nickMode = nickMode + i18n(" Voice");
                if (channelNick->isHalfOp()) nickMode = nickMode + i18n(" HalfOp");
                if (channelNick->isOp()) nickMode = nickMode + i18n(" Operator");
                if (channelNick->isOwner()) nickMode = nickMode + i18n(" Owner");
                if (channelNick->isAdmin()) nickMode = nickMode + i18n(" Admin");
                TQListViewItem* channelItem = findItemChild(nickRoot, channelName, NicksOnlineItem::ChannelItem);
                if (!channelItem) channelItem = new NicksOnlineItem(NicksOnlineItem::ChannelItem,nickRoot,
                        channelName, nickMode);
                channelItem->setText(nlvcAdditionalInfo, nickMode);

                // Icon for mode of nick in each channel.
                Images::NickPrivilege nickPrivilege = Images::Normal;
                if (channelNick->hasVoice()) nickPrivilege = Images::Voice;
                if (channelNick->isHalfOp()) nickPrivilege = Images::HalfOp;
                if (channelNick->isOp()) nickPrivilege = Images::Op;
                if (channelNick->isOwner()) nickPrivilege = Images::Owner;
                if (channelNick->isAdmin()) nickPrivilege = Images::Admin;
                if (server->getJoinedChannelMembers(channelName) != 0)
                    channelItem->setPixmap(nlvcChannel,
                        KonversationApplication::instance()->images()->getNickIcon(nickPrivilege, false));
                else
                    channelItem->setPixmap(nlvcChannel,
                        KonversationApplication::instance()->images()->getNickIcon(nickPrivilege, true));
            }
            // Remove channel if nick no longer in it.
            TQListViewItem* child = nickRoot->firstChild();
            while (child)
            {
                TQListViewItem* nextChild = child->nextSibling();
                if (channelList.find(child->text(nlvcNick)) == channelList.end())
                    delete child;
                child = nextChild;
            }
        }
        else
        {
            // Nick is offline.
            // Remove from online nicks, if present.
            TQListViewItem* item = findItemChild(networkRoot, nickname, NicksOnlineItem::NicknameItem);
            if (item) delete item;
            // Add to offline list if not already listed.
            TQListViewItem* nickRoot = findItemChild(offlineRoot, nickname, NicksOnlineItem::NicknameItem);
            if (!nickRoot) nickRoot = new NicksOnlineItem(NicksOnlineItem::NicknameItem,offlineRoot, nickname);
            nickRoot->setText(nlvcServerName, serverName);
            // Get addressbook entry for the nick.
            TDEABC::Addressee addressee = servr->getOfflineNickAddressee(nickname);
            // Format additional information for the nick.
            bool needWhois = false;
            TQString nickAdditionalInfo = getNickAdditionalInfo(0, addressee, needWhois);
            nickRoot->setText(nlvcAdditionalInfo, nickAdditionalInfo);
            // Set Kabc icon if the nick is associated with an addressbook entry.
            if (!addressee.isEmpty())
                nickRoot->setPixmap(nlvcKabc, m_tdeabcIconSet.pixmap(
                    TQIconSet::Small, TQIconSet::Normal, TQIconSet::On));
            else
                nickRoot->setPixmap(nlvcKabc, m_tdeabcIconSet.pixmap(
                    TQIconSet::Small, TQIconSet::Disabled, TQIconSet::Off));
        }
    }
    // Erase nicks no longer being watched.
    TQListViewItem* item = networkRoot->firstChild();
    while (item)
    {
        TQListViewItem* nextItem = item->nextSibling();
        if (static_cast<NicksOnlineItem*>(item)->type() != NicksOnlineItem::OfflineItem)
        {
            TQString nickname = item->text(nlvcNick);
            if ((watchList.find(nickname) == watchList.end()) &&
                (serverName == item->text(nlvcServerName))) delete item;
        }
        item = nextItem;
    }
    item = offlineRoot->firstChild();

    if(item) {
        while (item)
        {
            TQListViewItem* nextItem = item->nextSibling();
            TQString nickname = item->text(nlvcNick);
            if ((watchList.find(nickname) == watchList.end()) &&
                (serverName == item->text(nlvcServerName))) delete item;
            item = nextItem;
        }
    }
    else
    {
        delete offlineRoot;
    }
    // Expand server if newly added to list.
    if (newNetworkRoot)
    {
        networkRoot->setOpen(true);
        // Connect server NickInfo updates.
        connect (servr, TQ_SIGNAL(nickInfoChanged(Server*, const NickInfoPtr)),
            this, TQ_SLOT(slotNickInfoChanged(Server*, const NickInfoPtr)));
    }
}

/**
 * Determines if a nick is online in any of the servers in a network and returns
 * a NickInfo if found, otherwise 0.
 * @param networkName        Server network name.
 * @param nickname           Nick name.
 * @return                   NickInfo if nick is online in any server, otherwise 0.
 */
NickInfoPtr NicksOnline::getOnlineNickInfo(TQString& networkName, TQString& nickname)
{
    // Get list of pointers to all servers.
    KonversationApplication* konvApp = static_cast<KonversationApplication*>(kapp);
    TQPtrList<Server> serverList = konvApp->getConnectionManager()->getServerList();
    for (Server* server = serverList.first(); server; server = serverList.next())
    {
        if (server->getDisplayName() == networkName)
        {
            NickInfoPtr nickInfo = server->getNickInfo(nickname);
            if (nickInfo) return nickInfo;
        }
    }
    return 0;
}

/**
 * Requests a WHOIS for a specified server network and nickname.
 * The request is sent to the first server found in the network.
 * @param groupName          Server group name.
 * @param nickname           Nick name.
 */
void NicksOnline::requestWhois(TQString& networkName, TQString& nickname)
{
    KonversationApplication* konvApp = static_cast<KonversationApplication*>(kapp);
    TQPtrList<Server> serverList = konvApp->getConnectionManager()->getServerList();
    for (Server* server = serverList.first(); server; server = serverList.next())
    {
        if (server->getDisplayName() == networkName)
        {
            server->requestWhois(nickname);
            return;
        }
    }
}

/**
 * Refresh the nicklistview for all servers.
 */
void NicksOnline::refreshAllServerOnlineLists()
{
    KonversationApplication* konvApp = static_cast<KonversationApplication*>(kapp);
    TQPtrList<Server> serverList = konvApp->getConnectionManager()->getServerList();
    Server* server;
    // Remove servers no longer connected.
    TQListViewItem* child = m_nickListView->firstChild();
    while (child)
    {
        TQListViewItem* nextChild = child->nextSibling();
        TQString networkName = child->text(nlvcNetwork);
        TQStringList serverNameList = TQStringList::split(",", child->text(nlvcAdditionalInfo));
        TQStringList::Iterator itEnd = serverNameList.end();
        TQStringList::Iterator it = serverNameList.begin();
        while (it != itEnd)
        {
            TQString serverName = *it;
            // Locate server in server list.
            bool found = false;
            for (server = serverList.first(); server; server = serverList.next())
            {
                if ((server->getServerName() == serverName) &&
                    (server->getDisplayName() == networkName)) found = true;
            }
            if (!found)
                it = serverNameList.remove(it);
            else
                ++it;
        }
        // Remove Networks with no servers connected, otherwise update list of connected
        // servers.
        if (serverNameList.empty())
            delete child;
        else
            child->setText(nlvcAdditionalInfo, serverNameList.join(","));
        child = nextChild;
    }
    // Display info for all currently-connected servers.
    for (server = serverList.first(); server; server = serverList.next())
    {
        updateServerOnlineList(server);
    }
    // Refresh addressbook buttons.
    slotNickListView_SelectionChanged();
}

void NicksOnline::timerFired()
{
    // Allow one WHOIS request per cycle.
    m_whoisRequested = false;
    refreshAllServerOnlineLists();
}

/**
 * When a user double-clicks a nickname in the nicklistview, let server know so that
 * it can perform the user's chosen default action for that.
 */
void NicksOnline::processDoubleClick(TQListViewItem* item)
{
    // Only emit signal when the user double clicked a nickname rather than
    // a server name or channel name.
    TQString serverName;
    TQString nickname;
    if (getItemServerAndNick(item, serverName, nickname))
        emit doubleClicked(serverName, nickname);
}

/**
 * Returns the server name and nickname of the specified nicklistview item.
 * @param item              The nicklistview item.
 * @return serverName       Name of the server for the nick at the item, or Null if not a nick.
 * @return nickname         The nickname at the item.
 */
bool NicksOnline::getItemServerAndNick(const TQListViewItem* item, TQString& serverName, TQString& nickname)
{
    if (!item) return false;
    // convert into NicksOnlineItem
    const NicksOnlineItem* nlItem=static_cast<const NicksOnlineItem*>(item);
    // If on a network, return false;
    if (nlItem->type() == NicksOnlineItem::NetworkRootItem) return false;
    // get server name
    serverName = item->text(nlvcServerName);
    // If on a channel, move up to the nickname.
    if (nlItem->type() == NicksOnlineItem::ChannelItem)
    {
        item = item->parent();
        serverName = item->text(nlvcServerName);
    }
    nickname = item->text(nlvcNick);
    // offline columns are not nick names
    if (nlItem->type() == NicksOnlineItem::OfflineItem) return false;
    return true;
}

NickInfoPtr NicksOnline::getNickInfo(const TQListViewItem* item)
{
    TQString serverName;
    TQString nickname;

    getItemServerAndNick(item, serverName, nickname);

    if (!serverName || !nickname)
        return 0;

    Server* server = KonversationApplication::instance()->getConnectionManager()->getServerByName(serverName);

    if (server) return server->getNickInfo(nickname);

    return 0;
}

/**
 * Given a server name and nickname, returns the item in the Nick List View displaying
 * the nick.
 * @param serverName        Name of server.
 * @param nickname          Nick name.
 * @return                  Pointer to TQListViewItem displaying the nick, or 0 if not found.
 *
 * @see getItemServerAndNick
 */
TQListViewItem* NicksOnline::getServerAndNickItem(const TQString& serverName,
const TQString& nickname)
{
    Server* server = KonversationApplication::instance()->getConnectionManager()->getServerByName(serverName);
    if (!server) return 0;
    TQString networkName = server->getDisplayName();
    TQListViewItem* networkRoot = m_nickListView->findItem(networkName, nlvcNetwork);
    if (!networkRoot) return 0;
    TQListViewItem* nickRoot = findItemChild(networkRoot, nickname, NicksOnlineItem::NicknameItem);
    return nickRoot;
}

/**
 * Perform an addressbook command (edit contact, create new contact,
 * change/delete association.)
 * @param id                The command id.  @ref CommandIDs.
 *
 * The operation is performed on the nickname at the currently-selected item in
 * the nicklistview.
 *
 * Also refreshes the nicklistview display to reflect the new addressbook state
 * for the nick.
 */
void NicksOnline::doCommand(int id)
{
    if(id < 0)
    {
        return;
    }

    TQString serverName;
    TQString nickname;
    TQListViewItem* item = m_nickListView->selectedItem();

    if(!getItemServerAndNick(item, serverName, nickname))
    {
        return;
    }

    // Get the server object corresponding to the server name.
    Server* server = KonversationApplication::instance()->getConnectionManager()->getServerByName(serverName);

    if (!server) return;

    // Get NickInfo object corresponding to the nickname.
    NickInfoPtr nickInfo = server->getNickInfo(nickname);
    // Get addressbook entry for the nick.
    TDEABC::Addressee addressee;

    if(nickInfo)
    {
        addressee = nickInfo->getAddressee();
    }
    else
    {
        addressee = server->getOfflineNickAddressee(nickname);
    }

    switch(id)
    {
        case ciSendEmail:
            Konversation::Addressbook::self()->sendEmail(addressee);
            return;                               //no need to refresh item
        case ciAddressbookEdit:
            Konversation::Addressbook::self()->editAddressee(addressee.uid());
            return;                               //no need to refresh item - nickinfo changed will be called anyway.
        case ciAddressbookChange:
            if(nickInfo)
            {
                nickInfo->showLinkAddressbookUI();
            }
            else
            {
                LinkAddressbookUI *linkaddressbookui = new LinkAddressbookUI(server->getViewContainer()->getWindow(), NULL, nickname, server->getServerName(), server->getDisplayName(), addressee.realName());
                linkaddressbookui->show();
            }
            break;
        case ciAddressbookNew:
        case ciAddressbookDelete:
        {
            Konversation::Addressbook *addressbook = Konversation::Addressbook::self();

            if(addressbook && addressbook->getAndCheckTicket())
            {
                if(id == ciAddressbookDelete)
                {
                    if (addressee.isEmpty())
                    {
                        return;
                    }

                    addressbook->unassociateNick(addressee, nickname, server->getServerName(), server->getDisplayName());
                }
                else
                {
                    addressee.setGivenName(nickname);
                    addressee.setNickName(nickname);
                    addressbook->associateNickAndUnassociateFromEveryoneElse(addressee, nickname, server->getServerName(), server->getDisplayName());
                }
                if(addressbook->saveTicket())
                {
                    //saveTicket will refresh the addressees for us.
                    if(id == ciAddressbookNew)
                    {
                        Konversation::Addressbook::self()->editAddressee(addressee.uid());
                    }
                }
            }
            break;
        }
        case ciJoinChannel:
        {
            // only join real channels
            if (static_cast<NicksOnlineItem*>(m_nickListView->selectedItem())->type() == NicksOnlineItem::ChannelItem)
            {
                TQString contactChannel = m_nickListView->selectedItem()->text(nlvcChannel);
                server->queue( "JOIN "+contactChannel );
            }
            break;
        }
        case ciWhois:
            server->queue("WHOIS "+nickname);
            return;
        case ciOpenQuery:
            NickInfoPtr nickInfo = server->obtainNickInfo(nickname);
            class Query* query = server->addQuery(nickInfo, true /*we initiated*/);
            emit showView(query);
            return;
    }

    refreshItem(item);
}

/**
 * Get the addressbook state of the nickname at the specified nicklistview item.
 * @param item              Item of the nicklistview.
 * @return                  Addressbook state.
 * 0 = not a nick, 1 = nick has no addressbook association, 2 = nick has association
 */
int NicksOnline::getNickAddressbookState(TQListViewItem* item)
{
    int nickState = nsNotANick;
    TQString serverName;
    TQString nickname;
    if (getItemServerAndNick(item, serverName, nickname))
    {
        Server *server = KonversationApplication::instance()->getConnectionManager()->getServerByName(serverName);
        if (!server) return nsNotANick;
        NickInfoPtr nickInfo = server->getNickInfo(nickname);
        if (nickInfo)
        {
            if (nickInfo->getAddressee().isEmpty())
                nickState = nsNoAddress;
            else
                nickState = nsHasAddress;
        }
        else
        {
            if (server->getOfflineNickAddressee(nickname).isEmpty())
                nickState = nsNoAddress;
            else
                nickState = nsHasAddress;
        }
    }
    return nickState;
}

/**
 * Sets the enabled/disabled state and labels of the addressbook buttons
 * based on the given nick addressbook state.
 * @param nickState         The state of the nick. 1 = not associated with addressbook,
 *                          2 = associated with addressbook.  @ref getNickAddressbookState.
 */
void NicksOnline::setupAddressbookButtons(int nickState)
{
    switch (nickState)
    {
        case nsNotANick:
        {
            m_editContactButton->setEnabled(false);
            m_changeAssociationButton->setEnabled(false);
            m_deleteAssociationButton->setEnabled(false);
            break;
        }
        case nsNoAddress:
        {
            m_editContactButton->setText(i18n("Create New C&ontact..."));
            m_editContactButton->setEnabled(true);
            m_changeAssociationButton->setText(i18n("&Choose Association..."));
            m_changeAssociationButton->setEnabled(true);
            m_deleteAssociationButton->setEnabled(false);
            break;
        }
        case nsHasAddress:
        {
            m_editContactButton->setText(i18n("Edit C&ontact..."));
            m_editContactButton->setEnabled(true);
            m_changeAssociationButton->setText(i18n("&Change Association..."));
            m_changeAssociationButton->setEnabled(true);
            m_deleteAssociationButton->setEnabled(true);
            break;
        }
    }
}

/**
 * Received when user clicks the Edit Contact (or New Contact) button.
 */
void NicksOnline::slotEditContactButton_Clicked()
{
    switch (getNickAddressbookState(m_nickListView->selectedItem()))
    {
        case nsNotANick:    break;
        case nsNoAddress:   { doCommand(ciAddressbookNew); break; }
        case nsHasAddress:  { doCommand(ciAddressbookEdit); break; }
    }
}

/**
 * Received when user clicks the Change Association button.
 */
void NicksOnline::slotChangeAssociationButton_Clicked() { doCommand(ciAddressbookChange); }
/**
 * Received when user clicks the Delete Association button.
 */
void NicksOnline::slotDeleteAssociationButton_Clicked() { doCommand(ciAddressbookDelete); }
/**
 * Received when user selects a different item in the nicklistview.
 */
void NicksOnline::slotNickListView_SelectionChanged()
{
    TQListViewItem* item = m_nickListView->selectedItem();
    int nickState = getNickAddressbookState(item);
    setupAddressbookButtons(nickState);
}

/**
 * Received when right-clicking an item in the NickListView.
 */
void NicksOnline::slotNickListView_RightButtonClicked(TQListViewItem* item, const TQPoint& pt)
{
    if (!item) return;
    m_popupMenu->clear();
    int nickState = getNickAddressbookState(item);
    switch (nickState)
    {
        case nsNotANick:
        {
            break;
        }
        case nsNoAddress:
        {
            m_popupMenu->insertItem(i18n("&Choose Association..."), ciAddressbookChange);
            m_popupMenu->insertItem(i18n("Create New C&ontact..."), ciAddressbookNew);
            m_popupMenu->insertSeparator();
            m_popupMenu->insertItem(i18n("&Whois"), ciWhois);
            m_popupMenu->insertItem(i18n("Open &Query"), ciOpenQuery);
            if (item->text(nlvcServerName).isEmpty())
                m_popupMenu->insertItem(i18n("&Join Channel"), ciJoinChannel);
            break;
        }
        case nsHasAddress:
        {
            m_popupMenu->insertItem(SmallIcon("mail_generic"), i18n("&Send Email..."), ciSendEmail);
            m_popupMenu->insertSeparator();
            m_popupMenu->insertItem(SmallIcon("contents"), i18n("Edit C&ontact..."), ciAddressbookEdit);
            m_popupMenu->insertSeparator();
            m_popupMenu->insertItem(i18n("&Change Association..."), ciAddressbookChange);
            m_popupMenu->insertItem(SmallIconSet("edit-delete"), i18n("&Delete Association"), ciAddressbookDelete);
            m_popupMenu->insertSeparator();
            m_popupMenu->insertItem(i18n("&Whois"), ciWhois);
            m_popupMenu->insertItem(i18n("Open &Query"), ciOpenQuery);
            if (item->text(nlvcServerName).isEmpty())
                m_popupMenu->insertItem(i18n("&Join Channel"), ciJoinChannel);
            break;
        }
    }
    if (nickState != nsNotANick)
        m_popupMenu->popup(pt);
}

/**
 * Received from popup menu when user chooses something.
 */
void NicksOnline::slotPopupMenu_Activated(int id)
{
    doCommand(id);
}

/**
 * Received from server when a NickInfo changes its information.
 */
void NicksOnline::slotNickInfoChanged(Server* server, const NickInfoPtr nickInfo)
{
    if (!nickInfo) return;
    TQString nickname = nickInfo->getNickname();

    if (!server) return;
    TQString serverName = server->getServerName();
    TQListViewItem* item = getServerAndNickItem(serverName, nickname);
    refreshItem(item);
}

/**
 * Refreshes the information for the given item in the list.
 * @param item               Pointer to listview item.
 */
void NicksOnline::refreshItem(TQListViewItem* item)
{
    if (!item) return;
    TQString serverName;
    TQString nickname;
    if (getItemServerAndNick(item, serverName, nickname))
    {
        Server *server = KonversationApplication::instance()->getConnectionManager()->getServerByName(serverName);
        if (server)
        {
            NickInfoPtr nickInfo = server->getNickInfo(nickname);
            TDEABC::Addressee addressee;
            if (nickInfo)
                addressee = nickInfo->getAddressee();
            else
                addressee = server->getOfflineNickAddressee(nickname);
            int nickState = 2;
            if (addressee.isEmpty()) nickState = 1;
            switch (nickState)
            {
                case nsNotANick:
                    break;
                case nsNoAddress:
                {
                    item->setPixmap(nlvcKabc, m_tdeabcIconSet.pixmap(
                        TQIconSet::Small, TQIconSet::Disabled, TQIconSet::Off)); break;
                }
                case nsHasAddress:
                {
                    item->setPixmap(nlvcKabc, m_tdeabcIconSet.pixmap(
                        TQIconSet::Small, TQIconSet::Normal, TQIconSet::On)); break;
                }
            }
            TQString nickAdditionalInfo;
            bool needWhois = false;
            if (nickInfo) nickAdditionalInfo = getNickAdditionalInfo(nickInfo, addressee,
                    needWhois);
            item->setText(nlvcAdditionalInfo, nickAdditionalInfo);
            if (item == m_nickListView->selectedItem()) setupAddressbookButtons(nickState);
        }
    }
}

void NicksOnline::childAdjustFocus() {}

#include "nicksonline.moc"
