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

/*
  addressbook.cpp  - This class gives function that interact with kaddressbook.
  begin:     Fri 2004-07-23
  copyright: (C) 2004 by John Tapsell
  email:     john@geola.co.uk
*/

#include "addressbook.h"
#include "../server.h"
#include "../konversationapplication.h"
#include "../konversationmainwindow.h"
#include "../channel.h"

#include <tqstringlist.h>

#include <tdelocale.h>
#include <kstringhandler.h>
#include <krun.h>
#include <tdeapplication.h>
#include <dcopclient.h>
#include <tdemessagebox.h>

namespace Konversation
{

    AddressbookBase::AddressbookBase()
    {
        TDEABC::StdAddressBook::setAutomaticSave( false );
        m_ticket=NULL;
    }

    AddressbookBase::~AddressbookBase()
    {
    }

    TDEABC::AddressBook *AddressbookBase::getAddressBook() { return addressBook; }

    TDEABC::Addressee AddressbookBase::getKABCAddresseeFromNick(const TQString &ircnick, const TQString &servername, const TQString &servergroup)
    {
        TDEABC::AddressBook::Iterator it;

        for( it = addressBook->begin(); it != addressBook->end(); ++it )
        {
            if(hasNick(*it, ircnick, servername, servergroup))
                return (*it);
        }
        return TDEABC::Addressee();
    }
    TDEABC::Addressee AddressbookBase::getKABCAddresseeFromNick(const TQString &nick_server)
    {
        TDEABC::AddressBook::Iterator it;

        for( it = addressBook->begin(); it != addressBook->end(); ++it )
        {
            if(hasNick(*it, nick_server))
                return (*it);
        }
        return TDEABC::Addressee();
    }

    bool AddressbookBase::hasNick(const TDEABC::Addressee &addressee, const TQString &ircnick, const TQString &servername, const TQString &servergroup)
    {

        TQString lnick = ircnick.lower();
        TQString lnick_servername;
        TQString lnick_servergroup;
        if(!servername.isEmpty())
            lnick_servername = lnick + TQChar(0xE120) + servername.lower();
        if(!servergroup.isEmpty())
            lnick_servergroup = lnick + TQChar(0xE120) + servergroup.lower();

        TQString lit;
        TQStringList addresses = TQStringList::split( TQChar( 0xE000 ), addressee.custom("messaging/irc", "All") );
        TQStringList::iterator end = addresses.end();
        for ( TQStringList::iterator it = addresses.begin(); it != end; ++it )
        {
            lit = (*it).lower();
            if(lit == lnick || lit == lnick_servername || lit == lnick_servergroup)
                return true;
        }
        return false;

    }

    TQStringList AddressbookBase::allContactsNicksForServer(const TQString &servername, const TQString &servergroup)
    {
        TQStringList contacts;
        for( TDEABC::AddressBook::Iterator it = addressBook->begin(); it != addressBook->end(); ++it )
            contacts += getNicks(*it, servername, servergroup);
        return contacts;

    }

    TQStringList AddressbookBase::getNicks(const TDEABC::Addressee &addressee, const TQString &servername, const TQString &servergroup)
    {
        TQStringList nicks;

        TQString lservername = servername.lower();
        TQString lservergroup = servergroup.lower();

        TQStringList addresses = TQStringList::split( TQChar( 0xE000 ), addressee.custom("messaging/irc", "All") );
        TQStringList::iterator end = addresses.end();
        for ( TQStringList::iterator it = addresses.begin(); it != end; ++it )
        {
            if(!(*it).contains(TQChar( 0xE120)))
                nicks.append(*it);
            else
            {
                TQString it_server = (*it).section(TQChar( 0xE120), 0,0).lower();
                if(it_server == lservername || it_server == lservergroup)
                    nicks.append((*it).section(TQChar( 0xE120 ), 1,1));
            }
        }
        return nicks;
    }

    bool AddressbookBase::hasNick(const TDEABC::Addressee &addressee, const TQString &nick_server)
    {
        TQString lnick_server = nick_server.lower();
        TQStringList addresses = TQStringList::split( TQChar( 0xE000 ), addressee.custom("messaging/irc", "All") );
        TQStringList::iterator end = addresses.end();
        for ( TQStringList::iterator it = addresses.begin(); it != end; ++it )
        {
            TQString it_server = (*it).section(TQChar( 0xE120), 0,0).lower();
            if(it_server ==lnick_server)
                return true;
        }
        return false;

    }

    TQString AddressbookBase::getBestNick(const TDEABC::Addressee &addressee)
    {
        //Look for a nickinfo for this nick, and use that.  That way we turn a nick that is online.
        NickInfoPtr nickInfo = getNickInfo(addressee);
        if(nickInfo)
            return nickInfo->getNickname();
        //No online nickinfo - not connected to server maybe.  just return the first nick.

        TQStringList addresses = TQStringList::split( TQChar( 0xE000 ), addressee.custom("messaging/irc", "All") );
        if(!addresses.empty())
            return addresses.first();
        //No irc nicks- nothing left to try - return null
        return TQString();
    }

