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

/*
  Copyright (C) 2002 Dario Abatianni <eisfuchs@tigress.com>
  Copyright (C) 2005-2008 Eike Hein <hein@kde.org>
*/

#include "query.h"
#include "channel.h"
#include "server.h"
#include "konversationapplication.h"
#include "konversationmainwindow.h"
#include "viewcontainer.h"
#include "ircinput.h"
#include "ircview.h"
#include "ircviewbox.h"
#include "common.h"
#include "topiclabel.h"

#include <tqhbox.h>
#include <tqcheckbox.h>
#include <tqlabel.h>
#include <tqlineedit.h>
#include <tqtextcodec.h>
#include <tqtooltip.h>
#include <tqtextstream.h>
#include <tqwhatsthis.h>
#include <tqsplitter.h>

#include <tdelocale.h>
#include <kstandarddirs.h>
#include <kdebug.h>
#include <tdemessagebox.h>
#include <kiconloader.h>
#include <kstringhandler.h>
#include <tdepopupmenu.h>


Query::Query(TQWidget* parent, TQString _name) : ChatWindow(parent)
{
    name=_name; // need the name a little bit earlier for setServer
    // don't setName here! It will break logfiles!
    //   setName("QueryWidget");
    setType(ChatWindow::Query);

    setChannelEncodingSupported(true);

    m_headerSplitter = new TQSplitter(TQt::Vertical, this);

    m_initialShow = true;
    awayChanged=false;
    awayState=false;
    TQHBox *box = new TQHBox(m_headerSplitter);
    addresseeimage = new TQLabel(box, "query_image");
    addresseeimage->setSizePolicy(TQSizePolicy::Fixed, TQSizePolicy::Fixed);
    addresseeimage->hide();
    addresseelogoimage = new TQLabel(box, "query_logo_image");
    addresseelogoimage->setSizePolicy(TQSizePolicy::Fixed, TQSizePolicy::Fixed);
    addresseelogoimage->hide();

    queryHostmask=new Konversation::TopicLabel(box, "query_hostmask");

    TQString whatsthis = i18n("<qt>Some details of the person you are talking to in this query is shown in this bar.  The full name and hostmask is shown, along with any image or logo this person has associated with them in the TDE Addressbook.<p>See the <i>Konversation Handbook</i> for information on associating a nick with a contact in the Addressbook, and for an explanation of what the hostmask is.</qt>");
    TQWhatsThis::add(addresseeimage, whatsthis);
    TQWhatsThis::add(addresseelogoimage, whatsthis);
    TQWhatsThis::add(queryHostmask, whatsthis);

    IRCViewBox* ircBox = new IRCViewBox(m_headerSplitter,0);
    setTextView(ircBox->ircView());               // Server will be set later in setServer();
    textView->setAcceptDrops(true);
    connect(textView,TQ_SIGNAL(filesDropped(const TQStrList&)),this,TQ_SLOT(filesDropped(const TQStrList&)));
    connect(textView,TQ_SIGNAL(popupCommand(int)),this,TQ_SLOT(popup(int)));

    // link "Whois", "Ignore" ... menu items into ircview popup
    TQPopupMenu* popup=textView->getPopup();
    popup->insertSeparator();
    popup->insertItem(i18n("&Whois"),Konversation::Whois);
    popup->insertItem(i18n("&Version"),Konversation::Version);
    popup->insertItem(i18n("&Ping"),Konversation::Ping);
    popup->insertSeparator();

    popup->insertItem(i18n("Ignore"), Konversation::IgnoreNick);
    popup->insertItem(i18n("Unignore"), Konversation::UnignoreNick);
    popup->setItemVisible(Konversation::IgnoreNick, false);
    popup->setItemVisible(Konversation::UnignoreNick, false);

    if (kapp->authorize("allow_downloading"))
        popup->insertItem(SmallIcon("2rightarrow"),i18n("Send &File..."),Konversation::DccSend);

    popup->insertItem(i18n("Add to Watched Nicks"), Konversation::AddNotify);

    // This box holds the input line
    TQHBox* inputBox=new TQHBox(this, "input_log_box");
    inputBox->setSpacing(spacing());

    awayLabel=new TQLabel(i18n("(away)"),inputBox);
    awayLabel->hide();
    blowfishLabel = new TQLabel(inputBox);
    blowfishLabel->hide();
    blowfishLabel->setPixmap(TDEGlobal::iconLoader()->loadIcon("encrypted", TDEIcon::Toolbar));
    queryInput=new IRCInput(inputBox);

    getTextView()->installEventFilter(queryInput);
    queryInput->installEventFilter(this);

    // connect the signals and slots
    connect(queryInput,TQ_SIGNAL (submit()),this,TQ_SLOT (queryTextEntered()) );
    connect(queryInput,TQ_SIGNAL (envelopeCommand()),this,TQ_SLOT (queryPassthroughCommand()) );
    connect(queryInput,TQ_SIGNAL (textPasted(const TQString&)),this,TQ_SLOT (textPasted(const TQString&)) );
    connect(getTextView(), TQ_SIGNAL(textPasted(bool)), queryInput, TQ_SLOT(paste(bool)));
    connect(getTextView(),TQ_SIGNAL (gotFocus()),queryInput,TQ_SLOT (setFocus()) );

    connect(textView,TQ_SIGNAL (sendFile()),this,TQ_SLOT (sendFileMenu()) );
    connect(textView,TQ_SIGNAL (extendedPopup(int)),this,TQ_SLOT (popup(int)) );
    connect(textView,TQ_SIGNAL (autoText(const TQString&)),this,TQ_SLOT (sendQueryText(const TQString&)) );

    updateAppearance();

    setLog(Preferences::log());
}

