/**********************************************************************
 * LeechCraft - modular cross-platform feature rich internet client.
 * Copyright (C) 2006-2013  Georg Rudoy
 * Copyright (C) 2011 Minh Ngo
 *
 * Boost Software License - Version 1.0 - August 17th, 2003
 *
 * Permission is hereby granted, free of charge, to any person or organization
 * obtaining a copy of the software and accompanying documentation covered by
 * this license (the "Software") to use, reproduce, display, distribute,
 * execute, and transmit the Software, and to prepare derivative works of the
 * Software, and to permit third-parties to whom the Software is furnished to
 * do so, all subject to the following:
 *
 * The copyright notices in the Software and this entire statement, including
 * the above license grant, this restriction and the following disclaimer,
 * must be included in all copies of the Software, in whole or in part, and
 * all derivative works of the Software, unless such copies or derivative
 * works are solely in the form of machine-executable object code generated by
 * a source language processor.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
 * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
 * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 **********************************************************************/

#include "p100q.h"
#include <QIcon>
#include <QString>
#include <QShortcut>
#include <QTranslator>
#include <QTextEdit>
#include <util/util.h>
#include <interfaces/azoth/imessage.h>
#include <interfaces/azoth/iclentry.h>
#include "xmlsettingsmanager.h"

namespace LeechCraft
{
namespace Azoth
{
namespace p100q
{
	const int PstoCommentPos = 6;

	void Plugin::Init (ICoreProxy_ptr)
	{
		Util::InstallTranslator ("azoth_p100q");

		XmlSettingsDialog_.reset (new Util::XmlSettingsDialog ());
		XmlSettingsDialog_->RegisterObject (&XmlSettingsManager::Instance (),
				"azothp100qsettings.xml");

		PstoCommentRX_ =  QRegExp ("#[a-z]+/[0-9]+[:]", Qt::CaseInsensitive);
		UserRX_ = QRegExp ("(?:[^>/]|<br />)@([\\w\\-]+)", Qt::CaseInsensitive);
		PostAuthorRX_ = QRegExp ("<br />@([\\w\\-]+)", Qt::CaseInsensitive);
		PostRX_ = QRegExp ("#([a-zA-Z]+)[\\+ :]", Qt::CaseInsensitive);
		PostByUserRX_ = QRegExp ("\\s#([a-zA-Z]+)", Qt::CaseInsensitive);
		CommentRX_ = QRegExp ("#([a-zA-Z]+)/([0-9]+)", Qt::CaseInsensitive);
		TagRX_ = QRegExp ("<br />[*] ([^*,<]+(, [^*,<]+)*)");
		ImgRX_ = QRegExp ("<br /><a href=\"(http://[^\"]+[.](png|gif|jpe?g))\">[^<]*</a>", Qt::CaseInsensitive);
	}

	Util::XmlSettingsDialog_ptr Plugin::GetSettingsDialog () const
	{
		return XmlSettingsDialog_;
	}

	void Plugin::SecondInit ()
	{
	}

	QByteArray Plugin::GetUniqueID () const
	{
		return "org.LeechCraft.Azoth.p100q";
	}

	void Plugin::Release ()
	{
	}

	QString Plugin::GetName () const
	{
		return "Azoth p100q";
	}

	QString Plugin::GetInfo () const
	{
		return tr ("Azoth p100q enhances experience with the psto.net microblogging service.");
	}

	QIcon Plugin::GetIcon () const
	{
		static QIcon icon ("lcicons:/plugins/azoth/plugins/p100q/resources/images/p100q.svg");
		return icon;
	}

	QStringList Plugin::Provides () const
	{
		return QStringList ();
	}

	QStringList Plugin::Needs () const
	{
		return QStringList ();
	}

	QStringList Plugin::Uses () const
	{
		return QStringList ();
	}

	void Plugin::SetProvider (QObject*, const QString&)
	{
	}

	QSet<QByteArray> Plugin::GetPluginClasses () const
	{
		QSet<QByteArray> result;
		result << "org.LeechCraft.Plugins.Azoth.Plugins.IGeneralPlugin";
		return result;
	}