    NickInfoPtr AddressbookBase::getNickInfo(const TDEABC::Addressee &addressee)
    {
        NickInfoPtr lastNickInfo;
        TQStringList addresses = TQStringList::split( TQChar( 0xE000 ), addressee.custom("messaging/irc", "All") );
        TQStringList::iterator end = addresses.end();
        for ( TQStringList::iterator it = addresses.begin(); it != end; ++it )
        {
            TQString ircnick;
            TQString serverOrGroup;
            KonversationApplication::splitNick_Server(*it, ircnick, serverOrGroup);
            NickInfoPtr nickInfo =
                dynamic_cast<KonversationApplication*>(kapp)->getNickInfo(ircnick, serverOrGroup);
            if(nickInfo)
            {
                if(!nickInfo->isAway())
                    return nickInfo;
                //This nick is away.  Keep looking, but use it if we can't find one that's on
                lastNickInfo = nickInfo;
            }
        }
        //Use a nick that's either away, or non-existant.
        return lastNickInfo;
    }

    bool AddressbookBase::hasAnyNicks(const TDEABC::Addressee &addressee)
    {
        return !addressee.custom("messaging/irc", "All").isEmpty();
    }
    void AddressbookBase::unassociateNick(TDEABC::Addressee &addressee, const TQString &ircnick, const TQString &servername, const TQString &servergroup)
    {

        kdDebug() << "in unassociatenick for '" << ircnick << endl;
        if(ircnick.isEmpty()) return;

        TQString lnick = ircnick.lower();
        TQString lnick_servername;
        TQString lnick_servergroup;
        if(!servername.isEmpty())
            lnick_servername = lnick + TQChar(0xE120) + servername.lower();
        if(!servergroup.isEmpty())
            lnick_servergroup = lnick + TQChar(0xE120) + servergroup.lower();

        //We should now have lnick = ircnick, and versions with servername and servergroup -
        // like johnflux, johnflux@freenode, or johnflux@irc.kde.org    except with the unicode
        // separator char 0xe120 instead of the @

        kdDebug() << "nick" << ircnick<< endl;
        bool changed = false;
        if(addressee.isEmpty())
        {
            kdDebug() << "Ignoring unassociation command for empty addressee for nick " << ircnick << endl;
        }
        TQString lit;
        TQStringList addresses = TQStringList::split( TQChar( 0xE000 ), addressee.custom("messaging/irc", "All") );
        TQStringList::iterator it = addresses.begin();
        while(it != addresses.end())
        {
            lit = (*it).lower();
            if(lit == lnick || lit == lnick_servername || lit == lnick_servergroup)
            {
                changed = true;
                it = addresses.remove(it);
            }
            else
            {
                it++;
            }
        }
        if(!changed)
            return;

        //if(!getAndCheckTicket())
        //	return;
        TQString new_custom = addresses.join( TQChar( 0xE000 ));
        if(new_custom.isEmpty())
            addressee.removeCustom("messaging/irc", "All");
        else
            addressee.insertCustom("messaging/irc", "All", new_custom);

        addressBook->insertAddressee(addressee);
        //saveTicket();
    }
    void AddressbookBase::focusAndShowErrorMessage(const TQString &errorMsg)
    {
        static_cast<KonversationApplication *>(kapp)->getMainWindow()->focusAndShowErrorMessage(errorMsg);
    }
    /**For a given contact, adds the ircnick if they don't already have it.  If you pass an addressBook, the contact is inserted
     * if it has changed. */
    void AddressbookBase::associateNick(TDEABC::Addressee &addressee, const TQString &ircnick, const TQString &servername, const TQString &servergroup)
    {
        //It's easiest to just remove it from the list if it's there already
        unassociateNick(addressee, ircnick, servername, servergroup);
        TQString nick_server = ircnick;
        if(!servergroup.isEmpty())
            nick_server += TQChar(0xE120) + servergroup;
        else if(!servername.isEmpty())
            nick_server += TQChar(0xE120) + servername;
        TQStringList addresses = TQStringList::split( TQChar( 0xE000 ), addressee.custom("messaging/irc", "All") );
        //For sanity reasons, don't let the number of irc nicks go above 10.
        //To do this, just remove the irc nick at the end of the list.
        if(addresses.count() >= 10)
            addresses.pop_back();
        addresses.append(nick_server);
        addressee.insertCustom("messaging/irc", "All", addresses.join( TQChar( 0xE000 )));

        addressBook->insertAddressee(addressee);
    }
    /** This function associates the nick for a person, then iterates over all the contacts unassociating the nick from everyone else. It saves the addressses that have changed.*/
    bool AddressbookBase::associateNickAndUnassociateFromEveryoneElse(TDEABC::Addressee &addressee, const TQString &ircnick, const TQString &servername, const TQString &servergroup)
    {
        for( TDEABC::AddressBook::Iterator it = addressBook->begin(); it != addressBook->end(); ++it )
            if((*it).uid() != addressee.uid())
                unassociateNick(*it, ircnick, servername, servergroup);
        associateNick(addressee, ircnick, servername, servergroup);
        return true;;
    }