Query::~Query()
{
}

void Query::setServer(Server* newServer)
{
    if (m_server != newServer)
        connect(newServer, TQ_SIGNAL(connectionStateChanged(Server*, Konversation::ConnectionState)),
                TQ_SLOT(connectionStateChanged(Server*, Konversation::ConnectionState)));

    ChatWindow::setServer(newServer);

    if (!newServer->getKeyForRecipient(getName()).isNull())
        blowfishLabel->show();
}

void Query::connectionStateChanged(Server* server, Konversation::ConnectionState state)
{
    if (server == m_server)
    {
        if (state ==  Konversation::SSConnected)
        {
            //HACK the way the notification priorities work sucks, this forces the tab text color to ungray right now.
            if (m_currentTabNotify == Konversation::tnfNone || !Preferences::tabNotificationsEvents())
                KonversationApplication::instance()->getMainWindow()->getViewContainer()->unsetViewNotification(this);
        }
        else
        {
            //HACK the way the notification priorities work sucks, this forces the tab text color to gray right now.
            if (m_currentTabNotify == Konversation::tnfNone || (!Preferences::tabNotificationsEvents() && m_currentTabNotify == Konversation::tnfControl))
                KonversationApplication::instance()->getMainWindow()->getViewContainer()->unsetViewNotification(this);
        }
    }
}

void Query::setName(const TQString& newName)
{
    //if(ChatWindow::getName() == newName) return;  // no change, so return

    ChatWindow::setName(newName);

    // don't change logfile name if query name changes
    // This will prevent Nick-Changers to create more than one log file,
    if (logName.isEmpty())
    {
        TQString logName =  (Preferences::lowerLog()) ? getName().lower() : getName() ;

        if(Preferences::addHostnameToLog())
        {
            if(m_nickInfo)
                logName += m_nickInfo->getHostmask();
        }

        setLogfileName(logName);
    }
}

void Query::setEncryptedOutput(bool e)
{
    if (e)
        blowfishLabel->show();
    else
        blowfishLabel->hide();
}

void Query::queryTextEntered()
{
    TQString line=queryInput->text();
    queryInput->setText("");
    if(line.lower()==Preferences::commandChar()+"clear")
    {
        textView->clear();
    }
    else if(line.lower()==Preferences::commandChar()+"part")
    {
        m_server->closeQuery(getName());
    }
    else if(line.length())
    {
         sendQueryText(line);
    }
}

void Query::queryPassthroughCommand()
{
    TQString commandChar = Preferences::commandChar();
    TQString line = queryInput->text();

    queryInput->setText("");

    if(!line.isEmpty())
    {
        // Prepend commandChar on Ctrl+Enter to bypass outputfilter command recognition
        if (line.startsWith(commandChar))
        {
            line = commandChar + line;
        }
        sendQueryText(line);
    }
}

