/***************************************************************************
 *   Copyright (C) 2005 by Joris Guisson <joris.guisson@gmail.com>         *
 *   Copyright (C) 2007 by Modestas Vainius <modestas@vainius.eu>          *
 *                                                                         *
 *   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.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.          *
 ***************************************************************************/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <tdelocale.h>
#include <tdeglobal.h>
#include <kiconloader.h>
#include <kstandarddirs.h>
#include <tdemessagebox.h>
#include <tdesocketaddress.h>
#include <tqpoint.h>
#include <tqlistview.h>
#include <tdepopupmenu.h>
#include <interfaces/peerinterface.h>
#include <interfaces/functions.h>
#include <torrent/ipblocklist.h>
#include <util/log.h>
#include "config.h"

#ifdef USE_SYSTEM_GEOIP
#include <GeoIP.h>
#else
#include "GeoIP.h"
#endif
#include "peerview.h"
#include "flagdb.h"

using namespace bt;
using namespace kt;

namespace kt
{
	
	Uint32 PeerViewItem::pvi_count = 0;
	// Global GeoIP pointer, gets destroyed when no PeerViewItem's exist
	static GeoIP* geo_ip = 0; 
	static TQPixmap yes_pix;
	static TQPixmap no_pix;
	static TQPixmap lock_pix;
	static FlagDB flagDB(22, 18);
	static bool yes_no_pix_loaded = false;
	static bool geoip_db_exists = true;
	static TQString geoip_data_file;
		
	PeerViewItem::PeerViewItem(PeerView* pv,kt::PeerInterface* peer) : TDEListViewItem(pv),peer(peer)
	{	
		if (!yes_no_pix_loaded)
		{
			TDEIconLoader* iload = TDEGlobal::iconLoader();
			/* Prefer builtin flag images to the ones provided by KDE */
			flagDB.addFlagSource("data",  TQString("ktorrent/geoip/%1.png"));
			flagDB.addFlagSource("locale", TQString("l10n/%1/flag.png"));
			yes_pix = iload->loadIcon("button_ok",TDEIcon::Small);
			no_pix = iload->loadIcon("button_cancel",TDEIcon::Small);
			lock_pix = iload->loadIcon("ktencrypted",TDEIcon::Small);
#ifdef USE_SYSTEM_GEOIP
			geo_ip = GeoIP_open_type(GEOIP_COUNTRY_EDITION, GEOIP_STANDARD);
			geoip_db_exists = (geo_ip != NULL);
#else
			geoip_db_exists = !locate("data", "ktorrent/geoip/geoip.dat").isNull(); 
			if(geoip_db_exists) {
				geoip_data_file = "ktorrent/geoip/geoip.dat";
			} else {
				geoip_db_exists = !locate("data", "ktorrent/geoip/GeoIP.dat").isNull();
				if (geoip_db_exists)
					geoip_data_file = "ktorrent/geoip/GeoIP.dat";
			}
#endif
			yes_no_pix_loaded = true;
		}
		
		pvi_count++;
		const char * hostname = 0;
		const char * country_code = 0;
		const char * country_name = 0;
		int country_id = 0;

		const PeerInterface::Stats & s = peer->getStats();
		hostname = s.ip_address.ascii();

		// open GeoIP if necessary
		if (!geo_ip && geoip_db_exists) {
#ifdef USE_SYSTEM_GEOIP
			geo_ip = GeoIP_open_type(GEOIP_COUNTRY_EDITION, GEOIP_STANDARD);
#else
			geo_ip = GeoIP_open(locate("data", geoip_data_file).ascii(),0);
#endif
		}
		if (geo_ip)
		{
			country_id = GeoIP_id_by_name(geo_ip, hostname);
			country_code = GeoIP_country_code[country_id];
			country_name = GeoIP_country_name[country_id];
			setText(1, country_name);
			m_country = TQString(country_name);
		}
		else
		{
			setText(1,"N/A");
		}
		
	/*	if (s.fast_extensions)
			setText(0,s.ip_address + " (F)");
		else*/
			setText(0,s.ip_address);
			
		struct in_addr addr = {0};
		inet_aton(s.ip_address.ascii(),&addr);
		ip = ntohl(addr.s_addr);
			
		setText(2,s.client);
		
		if (country_code)
		{
			setPixmap(1, flagDB.getFlag(country_code));
		}
		
		if (s.encrypted)
			setPixmap(0,lock_pix);
		update();
	}
	
