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

/*
  serverison.h  -  Class to give a list of all the nicks known to the
                   addressbook and watchednick list that are on this
           server.  There is one instance of this class for
           each Server object.
  begin:     Fri Sep 03 2004
  copyright: (C) 2004 by John Tapsell
  email:     john@geola.co.uk
*/

#include "serverison.h"
#include "server.h"
#include "addressbook.h"
#include "konversationapplication.h"
#include "nickinfo.h"
#include "viewcontainer.h"

#include <tqmap.h>
#include <tqstring.h>
#include <tqstringlist.h>
#include <tdeabc/addressbook.h>
#include <tdeabc/stdaddressbook.h>


ServerISON::ServerISON(Server* server) : m_server(server)
{
    m_ISONList_invalid = true;
    //We need to know when the addressbook changes because if the info for an offline nick changes,
    //we won't get a nickInfoChanged signal.
    connect( Konversation::Addressbook::self()->getAddressBook(), TQ_SIGNAL( addressBookChanged( AddressBook * ) ),
        this, TQ_SLOT( addressbookChanged() ) );
    connect( Konversation::Addressbook::self(), TQ_SIGNAL(addresseesChanged()),
        this, TQ_SLOT(addressbookChanged()));
    connect( m_server, TQ_SIGNAL(nickInfoChanged(Server*, const NickInfoPtr)),
        this, TQ_SLOT(nickInfoChanged(Server*, const NickInfoPtr)));
    connect( m_server,
        TQ_SIGNAL(channelMembersChanged(Server*, const TQString&, bool, bool, const TQString& )),
        this,
        TQ_SLOT(slotChannelMembersChanged(Server*, const TQString&, bool, bool, const TQString& )));
    connect( m_server,
        TQ_SIGNAL(channelJoinedOrUnjoined(Server*, const TQString&, bool )),
        this,
        TQ_SLOT(slotChannelJoinedOrUnjoined(Server*, const TQString&, bool )));
    connect(KonversationApplication::instance(), TQ_SIGNAL(serverGroupsChanged(const Konversation::ServerGroupSettings*)),
        this, TQ_SLOT(slotServerGroupsChanged()));
}

TQStringList ServerISON::getWatchList()
{
    if(m_ISONList_invalid)
        recalculateAddressees();
    return m_watchList;
}

TQStringList ServerISON::getISONList()
{
    if(m_ISONList_invalid)
        recalculateAddressees();
    return m_ISONList;
}

TQStringList ServerISON::getAddressees()
{
    if(m_ISONList_invalid)
        recalculateAddressees();
    return m_addresseesISON;
}

TDEABC::Addressee ServerISON::getOfflineNickAddressee(TQString& nickname)
{
    TQString lcNickname = nickname.lower();
    if(m_ISONList_invalid)
        recalculateAddressees();
    if (m_offlineNickToAddresseeMap.contains(lcNickname))
        return m_offlineNickToAddresseeMap[lcNickname];
    else
        return TDEABC::Addressee();
}

