/*
    irccontact.cpp - IRC Contact

    Copyright (c) 2002      by Nick Betcher <nbetcher@kde.org>
    Copyright (c) 2004      by Michel Hermier <michel.hermier@wanadoo.fr>
    Copyright (c) 2005      by Tommi Rantala <tommi.rantala@cs.helsinki.fi>

    Kopete    (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>

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

#include <kdebug.h>
#include <tdelocale.h>
#include <tqregexp.h>

#include <tqtimer.h>
#include <tqtextcodec.h>

#include "ircaccount.h"
#include "kopeteglobal.h"
#include "kopeteuiglobal.h"
#include "kopetemetacontact.h"
#include "kopeteview.h"
#include "ircusercontact.h"
#include "irccontact.h"
#include "ircprotocol.h"
#include "ircservercontact.h"
#include "irccontactmanager.h"
#include "ksparser.h"

IRCContact::IRCContact(IRCAccount *account, KIRC::EntityPtr entity, Kopete::MetaContact *metac, const TQString& icon)
	: Kopete::Contact(account, entity->name(), metac, icon),
	  m_chatSession(0)
{
}

IRCContact::IRCContact(IRCContactManager *contactManager, const TQString &nick, Kopete::MetaContact *metac, const TQString& icon)
	: Kopete::Contact(contactManager->account(), nick, metac, icon),
	  m_nickName(nick),
	  m_chatSession(0)
{
	KIRC::Engine *engine = kircEngine();

	// Contact list display name
	setProperty( Kopete::Global::Properties::self()->nickName(), m_nickName );

	// IRCContactManager stuff
	TQObject::connect(contactManager, TQT_SIGNAL(privateMessage(IRCContact *, IRCContact *, const TQString &)),
			this, TQT_SLOT(privateMessage(IRCContact *, IRCContact *, const TQString &)));

	// Kopete::ChatSessionManager stuff
	mMyself.append( static_cast<Kopete::Contact*>( this ) );

	// KIRC stuff
	TQObject::connect(engine, TQT_SIGNAL(incomingNickChange(const TQString &, const TQString &)),
			this, TQT_SLOT( slotNewNickChange(const TQString&, const TQString&)));
	TQObject::connect(engine, TQT_SIGNAL(successfullyChangedNick(const TQString &, const TQString &)),
			this, TQT_SLOT(slotNewNickChange(const TQString &, const TQString &)));
	TQObject::connect(engine, TQT_SIGNAL(incomingQuitIRC(const TQString &, const TQString &)),
			this, TQT_SLOT( slotUserDisconnected(const TQString&, const TQString&)));

	TQObject::connect(engine, TQT_SIGNAL(statusChanged(KIRC::Engine::Status)),
			this, TQT_SLOT(updateStatus()));

	engine->setCodec( m_nickName, codec() );
}

IRCContact::~IRCContact()
{
//	kdDebug(14120) << k_funcinfo << m_nickName << endl;
	if (metaContact() && metaContact()->isTemporary() && !isChatting(m_chatSession))
		metaContact()->deleteLater();

	emit destroyed(this);
}

IRCAccount *IRCContact::ircAccount() const
{
	return static_cast<IRCAccount *>(account());
}

KIRC::Engine *IRCContact::kircEngine() const
{
	return ircAccount()->engine();
}

bool IRCContact::isReachable()
{
	if (onlineStatus().status() != Kopete::OnlineStatus::Offline &&
		onlineStatus().status() != Kopete::OnlineStatus::Unknown)
		return true;

	return false;
}

const TQString IRCContact::caption() const
{
	return TQString();
}
/*
const TQString IRCContact::formatedName() const
{
	return TQString();
}
*/
void IRCContact::updateStatus()
{
}

void IRCContact::privateMessage(IRCContact *, IRCContact *, const TQString &)
{
}

void IRCContact::setCodec(const TQTextCodec *codec)
{
	kircEngine()->setCodec(m_nickName, codec);
	metaContact()->setPluginData(m_protocol, TQString::fromLatin1("Codec"), TQString::number(codec->mibEnum()));
}

const TQTextCodec *IRCContact::codec()
{
	TQString codecId = metaContact()->pluginData(m_protocol, TQString::fromLatin1("Codec"));
	TQTextCodec *codec = ircAccount()->codec();

	if( !codecId.isEmpty() )
	{
		bool test = true;
		uint mib = codecId.toInt(&test);
		if (test)
			codec = TQTextCodec::codecForMib(mib);
		else
			codec = TQTextCodec::codecForName(codecId.latin1());
	}

	if( !codec )
		return kircEngine()->codec();

	return codec;
}