void Query::sendQueryText(const TQString& sendLine)
{
    // create a work copy
    TQString outputAll(sendLine);
    // replace aliases and wildcards
    if(m_server->getOutputFilter()->replaceAliases(outputAll))
    {
        outputAll = m_server->parseWildcards(outputAll, m_server->getNickname(), getName(), TQString(), TQString(), TQString());
    }

    // Send all strings, one after another
    TQStringList outList=TQStringList::split('\n',outputAll);
    for(unsigned int index=0;index<outList.count();index++)
    {
        TQString output(outList[index]);

        // encoding stuff is done in Server()
        Konversation::OutputFilterResult result = m_server->getOutputFilter()->parse(m_server->getNickname(), output, getName());

        if(!result.output.isEmpty())
        {
            if(result.type == Konversation::Action) appendAction(m_server->getNickname(), result.output);
            else if(result.type == Konversation::Command) appendCommandMessage(result.typeString, result.output);
            else if(result.type == Konversation::Program) appendServerMessage(result.typeString, result.output);
            else if(!result.typeString.isEmpty()) appendQuery(result.typeString, result.output);
            else appendQuery(m_server->getNickname(), result.output);
        }
        else if (result.outputList.count())
        {
            Q_ASSERT(result.type==Konversation::Message);
            for ( TQStringList::Iterator it = result.outputList.begin(); it != result.outputList.end(); ++it )
            {
                append(m_server->getNickname(), *it);
            }
        }

        if (!result.toServerList.empty())
        {
            m_server->queueList(result.toServerList);
        }
        else
        {
            m_server->queue(result.toServer);
        }
    } // for
}

void Query::updateAppearance()
{
    TQColor fg;
    TQColor bg;

    if(Preferences::inputFieldsBackgroundColor())
    {
        fg=Preferences::color(Preferences::ChannelMessage);
        bg=Preferences::color(Preferences::TextViewBackground);
    }
    else
    {
        fg=colorGroup().foreground();
        bg=colorGroup().base();
    }

    queryInput->unsetPalette();
    queryInput->setPaletteForegroundColor(fg);
    queryInput->setPaletteBackgroundColor(bg);

    getTextView()->unsetPalette();

    if (Preferences::showBackgroundImage())
    {
        getTextView()->setViewBackground(Preferences::color(Preferences::TextViewBackground),
            Preferences::backgroundImage());
    }
    else
    {
        getTextView()->setViewBackground(Preferences::color(Preferences::TextViewBackground),
            TQString());
    }

    if (Preferences::customTextFont())
    {
        getTextView()->setFont(Preferences::textFont());
        queryInput->setFont(Preferences::textFont());
    }
    else
    {
        getTextView()->setFont(TDEGlobalSettings::generalFont());
        queryInput->setFont(TDEGlobalSettings::generalFont());
    }

    ChatWindow::updateAppearance();
}

void Query::textPasted(const TQString& text)
{
    if(m_server)
    {
        TQStringList multiline=TQStringList::split('\n',text);
        for(unsigned int index=0;index<multiline.count();index++)
        {
            TQString line=multiline[index];
            TQString cChar(Preferences::commandChar());
            // make sure that lines starting with command char get escaped
            if(line.startsWith(cChar)) line=cChar+line;
            sendQueryText(line);
        }
    }
}

void Query::indicateAway(bool show)
{
    // TQt does not redraw the label properly when they are not on screen
    // while getting hidden, so we remember the "soon to be" state here.
    if(isHidden())
    {
        awayChanged=true;
        awayState=show;
    }
    else
    {
        if(show)
            awayLabel->show();
        else
            awayLabel->hide();
    }
}

// fix TQTs broken behavior on hidden TQListView pages
void Query::showEvent(TQShowEvent*)
{
    if(awayChanged)
    {
        awayChanged=false;
        indicateAway(awayState);
    }

    if(m_initialShow) {
        m_initialShow = false;
        TQValueList<int> sizes;
        sizes << queryHostmask->sizeHint().height() << (height() - queryHostmask->sizeHint().height());
        m_headerSplitter->setSizes(sizes);
    }
}