	QString Plugin::FormatBody (QString body)
	{
		auto getProp = [] (const QByteArray& name)
		{
			return XmlSettingsManager::Instance ().property (name).toBool ();
		};
		const bool alwaysReplace = getProp ("AlwaysReplace");

		if (body.indexOf (PstoCommentRX_, 0) != PstoCommentPos)
		{
			QString tags, tag;
			int pos = 0;
			int delta = 0;
			while ((pos = TagRX_.indexIn (body, pos)) != -1)
			{
				tags = " * ";
				tag = TagRX_.cap (0);
				QStringList tagslist = TagRX_.cap (1).split (", ");

				Q_FOREACH (const QString& tagval, tagslist)
				{
					QString t = QString (QUrl::toPercentEncoding (tagval)).replace ("%2F", "/");
					auto tmpl = alwaysReplace ?
							" <a href=\"azoth://msgeditreplace/S *%1\" title=\"" :
							" <a href=\"azoth://msgeditinsert/%1%20/%23/S *%1\" title=\"";
					tags += QString (tmpl + tr ("Subscribe to tag") + "\">" + tagval + "</a> ")
								.arg (t);
				}
				delta = body.length ();
				body.replace (tag, tags);
				pos += body.length () - delta;
			}
		}

		const bool showRecommendButton = getProp ("RecommendButton");
		const bool showAvatars = getProp ("ShowAvatars");
		const bool showAddToBookmarkButton = getProp ("AddToBookmarkButton");
		const bool showImg = getProp ("ShowImage");
		const bool showPrivateMessageButton = getProp ("PrivateMessageButton");
		const bool showSubscribeButton = getProp ("SubscribeButton");
		const bool showBlockButton = getProp ("BlockButton");
		const bool showCommentsButton = getProp ("CommentsButton");

		QString postRX = alwaysReplace ?
				" <a href=\"azoth://msgeditreplace/%23\\1%20\">#\\1</a> " :
				" <a href=\"azoth://msgeditinsert/%23\\1%20\">#\\1</a> ";

		QString postAuthorRX = (alwaysReplace ?
				" <a href=\"azoth://msgeditreplace/@\\1+\" title=\"" :
				" <a href=\"azoth://msgeditinsert/@\\1%20/%23/@\\1+\" title=\"") +
			tr ("View user's posts") + "\">@\\1</a> ";

		QString userRX = (alwaysReplace ?
				" <a href=\"azoth://msgeditreplace/@\\1+\" title=\"" :
				" <a href=\"azoth://msgeditinsert/@\\1%20/%23/@\\1+\" title=\"")+
			tr ("View user's posts") + "\">@\\1</a> ";

		QString commentRX = (alwaysReplace ?
				" <a href=\"azoth://msgeditreplace/%23\\1/\\2%20\" title=\"" :
				" <a href=\"azoth://msgeditinsert/%23\\1/\\2%20\" title=\"")+
			tr ("Reply") + "\">#\\1/\\2</a> ";

		QString postByUserRX = (alwaysReplace ?
				" <a href=\"azoth://msgeditreplace/%23\\1+\" title=\"" :
				" <a href=\"azoth://msgeditinsert/%23\\1%20/%23/%23\\1+\" title=\"") +
			tr ("View post") + "\">#\\1</a> ";

		QString imgRX = "<p><a href=\"\\1\"><img style='max-height: 300px; max-width:300px;' src=\"\\1\"/></a><p/>";

		if (showSubscribeButton || showCommentsButton || showRecommendButton || showAddToBookmarkButton)
		{
			postRX += "(";
			commentRX += "(";
		}
		if (showAvatars)
			postAuthorRX +=
				"<img style='float:left;margin-right:4px' width='32px' height='32px' src='http://psto.net/img/a/40/\\1.png'>";
		if (showBlockButton || showPrivateMessageButton)
		{
			userRX += " (";
			postAuthorRX += " (";
			postByUserRX += " (";
		}
		if (showSubscribeButton)
		{
			postRX += "<a href=\"azoth://msgeditreplace/S%20%23\\1\" title=\"" +
				tr ("Subscribe") + "\">S</a> " +
				"<a href=\"azoth://msgeditreplace/U%20%23\\1\" title=\"" +
				tr ("Unsubscribe") + "\">U</a> ";
			commentRX += "<a href=\"azoth://msgeditreplace/U%20%23\\1\" title=\"" +
					tr ("Unsubscribe from post") + "\">U</a> ";
		}
		if (showCommentsButton)
			postRX += "<a href=\"azoth://msgeditreplace/%23\\1+\" title=\"" +
				tr ("View") + "\">+</a> ";
		if (showRecommendButton)
		{
			postRX += "<a href=\"azoth://msgeditreplace/!%20%23\\1%20\" title=\"" +
				tr ("Recommend") + "\">!</a> ";
			commentRX += "<a href=\"azoth://msgeditreplace/!%20%23\\1/\\2%20\" title=\"" +
				tr ("Recommend") + "\">!</a> ";
		}
		if (showAddToBookmarkButton)
		{
			postRX += "<a href=\"azoth://msgeditreplace/~%20%23\\1%20\" title=\"" +
				tr ("Add to bookmarks") + "\">~</a> ";
			commentRX += "<a href=\"azoth://msgeditreplace/~%20%23\\1%20\" title=\"" +
				tr ("Add to bookmarks") + "\">~</a> ";
		}
		if (showBlockButton)
		{
			userRX += " <a href=\"azoth://msgeditreplace/BL%20%40\\1\" title=\"" +
				tr ("Block user") + "\">BL</a>";
			postAuthorRX += " <a href=\"azoth://msgeditreplace/BL%20%40\\1\" title=\"" +
				tr ("Block user") + "\">BL</a>";
			postByUserRX += " <a href=\"azoth://msgeditreplace/BL%20%40\\1\" title=\"" +
				tr ("Block user") + "\">BL</a>";
		}
		if (showPrivateMessageButton)
		{
			postAuthorRX += " <a href=\"azoth://msgeditreplace/P%20%40\\1\" title=\"" +
				tr ("Send private message to user") + "\">P</a> ";
			userRX += " <a href=\"azoth://msgeditreplace/P%20%40\\1\" title=\"" +
				tr ("Send private message to user") + "\">P</a> ";
			postByUserRX += " <a href=\"azoth://msgeditreplace/P%20%40\\1\" title=\"" +
				tr ("Send private message to user") + "\">P</a> ";
		}
		if (showSubscribeButton || showCommentsButton || showRecommendButton || showAddToBookmarkButton)
		{
			postRX += ") ";
			commentRX += ")";
		}
		if (showBlockButton || showPrivateMessageButton)
		{
			userRX += ") ";
			postAuthorRX += ") ";
			postByUserRX += ") ";
		}

		if (showImg)
			body.replace (ImgRX_, imgRX);
		body.replace (PostRX_, postRX);
		body.replace (PostAuthorRX_, postAuthorRX);
		body.replace (UserRX_, userRX);
		body.replace (CommentRX_, commentRX);
		body.replace (PostByUserRX_, postByUserRX);
		while (body.startsWith ("<br />"))
			body = body.mid (6);
		return body;
	}

