/***************************************************************************
 *   Copyright (C) 2005 by Debajyoti Bera                                  *
 *   dbera.web@gmail.com                                                   *
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "beaglesearch.h"
#include <kmessagebox.h>
#include <kdebug.h>
#include <kurl.h>
#include <klocale.h>

BeagleSearch::BeagleSearch(QObject *parent, const char *name)
    : QObject(parent)
{
   setObjectName( name );
}

BeagleSearch::~BeagleSearch()
{
}

BeagleSearch *BeagleSearch::current_instance = NULL;

// It would be much easier if C++ could access C style enum-ints
int domains[4] = {BEAGLE_QUERY_DOMAIN_LOCAL,
    	 BEAGLE_QUERY_DOMAIN_SYSTEM,
    	 BEAGLE_QUERY_DOMAIN_NEIGHBORHOOD,
    	 BEAGLE_QUERY_DOMAIN_GLOBAL};

void set_query_domain (BeagleQuery *query, uint domain)
{
	if (domain != 0) {
	    for (int i=0; i < 4; ++i) {
		if ((domain & domains[i]) != 0)
		    beagle_query_add_domain(query, (BeagleQueryDomain) domains[i]);
		else
		    beagle_query_remove_domain(query, (BeagleQueryDomain) domains[i]);
	    }
	}

}

void BeagleSearch::search(QString& searchstr, int num_results, uint domain)
{
		BeagleClient   *client;
		BeagleQuery    *query;
		GMainLoop      *main_loop;
		
		kDebug() << "----------- query string:" << searchstr << " max=" << num_results << " domain=" << domain << endl;
		
		if (searchstr.isEmpty ()) {
			//emit oops_error ("beagle doesnt like finding nothing :-X");
			return;
		}

		g_type_init ();
		
		client = beagle_client_new (NULL);
		if (client == NULL) {
			emit oops_error (i18n ("Beagle search service is not running."));
			//kDebug() << "------------ beagled not running" << endl;
			return;
		}
		
		main_loop = g_main_loop_new (NULL, FALSE);
		
		query = beagle_query_new ();
		beagle_query_add_text (query, searchstr.toUtf8 ());

		// add domain and num_results information
		set_query_domain(query, domain);
		if (num_results > 0)
		    beagle_query_set_max_hits(query, num_results);

		// don't search documentation, IMLog
		beagle_query_add_text (query, "-filetype:documentation -type:IMlog");

		GError *err = NULL;
		current_instance = this; // store for callbacks
		
		g_signal_connect (query, "hits-added",
			  G_CALLBACK (BeagleSearch::hits_added_cb),
			  client);

		g_signal_connect (query, "finished",
			  G_CALLBACK (BeagleSearch::finished_cb),
			  main_loop);
	
		beagle_client_send_request_async (client, BEAGLE_REQUEST (query),
					  &err);
		if (err) {
			emit oops_error (i18n ("Error in querying beagle search service."));
			kDebug() << "-------- err:" << err->message << endl;
			g_error_free (err);
			return;
		}
		
		g_main_loop_run (main_loop);
		g_object_unref (query);
		g_object_unref (client);
		g_main_loop_unref (main_loop);    
}

static QString beagleHitGetProperty (BeagleHit *hit, QString name)
{
	const char *value;
	gboolean ret = beagle_hit_get_one_property (hit, name.toUtf8 (), &value);
	if (! ret)
		return QString::null;
	else
		return QString::fromUtf8 (value);
}

void BeagleSearch::hits_added_cb (BeagleQuery *query, BeagleHitsAddedResponse *response) 
{
	GSList *hits, *l;
	gint    i;
	gint    nr_hits;
	BeagleResultList results;
	
	hits = beagle_hits_added_response_get_hits (response);
	nr_hits = g_slist_length (hits);
	kDebug() << "---------- hits added:" << nr_hits << endl;

	QMap<QString, bool> uri_map;

	for (l = hits, i = 1; l; l = l->next, ++i) {
		BeagleHit *hit = BEAGLE_HIT (l->data);
		
		QString hittype = QString::fromUtf8 (beagle_hit_get_type (hit));
		QString hitsource = QString::fromUtf8 (beagle_hit_get_source (hit));
		QString hitmimetype = QString::fromUtf8 (beagle_hit_get_mime_type (hit));

		//kDebug () << "type:" << hittype << " source:" << hitsource << " mimetype:" << hitmimetype << endl;
		// if hit source is None, dont handle it. Might be anthrax-envelope :)
		// also dont handle IMLogs and other offensives which we cant handle
		if (hitsource.isNull () || hitsource == "documentation" || hittype == "IMLog")
		    continue;
		
		beagle_result_struct *result = new beagle_result_struct;

		// sure shots
		if (hittype == "FeedItem") {
			result->uri = beagleHitGetProperty (hit, "dc:identifier");
			result->title = beagleHitGetProperty (hit, "dc:title");
			result->mime_type = "text/html";
			result->type = Link;

		} else if (hittype == "WebHistory" || hittype == "Bookmark") {
			result->uri = QString::fromUtf8 (beagle_hit_get_uri (hit));
			result->title = beagleHitGetProperty (hit, "dc:title");
			if (result->title.isEmpty () || result->title.trimmed().isEmpty()) {
				KUrl url (result->uri);
				result->title = url.prettyUrl ();
			}
			result->mime_type = "text/html";
			result->type = Link;

		} else if (hittype == "MailMessage") {
			QString prefix = QString::null;
			bool is_attachment = 
				(beagleHitGetProperty (hit, "prefix:fixme:hasAttachments") == "true");
			bool has_parent = (! QString::fromUtf8 (beagle_hit_get_parent_uri (hit)).isEmpty ());
			bool parent_mbox_file = false;
			if (has_parent)
				parent_mbox_file =
					(beagleHitGetProperty (hit, "parent:fixme:folder") == QString::null);
		
			// Logic:
			// If has_parent == false, everything is normal
			// If has_parent == true, parent_mbox_file == false, everything is normal, use uri
			// FIXME: If has_parent == true, parent_mbox_file == true, ???
			// If has_parent == true, is_attachment == true, hit is attach and access with prefix "parent:", use parenturi
			// Else, not attachment (multipart), access with prefix "parent:", use parenturi

			if (has_parent && !parent_mbox_file) {
				result->uri = QString::fromUtf8 (beagle_hit_get_parent_uri (hit));
				prefix = "parent:";
				if (is_attachment)
					result->title = beagleHitGetProperty (hit, "fixme:attachment_title");
				if (result->title.isEmpty ())
					result->title = beagleHitGetProperty (hit, prefix + "dc:title");
				if (result->title.isEmpty ())
					result->title = "No subject";
				
				if (is_attachment)
					result->title = result->title.prepend ("(Attachment) ");
			} else {
				result->uri = QString::fromUtf8 (beagle_hit_get_uri (hit));
				result->title = beagleHitGetProperty (hit, "dc:title");
			}

			if (hitsource == "GMailSearch") {
				result->mime_type = "text/html";
				result->type = Link;
			} else {
				result->mime_type = "message/rfc822"; // to handle attachment results
				result->type = File;
			}

		} else if (hittype == "File" && beagleHitGetProperty (hit, "beagle:FilenameExtension") == ".desktop") {
			result->uri = QString::fromUtf8 (beagle_hit_get_uri (hit));
			result->title = beagleHitGetProperty (hit, "dc:title");
			if (result->title.isEmpty ())
				result->title = beagleHitGetProperty (hit, "fixme:name");
			if (result->title.isEmpty ())
				result->title = beagleHitGetProperty (hit, "beagle:Filename");
			result->mime_type = hitmimetype;
			result->type = File;

		} else if (hittype == "File" && beagleHitGetProperty (hit, "fixme:inside_archive") == "true") {
			result->uri = QString::fromUtf8 (beagle_hit_get_uri (hit));
			result->title = beagleHitGetProperty (hit, "beagle:ExactFilename"); // Use FileName for archive entries
			result->title += QString("(Inside %1)").arg(beagleHitGetProperty(hit, "parent:beagle:ExactFilename"));
			//result->mime_type = hitmimetype; // dont set the mimetype for archive contents
			result->type = File;

		} else if (hittype == "File") {
			if (hitmimetype == "inode/directory")
				result->type = Directory;
			else
				result->type = File;
			result->uri = QString::fromUtf8 (beagle_hit_get_uri (hit));
			KUrl url (result->uri);
			result->title = url.path ();
			result->mime_type = hitmimetype;

		} else {
			delete result;
			continue;
		}

		if (uri_map.contains (result->uri)) {
			delete result;
			continue;
		} else
			uri_map [result->uri] = true;
		
		results.append (*result);
	}
	
	if (current_instance != NULL)
		current_instance->gotOutput(results);
}

void BeagleSearch::finished_cb (BeagleQuery            *query,
	     BeagleFinishedResponse *response, 
	     GMainLoop              *main_loop)
{
	g_main_loop_quit (main_loop);
	if (current_instance != NULL)
		current_instance->finishedSearch();
}

void BeagleSearch::finishedSearch()
{
   emit finished();
}

void BeagleSearch::gotOutput(BeagleResultList &items)
{
	kDebug() << "-------- gotOutput has " << items.count() << " results" << endl;
	emit found(items);
}

#include "beaglesearch.moc"