void Query::popup(int id)
{
    // get the nickname to the context menu popup
    TQString name = textView->getContextNick();
    // if there was none (right click into the text view) assume query partner
    if (name.isEmpty()) name = getName();

    switch (id)
    {
        case Konversation::Whois:
            sendQueryText(Preferences::commandChar()+"WHOIS "+name+' '+name);
            break;

        case Konversation::IgnoreNick:
        {
            if (KMessageBox::warningContinueCancel(this, i18n("Do you want to ignore %1?").arg(name),
                i18n("Ignore"), i18n("Ignore"), "IgnoreNick") == KMessageBox::Continue)
            {
                sendQueryText(Preferences::commandChar()+"IGNORE -ALL "+name);

                int rc = KMessageBox::questionYesNo(this,
                i18n("Do you want to close this query after ignoring this nickname?"),
                i18n("Close This Query"),
                i18n("Close"),
                i18n("Keep Open"),
                "CloseQueryAfterIgnore");

                if (rc == KMessageBox::Yes && m_server)
                    TQTimer::singleShot(0, this, TQ_SLOT(closeWithoutAsking()));
            }

            break;
        }
        case Konversation::UnignoreNick:
        {
            TQString question = i18n("Do you want to stop ignoring %1?").arg(name);

            if (KMessageBox::warningContinueCancel(this, question, i18n("Unignore"), i18n("Unignore"), "UnignoreNick") ==
                KMessageBox::Continue)
            {
                sendQueryText(Preferences::commandChar()+"UNIGNORE "+name);
            }

            break;
        }
        case Konversation::AddNotify:
        {
            if (m_server->getServerGroup())
            {
                if (!Preferences::isNotify(m_server->getServerGroup()->id(), name))
                    Preferences::addNotify(m_server->getServerGroup()->id(),name);
            }
            break;
        }
        case Konversation::DccSend:
            sendQueryText(Preferences::commandChar()+"DCC SEND "+name);
            break;

        case Konversation::Version:
            sendQueryText(Preferences::commandChar()+"CTCP "+name+" VERSION");
            break;

        case Konversation::Ping:
            sendQueryText(Preferences::commandChar()+"CTCP "+name+" PING");
            break;

        case Konversation::Topic:
            m_server->requestTopic(getTextView()->currentChannel());
            break;
        case Konversation::Names:
            m_server->queue("NAMES " + getTextView()->currentChannel(), Server::LowPriority);
            break;
        case Konversation::Join:
            m_server->queue("JOIN " + getTextView()->currentChannel());
            break;

        default:
            kdDebug() << "Query::popup(): Popup id " << id << " does not belong to me!" << endl;
            break;
    }

    // delete context menu nickname
    textView->clearContextNick();
}

void Query::sendFileMenu()
{
    emit sendFile(getName());
}

void Query::childAdjustFocus()
{
    queryInput->setFocus();
}

void Query::setNickInfo(const NickInfoPtr & nickInfo)
{
    if(m_nickInfo)
        disconnect(m_nickInfo, TQ_SIGNAL(nickInfoChanged()), this, TQ_SLOT(nickInfoChanged()));

    m_nickInfo = nickInfo;
    Q_ASSERT(m_nickInfo); if(!m_nickInfo) return;
    setName(m_nickInfo->getNickname());
    connect(m_nickInfo, TQ_SIGNAL(nickInfoChanged()), this, TQ_SLOT(nickInfoChanged()));
    nickInfoChanged();
}

void Query::nickInfoChanged()
{
    if(m_nickInfo)
    {
        setName(m_nickInfo->getNickname());
        TQString text = m_nickInfo->getBestAddresseeName();
        if(!m_nickInfo->getHostmask().isEmpty() && !text.isEmpty())
            text += " - ";
        text += m_nickInfo->getHostmask();
        if(m_nickInfo->isAway() && !m_nickInfo->getAwayMessage().isEmpty())
            text += " (" + KStringHandler::rsqueeze(m_nickInfo->getAwayMessage(),100) + ") ";
        queryHostmask->setText(Konversation::removeIrcMarkup(text));

        TDEABC::Picture pic = m_nickInfo->getAddressee().photo();
        if(pic.isIntern())
        {
            TQPixmap qpixmap(pic.data().scaleHeight(queryHostmask->height()));
            if(!qpixmap.isNull())
            {
                addresseeimage->setPixmap(qpixmap);
                addresseeimage->show();
            }
            else
            {
                addresseeimage->hide();
            }
        }
        else
        {
            addresseeimage->hide();
        }
        TDEABC::Picture logo = m_nickInfo->getAddressee().logo();
        if(logo.isIntern())
        {
            TQPixmap qpixmap(logo.data().scaleHeight(queryHostmask->height()));
            if(!qpixmap.isNull())
            {
                addresseelogoimage->setPixmap(qpixmap);
                addresseelogoimage->show();
            }
            else
            {
                addresseelogoimage->hide();
            }
        }
        else
        {
            addresseelogoimage->hide();
        }

        TQString strTooltip;
        TQTextStream tooltip( &strTooltip, IO_WriteOnly );

        tooltip << "<qt>";

        tooltip << "<table cellspacing=\"0\" cellpadding=\"0\">";

        m_nickInfo->tooltipTableData(tooltip);

        tooltip << "</table></qt>";
        TQToolTip::add(queryHostmask, strTooltip);
        TQToolTip::add(addresseeimage, strTooltip);
        TQToolTip::add(addresseelogoimage, strTooltip);

    }
    else
    {
        addresseeimage->hide();
        addresseelogoimage->hide();
    }

    emit updateQueryChrome(this,getName());
    emitUpdateInfo();
}