void ServerISON::recalculateAddressees()
{
    // If not watching nicks, no need to build notify list.
    if (Preferences::useNotify())
    {
        // Get all nicks known to be online.
        const NickInfoMap* allNicks = m_server->getAllNicks();
        // Build a map of online nicknames with associated addressbook entry,
        // indexed by TDEABC::Addressee uid.
        // Note that there can be more than one nick associated with an addressee.
        TQMap<TQString,TQStringList> addresseeToOnlineNickMap;
        NickInfoMap::ConstIterator nickInfoItEnd = allNicks->constEnd();
        for(NickInfoMap::ConstIterator nickInfoIt=allNicks->constBegin();
            nickInfoIt != nickInfoItEnd; ++nickInfoIt)
        {
            NickInfoPtr nickInfo = nickInfoIt.data();
            TDEABC::Addressee addressee = nickInfo->getAddressee();
            if (!addressee.isEmpty())
            {
                TQString uid = addressee.uid();
                TQStringList nicknames = addresseeToOnlineNickMap[uid];
                nicknames.append(nickInfo->getNickname());
                addresseeToOnlineNickMap[uid] = nicknames;
            }
        }

        // Lowercase server name and server group.
        TQString lserverName = m_server->getServerName().lower();
        TQString lserverGroup = m_server->getDisplayName().lower();

        // Build notify list from nicks in addressbook, eliminating dups (case insensitive).
        TQMap<TQString,TQString> ISONMap;
        m_offlineNickToAddresseeMap.clear();
        for( TDEABC::AddressBook::ConstIterator it =
            Konversation::Addressbook::self()->getAddressBook()->begin();
            it != Konversation::Addressbook::self()->getAddressBook()->end(); ++it )
        {
            if(Konversation::Addressbook::self()->hasAnyNicks(*it))
            {
                TQString uid = (*it).uid();
                // First check if we already know that this addressee is online.
                // If so, add all the nicks of the addressee that are online, but do not
                // add the offline nicks.  There is no point in monitoring such nicks.
                if (addresseeToOnlineNickMap.contains(uid))
                {
                    TQStringList nicknames = addresseeToOnlineNickMap[uid];
                    TQStringList::iterator itEnd = nicknames.end();

                    for(TQStringList::iterator it = nicknames.begin(); it != itEnd; ++it)
                    {
                        ISONMap.insert((*it).lower(), (*it), true);
                    }
                }
                else
                {
                    // If addressee is not known to be online, add all of the nicknames
                    // of the addressee associated with this server or server group (if any)
                    // to the notify list.
                    // Simultaneously, build a map of all offline nicks and corresponding
                    // TDEABC::Addressee, indexed by lowercase nickname.
                    TQStringList nicks = TQStringList::split( TQChar( 0xE000 ),
                        (*it).custom("messaging/irc", "All") );
                    TQStringList::ConstIterator nicksItEnd = nicks.constEnd();
                    for( TQStringList::ConstIterator nicksIt = nicks.constBegin();
                        nicksIt != nicksItEnd; ++nicksIt )
                    {
                        TQString lserverOrGroup = (*nicksIt).section(TQChar(0xE120),1).lower();
                        if(lserverOrGroup == lserverName || lserverOrGroup == lserverGroup ||
                            lserverOrGroup.isEmpty())
                        {
                            TQString nickname = (*nicksIt).section(TQChar(0xE120),0,0);
                            TQString lcNickname = nickname.lower();
                            ISONMap.insert(lcNickname, nickname, true);
                            m_offlineNickToAddresseeMap.insert(lcNickname, *it, true);
                        }
                    }
                }
            }
        }
        // The part of the ISON list due to the addressbook.
        m_addresseesISON = ISONMap.values();
        // Merge with watch list from prefs, eliminating dups (case insensitive).
        // TODO: Don't add nick on user watch list if nick is known to be online
        // under a different nickname?
        TQStringList prefsWatchList =
            Preferences::notifyListByGroupName(m_server->getDisplayName());
        TQStringList::iterator itEnd = prefsWatchList.end();

        for(TQStringList::iterator it = prefsWatchList.begin(); it != itEnd; ++it)
        {
            ISONMap.insert((*it).lower(), (*it), true);
        }

        // Build final watch list.
        m_watchList = ISONMap.values();
        // Eliminate nicks that are online in a joined channel, since there is no point
        // in doing an ISON on such nicks.
        m_ISONList.clear();
        itEnd = m_watchList.end();

        for(TQStringList::iterator it = m_watchList.begin(); it != itEnd; ++it)
        {
            if (m_server->getNickJoinedChannels(*it).isEmpty())
            {
                m_ISONList.append(*it);
            }
        }
    }
    else
    {
        m_addresseesISON.clear();
        m_ISONList.clear();
    }

    m_ISONList_invalid = false;
}

// When user changes preferences and has nick watching turned on, rebuild notify list.
void ServerISON::slotServerGroupsChanged()
{
    kdDebug() << "ServerISON::slotServerGroupsChanged" << endl;
    m_ISONList_invalid = true;
}

void ServerISON::nickInfoChanged(Server* /*server*/, const NickInfoPtr /*nickInfo*/) {
//We need to call recalculateAddressees before returning m_ISONList

//Maybe we could do something like:
//if(m_ISONList.contains(nickInfo->getNickName())) return;
m_ISONList_invalid = true;
}

void ServerISON::addressbookChanged()
{
    //We need to call recalculateAddressees before returning m_ISONList
    m_ISONList_invalid = true;
}

void ServerISON::slotChannelMembersChanged(Server* /*server*/, const TQString& /*channelName*/,
bool joined, bool parted, const TQString& nickname)
{
    // Whenever a nick on the watch list leaves the last joined channel, must recalculate lists.
    // The nick will be added to the ISON list.
    if (joined && parted && m_watchList.contains(nickname))
        if (m_server->getNickJoinedChannels(nickname).isEmpty()) m_ISONList_invalid = true;
}

void ServerISON::slotChannelJoinedOrUnjoined(Server* /*server*/,
const TQString& /*channelName*/, bool /*joined*/)
{
    // If user left or joined a channel, need to recalculate lists, since watched nicks
    // may need to be moved from/to ISON list.
    m_ISONList_invalid = true;
}

#include "serverison.moc"