	PeerViewItem::~PeerViewItem()
	{
		if (pvi_count > 0) // just to be sure, let this not wrap around
			pvi_count--;
		
		// destroy when not needed anymore
		if (pvi_count == 0 && geo_ip)
		{
			GeoIP_delete(geo_ip);
			geo_ip = 0;
		}
	}
	
	
	void PeerViewItem::update()
	{
		TDELocale* loc = TDEGlobal::locale();
		const PeerInterface::Stats & s = peer->getStats();

		if (s.download_rate >= 103) // lowest "visible" speed, all below will be 0,0 Kb/s
			setText(3,KBytesPerSecToString(s.download_rate / 1024.0));
		else
			setText(3, "");
		if (s.upload_rate >= 103) // lowest "visible" speed, all below will be 0,0 Kb/s
			setText(4,KBytesPerSecToString(s.upload_rate / 1024.0));
		else
			setText(4, "");
		//setPixmap(5,!s.choked ? yes_pix : no_pix);
		setText(5,s.choked ? i18n("Yes") : i18n("No"));
		//setPixmap(6,!s.snubbed ? yes_pix : no_pix);
		setText(6,s.snubbed ? i18n("Yes") : i18n("No"));
		setText(7,TQString("%1 %").arg(loc->formatNumber(s.perc_of_file,2)));
		setPixmap(8,s.dht_support ? yes_pix : no_pix);
		setText(9,loc->formatNumber(s.aca_score,2));
		setPixmap(10,s.has_upload_slot ? yes_pix : TQPixmap());
		setText(11,TQString("%1 / %2").arg(s.num_down_requests).arg(s.num_up_requests));
		setText(12, BytesToString(s.bytes_downloaded));
		setText(13, BytesToString(s.bytes_uploaded));
	}
	
	int PeerViewItem::compare(TQListViewItem * i,int col,bool) const
	{
		PeerViewItem* pvi = (PeerViewItem*) i;
		PeerInterface* op = pvi->peer;
		const PeerInterface::Stats & s = peer->getStats();
		const PeerInterface::Stats & os = op->getStats();
		switch (col)
		{
			case 0: return CompareVal(ip,pvi->ip); // use numeric representation to sort
			//return TQString::compare(s.ip_address,os.ip_address);
			case 1:	return TQString::compare(m_country, pvi->m_country);
			case 2: return TQString::compare(s.client,os.client);
			case 3: return CompareVal(s.download_rate,os.download_rate);
			case 4: return CompareVal(s.upload_rate,os.upload_rate);
			case 5: return CompareVal(s.choked,os.choked);
			case 6: return CompareVal(s.snubbed,os.snubbed);
			case 7: return CompareVal(s.perc_of_file,os.perc_of_file);
			case 8: return CompareVal(s.dht_support,os.dht_support);
			case 9: return CompareVal(s.aca_score,os.aca_score);
			case 10: return CompareVal(s.has_upload_slot,os.has_upload_slot);
			case 11: return CompareVal(s.num_down_requests+s.num_up_requests, os.num_down_requests+os.num_up_requests);
			case 12: return CompareVal(s.bytes_downloaded, os.bytes_downloaded);
			case 13: return CompareVal(s.bytes_uploaded, os.bytes_uploaded);
			
		}
		return 0;
	}
	