NickInfoPtr Query::getNickInfo()
{
    return m_nickInfo;
}

TQString Query::getTextInLine() { return queryInput->text(); }

bool Query::canBeFrontView()        { return true; }
bool Query::searchView()       { return true; }

void Query::appendInputText(const TQString& s, bool fromCursor)
{
    if(!fromCursor)
    {
        queryInput->append(s);
    }
    else
    {
        int para = 0, index = 0;
        queryInput->getCursorPosition(&para, &index);
        queryInput->insertAt(s, para, index);
        queryInput->setCursorPosition(para, index + s.length());
    }
}

                                                  // virtual
void Query::setChannelEncoding(const TQString& encoding)
{
    Preferences::setChannelEncoding(m_server->getDisplayName(), getName(), encoding);
}

TQString Query::getChannelEncoding()               // virtual
{
    return Preferences::channelEncoding(m_server->getDisplayName(), getName());
}

TQString Query::getChannelEncodingDefaultDesc()    // virtual
{
    return i18n("Identity Default ( %1 )").arg(getServer()->getIdentity()->getCodecName());
}

bool Query::closeYourself(bool confirm)
{
    int result = KMessageBox::Continue;
    if (confirm)
        result=KMessageBox::warningContinueCancel(this, i18n("Do you want to close your query with %1?").arg(getName()), i18n("Close Query"), i18n("Close"), "QuitQueryTab");

    if (result == KMessageBox::Continue)
    {
        m_server->removeQuery(this);
        return true;
    }

    return false;
}

void Query::closeWithoutAsking()
{
    m_server->removeQuery(this);
}

void Query::filesDropped(const TQStrList& files)
{
    m_server->sendURIs(files,getName());
}

void Query::serverOnline(bool online)
{
    //queryInput->setEnabled(online);
    getTextView()->setNickAndChannelContextMenusEnabled(online);

    TQPopupMenu* popup = getTextView()->getPopup();

    if (popup)
    {
        popup->setItemEnabled(Konversation::Whois, online);
        popup->setItemEnabled(Konversation::Version, online);
        popup->setItemEnabled(Konversation::Ping, online);
        popup->setItemEnabled(Konversation::IgnoreNick, online);
        popup->setItemEnabled(Konversation::UnignoreNick, online);
        popup->setItemEnabled(Konversation::AddNotify, online);

        if (kapp->authorize("allow_downloading"))
            popup->setItemEnabled(Konversation::DccSend, online);
    }
}

void Query::emitUpdateInfo()
{
    TQString info;
    if(m_nickInfo->loweredNickname() == m_server->loweredNickname())
        info = i18n("Talking to yourself");
    else if(m_nickInfo)
        info = m_nickInfo->getBestAddresseeName();
    else
        info = getName();

    emit updateInfo(info);
}

// show quit message of nick if we see it
void Query::quitNick(const TQString& reason)
{
    TQString displayReason = reason;

    if (displayReason.isEmpty())
    {
        appendCommandMessage(i18n("Quit"),i18n("%1 has left this server.").arg(getName()),false);
    }
    else
    {
        if (displayReason.find(TQRegExp("[\\0000-\\0037]"))!=-1)
            displayReason+="\017";

        appendCommandMessage(i18n("Quit"),i18n("%1 has left this server (%2).").arg(getName()).arg(displayReason),false);
    }
}

#include "query.moc"