Kopete::ChatSession *IRCContact::manager(Kopete::Contact::CanCreateFlags canCreate)
{
	IRCAccount *account = ircAccount();
	KIRC::Engine *engine = kircEngine();

	if (canCreate == Kopete::Contact::CanCreate && !m_chatSession)
	{
		if( engine->status() == KIRC::Engine::Idle && dynamic_cast<IRCServerContact*>(this) == 0 )
			account->connect();

		m_chatSession = Kopete::ChatSessionManager::self()->create(account->myself(), mMyself, account->protocol());
		m_chatSession->setDisplayName(caption());

		TQObject::connect(m_chatSession, TQT_SIGNAL(messageSent(Kopete::Message&, Kopete::ChatSession *)),
			this, TQT_SLOT(slotSendMsg(Kopete::Message&, Kopete::ChatSession *)));
		TQObject::connect(m_chatSession, TQT_SIGNAL(closing(Kopete::ChatSession *)),
			this, TQT_SLOT(chatSessionDestroyed()));

		initConversation();
	}

	return m_chatSession;
}

void IRCContact::chatSessionDestroyed()
{
	m_chatSession = 0;

	if (metaContact()->isTemporary() && !isChatting())
		deleteLater();
}

void IRCContact::slotUserDisconnected(const TQString &user, const TQString &reason)
{
	if (m_chatSession)
	{
		TQString nickname = user.section('!', 0, 0);
		Kopete::Contact *c = locateUser( nickname );
		if ( c )
		{
			m_chatSession->removeContact(c, i18n("Quit: \"%1\" ").arg(reason), Kopete::Message::RichText);
			c->setOnlineStatus(m_protocol->m_UserStatusOffline);
		}
	}
}

void IRCContact::setNickName( const TQString &nickname )
{
	kdDebug(14120) << k_funcinfo << m_nickName << " changed to " << nickname << endl;
	m_nickName = nickname;
	Kopete::Contact::setNickName( nickname );
}

void IRCContact::slotNewNickChange(const TQString &oldnickname, const TQString &newnickname)
{
	IRCAccount *account = ircAccount();

	IRCContact *user = static_cast<IRCContact*>( locateUser(oldnickname) );
	if( user )
	{
		user->setNickName( newnickname );

		//If the user is in our contact list, then change the notify list nickname
		if (!user->metaContact()->isTemporary())
		{
			account->contactManager()->removeFromNotifyList( oldnickname );
			account->contactManager()->addToNotifyList( newnickname );
		}
	}
}

void IRCContact::slotSendMsg(Kopete::Message &message, Kopete::ChatSession *)
{
	TQString htmlString = message.escapedBody();

	// Messages we get with RichText enabled:
	//
	// Hello world in bold and color:
	//   <span style="font-weight:600;color:#403897">Hello World</span>
	// 
	// Two-liner in color:
	//   <span style="color:#403897">Hello<br />World</span>

	if (htmlString.find(TQString::fromLatin1("</span")) > -1)
	{
		TQRegExp findTags( TQString::fromLatin1("<span style=\"(.*)\">(.*)</span>") );
		findTags.setMinimal( true );
		int pos = 0;

		while (pos >= 0)
		{
			pos = findTags.search(htmlString);
			if (pos > -1)
			{
				TQString styleHTML = findTags.cap(1);
				TQString replacement = findTags.cap(2);
				TQStringList styleAttrs = TQStringList::split(';', styleHTML);

				for (TQStringList::Iterator attrPair = styleAttrs.begin(); attrPair != styleAttrs.end(); ++attrPair)
				{
					TQString attribute = (*attrPair).section(':',0,0);
					TQString value = (*attrPair).section(':',1);

					if( attribute == TQString::fromLatin1("color") )
					{
						int ircColor = KSParser::colorForHTML( value );
						if( ircColor > -1 )
							replacement.prepend( TQString( TQChar(0x03) ).append( TQString::number(ircColor) ) ).append( TQChar( 0x03 ) );
					}
					else if( attribute == TQString::fromLatin1("font-weight") &&
					             value == TQString::fromLatin1("600") ) {
						// Bolding
						replacement.prepend( TQChar(0x02) ).append( TQChar(0x02) );
					}
					else if( attribute == TQString::fromLatin1("text-decoration") &&
					             value == TQString::fromLatin1("underline") ) {
						replacement.prepend( TQChar(31) ).append( TQChar(31) );
					}
				}

				htmlString = htmlString.left( pos ) + replacement + htmlString.mid( pos + findTags.matchedLength() );
			}
		}
	}

	htmlString = Kopete::Message::unescape(htmlString);

	TQStringList messages = TQStringList::split( '\n', htmlString );

	for( TQStringList::Iterator it = messages.begin(); it != messages.end(); ++it )
	{
		// Dont use the resulting string(s). The problem is that we'd have to parse them
		// back to format that would be suitable for appendMessage().
		//
		// TODO: If the given message was plaintext, we could easily show what was
		// actually sent.

		sendMessage(*it);
	}

	if (message.requestedPlugin() != CHAT_VIEW) {
		Kopete::Message msg(message.from(), message.to(), message.escapedBody(), message.direction(),
				Kopete::Message::RichText, CHAT_VIEW, message.type());

		msg.setBg(TQColor());
		msg.setFg(TQColor());

		appendMessage(msg);
	} else {
		// Lets not modify the given message object.
		Kopete::Message msg = message;
		msg.setBg(TQColor());
		appendMessage(msg);
	}

	manager(Kopete::Contact::CanCreate)->messageSucceeded();
}