	void Plugin::hookChatTabCreated (IHookProxy_ptr,
			QObject *chatTab, QObject *entry, QWebView*)
	{
		if (!XmlSettingsManager::Instance ()
				.property ("BindLastID").toBool ())
			return;

		ICLEntry *other = qobject_cast<ICLEntry*> (entry);
		if (!other ||
				!other->GetEntryID ().contains ("psto@psto.net"))
			return;

		QTextEdit *edit;
		QMetaObject::invokeMethod (chatTab,
				"getMsgEdit",
				Q_RETURN_ARG (QTextEdit*, edit));
		connect (chatTab,
				SIGNAL (destroyed ()),
				this,
				SLOT (handleChatDestroyed ()));

		QShortcut *sh = new QShortcut (QString ("Ctrl+Shift+P"), qobject_cast<QWidget*> (chatTab));
		sh->setProperty ("Azoth/p100q/Tab", QVariant::fromValue<QObject*> (chatTab));
		connect (sh,
				SIGNAL (activated ()),
				this,
				SLOT (handleShortcutActivated ()));

		Entry2Tab_ [entry] = chatTab;
	}

	void Plugin::hookFormatBodyEnd (IHookProxy_ptr proxy,
			QObject *msgObj)
	{
		IMessage *msg = qobject_cast<IMessage*> (msgObj);
		if (msg->GetDirection () != IMessage::DIn ||
				msg->GetMessageType () != IMessage::MTChatMessage)
			return;

		ICLEntry *other = qobject_cast<ICLEntry*> (msg->OtherPart ());
		if (!other)
		{
			qWarning () << Q_FUNC_INFO
					<< "NULL other part for message"
					<< msgObj
					<< msg->GetBody ();
			return;
		}

		if (!other->GetEntryID ().contains ("psto@psto.net"))
			return;

		const QString& prevBody = proxy->GetValue ("body").toString ();
		proxy->SetValue ("body", FormatBody (prevBody));

		if (!XmlSettingsManager::Instance ()
				.property ("BindLastID").toBool ())
			return;

		const int aPos = PostByUserRX_.lastIndexIn (prevBody);
		const int pPos = PostRX_.lastIndexIn (prevBody);
		const int cPos = CommentRX_.lastIndexIn (prevBody);
		QRegExp rx;
		if (aPos > pPos && aPos > cPos)
			rx = PostByUserRX_;
		else if (pPos > cPos && pPos > aPos)
			rx = PostRX_;
		else
			rx = CommentRX_;

		QObject *tab = Entry2Tab_ [msg->OtherPart ()];
		if (rx.capturedTexts ().size () == 2)
			LastPostInTab_ [tab] = rx.cap (1);
		else if (rx.capturedTexts ().size () == 3)
			LastPostInTab_ [tab] = rx.cap (1) + '/' + rx.cap (2);
	}

	void Plugin::handleShortcutActivated ()
	{
		QObject *chat = sender ()->property ("Azoth/p100q/Tab").value<QObject*> ();

		QMetaObject::invokeMethod (chat,
				"appendMessageText",
				Q_ARG (QString, "#" + LastPostInTab_ [chat] + " "));
	}

	void Plugin::handleChatDestroyed ()
	{
		LastPostInTab_.remove (sender ());
		Entry2Tab_.remove (Entry2Tab_.key (sender ()));
	}
}
}
}

LC_EXPORT_PLUGIN (leechcraft_azoth_p100q, LeechCraft::Azoth::p100q::Plugin);