    bool AddressbookBase::getAndCheckTicket()
    {
        if(m_ticket)
        {
            kdError() << "Internal error - getting new ticket without saving old" << endl;
            return false;
        }
        m_ticket = addressBook->requestSaveTicket();
        if ( !m_ticket )
        {
            kdError() << "Resource is locked by other application!" << endl;
            //emit error
            return false;
        }
        kdDebug() << "gotTicketSuccessfully" << endl;
        return true;
    }
    void AddressbookBase::releaseTicket()
    {
        addressBook->releaseSaveTicket(m_ticket);
        m_ticket = NULL;
    }

    bool AddressbookBase::saveTicket()
    {
        if(!addressBook->save( m_ticket) )
        {
            kdError() << "Saving failed!" << endl;
            addressBook->releaseSaveTicket(m_ticket);
            m_ticket = NULL;
            return false;
        }
        kdDebug() << "saveTicket() was successful" << endl;
        m_ticket= NULL;
        emit addresseesChanged();
        return true;
    }

    bool AddressbookBase::saveAddressbook()
    {
        if(m_ticket)
        {
            kdError() << "Internal error - getting new ticket without saving old" << endl;
            return false;
        }
        m_ticket = addressBook->requestSaveTicket();
        if ( !m_ticket )
        {
            kdError() << "Resource is locked by other application!" << endl;
            return false;
        }
        else
        {
            if ( !addressBook->save( m_ticket ) )
            {
                kdError() << "Saving failed!" << endl;
                addressBook->releaseSaveTicket(m_ticket);
                m_ticket = NULL;
                return false;
            }
        }
        kdDebug() << "Save was successful" << endl;
        m_ticket = NULL;
        emit addresseesChanged();
        return true;
    }

    bool AddressbookBase::saveAddressee(TDEABC::Addressee &addressee)
    {
        Q_ASSERT(&addressee);
        addressBook->insertAddressee(addressee);
        bool success = saveAddressbook();
        if(success)
            emitContactPresenceChanged(addressee.uid(), presenceStatusByAddressee(addressee));
        return success;
    }

    /**
     * Indicate the presence as a number.  Checks all the nicks that that person has.
     * If we find them, return 4 (online).
     * If we are connected to any of the servers that they are on, and we don't find them, return 1 (offline)
     * If we find them, but they are set to away then return 3 (away)
     * If we are connected to none of the servers that they are on, return 0 (unknown)
     * @param addressee Addressbook contact you want to know of the presence of
     * @return 0 (unknown), 1 (offline), 3 (away), 4 (online)
     */
    int AddressbookBase::presenceStatusByAddressee(const TDEABC::Addressee &addressee)
    {
        Q_ASSERT(&addressee);
        NickInfoPtr nickInfo = getNickInfo(addressee);

        if(!nickInfo)
        {
            if(hasAnyNicks(addressee))
                return 1;                         //either offline, or we aren't on the same server.
            else
                return 0;                         //Not known to us
        }
        if(nickInfo->isAway()) return 3;
        return 4;

    }
    bool AddressbookBase::isOnline(TDEABC::Addressee &addressee)
    {
        return !!getNickInfo(addressee);
    }

    bool AddressbookBase::editAddressee(const TQString &uid)
    {
        Q_ASSERT(!uid.isEmpty());
        //FIXME:  This hack can be removed.  I fixed the below mentioned bug in kde 3.4 cvs - 2005-01-02
        //
        //Because of stupid bugs in kaddressbook, first load kaddressbook using startServiceByDesktopPath
        // then call it on the command line to actually put it in edit mode.  This is stupid :(
        kapp->startServiceByDesktopName( "kaddressbook" );

        TDEProcess *proc = new TDEProcess;
        *proc << "kaddressbook";
        *proc << "--editor-only" << "--uid" << uid;
        kdDebug() << "running kaddressbook --editor-only --uid " << uid << endl;
        if(!proc->start())
        {
            KMessageBox::error(0, i18n("Could not run your addressbook program (kaddressbook).  This is most likely because it is not installed.  Please install the 'tdepim' packages."));
            return false;
        }
        return true;
    }