	PeerView::PeerView(TQWidget *parent, const char *name)
			: TDEListView(parent, name)
	{
		addColumn(i18n("IP"));
		addColumn(i18n("Country"));
		addColumn(i18n("Client"));
		addColumn(i18n("Down Speed"));
		addColumn(i18n("Up Speed"));
		addColumn(i18n("Choked"));
		addColumn(i18n("Snubbed"));
		addColumn(i18n("Availability"));
		addColumn(i18n("DHT"));
		addColumn(i18n("Score"));
		addColumn(i18n("Upload Slot"));
		addColumn(i18n("Requests"));
		addColumn(i18n("Downloaded"));
		addColumn(i18n("Uploaded"));
		
		setAllColumnsShowFocus(true);
		setShowSortIndicator(true);
		
		setColumnAlignment(3,TQt::AlignRight);
		setColumnAlignment(4,TQt::AlignRight);
		setColumnAlignment(5,TQt::AlignCenter);
		setColumnAlignment(6,TQt::AlignCenter);
		setColumnAlignment(7,TQt::AlignRight);
		setColumnAlignment(8,TQt::AlignCenter);
		setColumnAlignment(9,TQt::AlignRight);
		setColumnAlignment(10,TQt::AlignCenter);
		setColumnAlignment(11,TQt::AlignCenter);
		setColumnAlignment(12,TQt::AlignRight);
		setColumnAlignment(13,TQt::AlignRight);
		
		for (Uint32 i = 0;i < (Uint32)columns();i++)
			setColumnWidthMode(i,TQListView::Manual);
			
		setShowSortIndicator(true);
		
		menu = new TDEPopupMenu(this);
		kick_id = menu->insertItem(TDEGlobal::iconLoader()->loadIcon("delete_user", TDEIcon::NoGroup), i18n("to kick", "Kick peer"));
		ban_id = menu->insertItem(TDEGlobal::iconLoader()->loadIcon("filter",TDEIcon::NoGroup), i18n("to ban", "Ban peer"));
		
		connect(this,TQ_SIGNAL(contextMenu(TDEListView*, TQListViewItem*, const TQPoint& )),
				this,TQ_SLOT(showContextMenu(TDEListView*, TQListViewItem*, const TQPoint& )));
		connect(menu, TQ_SIGNAL ( activated ( int ) ), this, TQ_SLOT ( contextItem ( int ) ) );
		setFrameShape(TQFrame::NoFrame);
	}
	
	
	PeerView::~PeerView()
	{}
	
	void PeerView::addPeer(kt::PeerInterface* peer)
	{
		PeerViewItem* i = new PeerViewItem(this,peer);
		items.insert(peer,i);
	}
	
	void PeerView::removePeer(kt::PeerInterface* peer)
	{
		TQMap<kt::PeerInterface*,PeerViewItem*>::iterator it = items.find(peer);
		if (it == items.end())
		{
			return;
		}

		PeerViewItem* pvi = it.data();
		if (pvi == curr)
			curr = 0;
				  
		delete pvi;
		items.erase(peer);
		
	}
	
	void PeerView::banPeer(kt::PeerInterface* peer)
	{
		if(!peer)
			return;
		
		IPBlocklist& filter = IPBlocklist::instance();
		const PeerInterface::Stats & s = peer->getStats();
		KNetwork::KIpAddress ip(s.ip_address);
		TQString ips = ip.toString();
		/**
		 * @TODO Clean this up.
		 * this whole mess was because of KNetwork classes
		 * since we no longer use them, may I clean it up?
		 * I'll wait some time just in case...
		 **/
		if(ips.startsWith(":"))  
			filter.insert(ips.section(":",-1),3);
		else
			filter.insert(ips,3);
		peer->kill();
	}
	
	void PeerView::kickPeer(kt::PeerInterface* peer)
	{
		if(!peer)
			return;
		
		peer->kill();
	}
	
	void PeerView::update()
	{
		TQMap<kt::PeerInterface*,PeerViewItem*>::iterator i = items.begin();
		while (i != items.end())
		{
			PeerViewItem* it = i.data();
			it->update();
			i++;
		}
		sort();
	}
	
	void PeerView::removeAll()
	{
		items.clear();
		clear();
	}
	
	void PeerView::showContextMenu( TDEListView*, TQListViewItem* item, const TQPoint& p)
	{
		if(!item)
			return;
		
		curr = dynamic_cast<PeerViewItem*>(item);
		if (curr)
		{
			menu->setItemEnabled(ban_id, true);
			menu->setItemEnabled(kick_id, true);
			menu->popup(p);
		}
	}
	
	void PeerView::contextItem(int id)
	{
		if (id == ban_id && curr)
			banPeer(curr->getPeer());
		
		if (id == kick_id && curr)
			kickPeer(curr->getPeer());
	}
}
	
#include "peerview.moc"