TQStringList IRCContact::sendMessage( const TQString &msg )
{
	TQStringList messages;

	TQString newMessage = msg;

	// IRC limits the message size to 512 characters. So split the given
	// message into pieces.
	//
	// This can of course give nasty results, but most of us dont write
	// that long lines anyway ;-)... And this is how other clients also
	// seem to behave.

	int l = 500 - m_nickName.length();

	do {
		messages.append(newMessage.mid(0, l));
		newMessage.remove(0, l);
	} while (!newMessage.isEmpty());

	for (TQStringList::const_iterator it = messages.begin();
	     it != messages.end(); ++it)
		kircEngine()->privmsg(m_nickName, *it);

	return messages;
}

Kopete::Contact *IRCContact::locateUser(const TQString &nick)
{
	IRCAccount *account = ircAccount();

	if (m_chatSession)
	{
		if( nick == account->mySelf()->nickName() )
			return account->mySelf();
		else
		{
			Kopete::ContactPtrList mMembers = m_chatSession->members();
			for (Kopete::Contact *it = mMembers.first(); it; it = mMembers.next())
			{
				if (static_cast<IRCContact*>(it)->nickName() == nick)
					return it;
			}
		}
	}
	return 0;
}

bool IRCContact::isChatting(const Kopete::ChatSession *avoid) const
{
	IRCAccount *account = ircAccount();

	if (!account)
		return false;

	TQValueList<Kopete::ChatSession*> sessions = Kopete::ChatSessionManager::self()->sessions();
	for (TQValueList<Kopete::ChatSession*>::Iterator it= sessions.begin(); it!=sessions.end() ; ++it)
	{
	  if( (*it) != avoid && (*it)->account() == account &&
			   (*it)->members().contains(this) )
		{
			return true;
		}
	}
	return false;
}

void IRCContact::deleteContact()
{
	kdDebug(14120) << k_funcinfo << m_nickName << endl;

	delete m_chatSession;

	if (!isChatting())
	{
		kdDebug(14120) << k_funcinfo << "will delete " << m_nickName << endl;
		Kopete::Contact::deleteContact();
	}
	else
	{
		metaContact()->removeContact(this);
		Kopete::MetaContact *m = new Kopete::MetaContact();
		m->setTemporary(true);
		setMetaContact(m);
	}
}

void IRCContact::appendMessage(Kopete::Message &msg)
{
	manager(Kopete::Contact::CanCreate)->appendMessage(msg);
}

KopeteView *IRCContact::view()
{
	if (m_chatSession)
		return m_chatSession->view(false);
	return 0L;
}
void IRCContact::serialize(TQMap<TQString, TQString> & /*serializedData*/, TQMap<TQString, TQString> &addressBookData)
{
	// write the
	addressBookData[ protocol()->addressBookIndexField() ] = ( contactId() + TQChar(0xE120) + account()->accountId() );
}

void IRCContact::receivedMessage( KIRC::Engine::ServerMessageType type,
				const KIRC::EntityPtr &from,
				const KIRC::EntityPtrList &to,
				const TQString &msg)
{
	if (to.contains(m_entity))
	{
		IRCContact *fromContact = ircAccount()->getContact(from);
		Kopete::Message message(fromContact, manager()->members(), msg, Kopete::Message::Inbound,
					Kopete::Message::RichText, CHAT_VIEW);
		appendMessage(message);
	}
}

#include "irccontact.moc"