    bool AddressbookBase::sendEmail(const TDEABC::Addressee &addressee)
    {
        if(addressee.preferredEmail().isEmpty())
        {
            KMessageBox::sorry(0, i18n("The contact that you have selected does not have an email address associated with them. "), i18n("Cannot Send Email"));
            return false;
        }
        return runEmailProgram(addressee.fullEmail());
    }

    bool AddressbookBase::runEmailProgram(const TQString &mailtoaddress)
    {
        KRun *proc = new KRun(KURL(TQString("mailto:") + KStringHandler::from8Bit(mailtoaddress.ascii())));
        kdDebug() << "Sending email to " << mailtoaddress << endl;
        if(proc->hasError())
        {
            KMessageBox::error(0, i18n("Could not run your email program. This is possibly because one is not installed. To install the TDE email program (kmail) please install the 'tdepim' packages."));
            return false;
        }
        return true;

    }
    bool AddressbookBase::sendEmail(const ChannelNickList &nickList)
    {
        if(nickList.isEmpty()) return false;
        TQString mailto;

        TQStringList nicksWithoutAddressee;
        TQStringList nicksWithoutEmails;
        TQStringList nicksWithEmails;
        for(ChannelNickList::ConstIterator it=nickList.begin();it!=nickList.end();++it)
        {
            NickInfoPtr nickInfo = (*it)->getNickInfo();
            Q_ASSERT(nickInfo);  if(!nickInfo) return false;
            TDEABC::Addressee addr = nickInfo->getAddressee();
            if(addr.isEmpty())
            {
                nicksWithoutAddressee.append(nickInfo->getNickname());
            }
            else if(addr.preferredEmail().isEmpty())
            {
                nicksWithoutEmails.append(nickInfo->getNickname());
            }
            else
            {
                nicksWithEmails.append(nickInfo->getNickname());
                if(!mailto.isEmpty())
                    mailto += ", ";
                mailto += addr.fullEmail();

            }
        }
        if(!nicksWithoutAddressee.isEmpty() || !nicksWithoutEmails.isEmpty())
        {
            TQString message;
            if(nicksWithoutEmails.isEmpty())
            {
                if(nicksWithEmails.isEmpty())
                {
                    if(nicksWithoutAddressee.count() > 1)
                        message = i18n("None of the contacts that you have selected were associated with an addressbook contacts. ");
                    else
                        message = i18n("The contact that you have selected is not associated with an addressbook contact. ");
                }
                else
                {
                    if(nicksWithoutAddressee.count() > 1)
                        message = i18n("Some of the contacts (%1) that you have selected are not associated with addressbook contacts. ").arg(nicksWithoutAddressee.join(", "));
                    else
                        message = i18n("One of the contacts (%1) that you have selected is not associated with an addressbook contact. ").arg(nicksWithoutAddressee.join(", "));
                }
                message += i18n("You can right click on a contact, and choose to edit the Addressbook Associations to link them to a contact in your addressbook.");
            }
            else if(nicksWithoutAddressee.isEmpty())
            {
                if(nicksWithEmails.isEmpty())
                {
                    if(nicksWithoutEmails.count() > 1)
                        message = i18n("None of the contacts that you have selected have an email address associated with them. ");
                    else
                        message = i18n("The contact that you have selected does not have an email address associated with them. ");
                }
                else
                {
                    if(nicksWithoutEmails.count() > 1)
                        message = i18n("Some of the contacts (%1) that you have selected do not have an email address associated with them. ").arg(nicksWithoutEmails.join(", "));
                    else
                        message = i18n("One of the contacts (%1) that you have selected does not have an email address associated with them. ").arg(nicksWithoutEmails.join(", "));
                }
                message += i18n("You can right click on a contact, and choose to edit the addressbook contact, adding an email for them.");
            }
            else
            {
                message = i18n("Some of the contacts (%1) that you have selected are not associated with addressbook contacts, and some of the contacts (%2) do not have an email address associated with them.  ").arg(nicksWithoutAddressee.join(", ").arg(nicksWithoutEmails.join(", ")));
                message += i18n("You can right click on a contact, and choose to edit the Addressbook Associations to link them to a contact in your addressbook, and choose to edit the addressbook contact, adding an email for them.");
            }
            if(nicksWithEmails.isEmpty())
            {
                KMessageBox::sorry(0, message, i18n("Cannot Send Email"));
                return false;
            }
            else
            {
                message += i18n("\nDo you want to send an email anyway to the nicks that do have an email address?");
                int result = KMessageBox::questionYesNo(0, message, i18n("Send Email"), KGuiItem(i18n("&Send Email...")), KGuiItem(i18n("&Cancel")));
                if(result == KMessageBox::No)
                {
                    return false;
                }
            }
        }

        return runEmailProgram(mailto);
    }

}                                                 // NAMESPACE

#include "addressbook_base.moc"
