/***************************************************************************
 *
 * knetworkmanager-wireless_device_tray.cpp - A NetworkManager frontend for KDE
 *
 * Copyright (C) 2005, 2006 Novell, Inc.
 *
 * Author: Helmut Schaa       <hschaa@suse.de>, <helmut.schaa@gmx.de>
 *
 * 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
 *
 **************************************************************************/

// TQt includes
#include <tqevent.h>
#include <tqvbox.h>
#include <tqlayout.h>
#include <tqpushbutton.h>
#include <tqbitmap.h>
#include <tqimage.h>
#include <tqpixmap.h>
#include <tqpixmapcache.h>
#include <tqpainter.h>
#include <tqstyle.h>
#include <tqstring.h>
#include <tqguardedptr.h>

// KDE includes
#include <kdebug.h>
#include <kiconloader.h>
#include <tdelocale.h>
#include <knotifyclient.h>

// QT DBus
#include <tqdbuserror.h>
#include <tqdbusobjectpath.h>

// KNM includes
#include "knetworkmanager-wireless_device_tray.h"
#include "knetworkmanager-wireless_device.h"
#include "knetworkmanager-wireless_menuitem.h"
#include "knetworkmanager-wireless_network.h"
#include "knetworkmanager-accesspoint.h"
#include "knetworkmanager-menu_subhead.h"
#include "knetworkmanager-connection_store.h"
#include "knetworkmanager-wireless_connection.h"
#include "knetworkmanager-connection_setting_info.h"
#include "knetworkmanager-connection_setting_wireless.h"
#include "knetworkmanager-connection_setting_wireless_security.h"
#include "knetworkmanager-nm_proxy.h"
#include "knetworkmanager-wireless_manager.h"
#include "knetworkmanager-connection_settings_dialog.h"

using namespace ConnectionSettings;

extern char use_new_wireless_essid;
extern TQByteArray new_wireless_essid;

class WirelessDeviceTrayPrivate
{
	public:
		WirelessDeviceTrayPrivate() :dev(0), activeAccessPoint(0) { }
		~WirelessDeviceTrayPrivate() {}

		WirelessDevice* dev;
		TQGuardedPtr<AccessPoint> activeAccessPoint;
};

TQStringList WirelessDeviceTray::getToolTipText()
{
	TQStringList tooltip = DeviceTrayComponent::getToolTipText();

	AccessPoint * ap = d->dev->getActiveAccessPoint();
	if (ap)
		tooltip.append(i18n("Network: %1").arg(ap->getDisplaySsid()));

	return tooltip;
}

void WirelessDeviceTray::newConnection()
{
	// create a new wireless connection
	Connection* conn = new WirelessConnection();

	// open a dialog for editing the connection
	use_new_wireless_essid = 0;	// deactivate autofill for now
	ConnectionSettingsDialogImpl* dlg = new ConnectionSettingsDialogImpl(conn, true, NULL, tray(), "connect_something", false, TQt::WDestructiveClose);
	dlg->show();
}

bool WirelessDeviceTray::findMatchingNetwork(const WirelessConnection* conn, const TQValueList<WirelessNetwork>& nets, WirelessNetwork& net)
{
	Wireless* wireless = conn->getWirelessSetting();
	WirelessSecurity* security = conn->getWirelessSecuritySetting();

	if (!wireless && !security)
		return false;

	for (TQValueList<WirelessNetwork>::ConstIterator it = nets.begin(); it != nets.end(); ++it)
	{
		if (wireless->getEssid() == (*it).getSsid())
		{
			net = *it;
			return true;
		}	
	}
	return false;
}

WirelessConnection* WirelessDeviceTray::findMatchingConnection(const WirelessNetwork& net, const TQValueList<WirelessConnection*>& connections)
{
	// try to find a connection matching this network
	for (TQValueList<WirelessConnection*>::ConstIterator it = connections.begin(); it != connections.end(); ++it)
	{
		Wireless* wireless = (*it)->getWirelessSetting();
		WirelessSecurity* security = (*it)->getWirelessSecuritySetting();

		// should not happen but its ever better to check
		if (!wireless || !security)
			continue;

		if (wireless->getEssid() == net.getSsid())
		{
			return *it;
		}
	}

	return NULL;
}

void WirelessDeviceTray::addWirelessNetworks(TDEPopupMenu* menu)
{
	printf("Updating wireless network list\n");

	// get all wireless networks
	TQValueList<WirelessNetwork> nets = WirelessManager::getWirelessNetworks(d->dev);

	// get all wireless connections
	TQValueList<WirelessConnection*> conns = WirelessManager::getWirelessConnections();

	// get the currently active connection
	NMProxy* nm = NMProxy::getInstance();
	Connection* active_conn = nm->getActiveConnection(d->dev);
	if (active_conn)
		kdDebug() << active_conn->getObjectPath().ascii() << endl;

	// add all wireless connections in range
	// (we may have more then one connection per network)
	for (TQValueList<WirelessConnection*>::iterator it = conns.begin(); it != conns.end(); ++it)
	{
		WirelessNetworkItem* wirelessNetworkItem;
		WirelessNetwork net;

		// only show connections which are in range
		if ( !findMatchingNetwork(*it, nets, net) )
			continue;

		wirelessNetworkItem = new WirelessNetworkItem (menu,
		                            d->dev,
		                            net,
		                            *it,
		                            false);
		int id = menu->insertItem (wirelessNetworkItem, -1, -1);	
		menu->setItemChecked(id, ((Connection*)(*it) == active_conn));
		menu->connectItem(id, wirelessNetworkItem, TQT_SLOT(slotActivate()));
	}

	// now add all connections which are not in range to a submenu
	TQPopupMenu* popup = new TQPopupMenu(menu);

	uint networkItemsAdded = 0;
	for (TQValueList<WirelessConnection*>::iterator it = conns.begin(); it != conns.end(); ++it)
	{
		WirelessNetworkItem* wirelessNetworkItem;
		WirelessNetwork net;

		// only show connections which are out of range
		if ( findMatchingNetwork(*it, nets, net) )
			continue;

		Info* info = (*it)->getInfoSetting();
		Wireless* wireless = (*it)->getWirelessSetting();

		if (!info || !wireless)
			continue;

		wirelessNetworkItem = new WirelessNetworkItem (menu,
		                            d->dev,
		                            net,
		                            *it,
		                            false);

		int id = popup->insertItem (wirelessNetworkItem, -1, -1);	
		popup->connectItem(id, wirelessNetworkItem, TQT_SLOT(slotActivate()));
		networkItemsAdded += 1;
	}
	
	if (networkItemsAdded) {
		menu->insertSeparator();
		menu->insertItem(i18n("Connect to saved network"), popup);
	}

// 	// List available unsaved networks
 	TQPopupMenu* newpopup = new TQPopupMenu(menu);
 	WirelessConnection* newconn;
 	uint newnetworkItemsAdded = 0;
 	TQValueList<WirelessNetwork> newnets = WirelessManager::getWirelessNetworks(0, WirelessNetwork::MATCH_SSID);
 
 	for (TQValueList<WirelessNetwork>::Iterator it = newnets.begin(); it != newnets.end(); ++it)
 	{
		// Only display networks with no existing connnection
		if ( findMatchingConnection(*it, conns) != NULL)
			continue;

 		WirelessNetworkItem* wirelessNetworkItem;
		wirelessNetworkItem = new WirelessNetworkItem (menu,
 		                            d->dev,
 		                            *it,
                                            NULL,
 		                            false);
 
 		int id = newpopup->insertItem (wirelessNetworkItem, -1, -1);
		newpopup->connectItem(id, this, TQT_SLOT(newConnection()));
		
 		newnetworkItemsAdded += 1;
 	}
 
	if (newnetworkItemsAdded) {
 		menu->insertSeparator();
 		menu->insertItem(i18n("Connect to new network"), newpopup);
 	}
	// Signal done with wireless menu
	//menu->insertSeparator();
}

void WirelessDeviceTray::addMenuItems(TDEPopupMenu* menu)
{
	NMProxy* nm = NMProxy::getInstance();
	TQT_DBusError err;

	// device title
	Subhead* subhead = new Subhead (menu, "subhead", TQString("Wireless Connection (%1)").arg(d->dev->getInterface()), SmallIcon("wireless", TQIconSet::Automatic));
	menu->insertItem (subhead, -1, -1);

    // bolding subhead instead
	//menu->insertSeparator();

	if (!nm->getWirelessEnabled(err))
	{
		// wireless disabled -> do not show any connections
		subhead = new Subhead(menu, "subhead2", i18n("Wireless disabled"), SmallIcon("no", TQIconSet::Automatic));
		menu->insertItem(subhead, -1, -1);
	}
	else if (!nm->getWirelessHardwareEnabled(err))
	{
		// wireless disabled -> do not show any connections
		subhead = new Subhead(menu, "subhead2", i18n("Wireless disabled by Killswitch"), SmallIcon("no", TQIconSet::Automatic));
		menu->insertItem(subhead, -1, -1);
	}
	else
	{
		// networks
		addWirelessNetworks(menu);

		// bring the device down
		TDEAction* deactivate = tray()->actionCollection()->action("deactivate_device");
		if (deactivate)
			deactivate->plug(menu);
	}
	menu->insertSeparator();
}

void WirelessDeviceTray::slotUpdateDeviceState(NMDeviceState state)
{
    slotCheckActiveAccessPoint();
	if (state == NM_DEVICE_STATE_ACTIVATED)
	{
		// trigger an update of the connections seen bssids property

		AccessPoint * ap = d->dev->getActiveAccessPoint();
		if (ap) {
			int strength = ap->getStrength();

			if (strength > 80)
				setPixmapForState((NMDeviceState)state, "nm_signal_100");
			else if (strength > 55)
				setPixmapForState((NMDeviceState)state, "nm_signal_75");
			else if (strength > 30)
				setPixmapForState((NMDeviceState)state, "nm_signal_50");
			else if (strength > 5)
				setPixmapForState((NMDeviceState)state, "nm_signal_25");
			else
				setPixmapForState((NMDeviceState)state, "nm_signal_00");
		}
	}
}

void WirelessDeviceTray::slotCheckActiveAccessPoint()
{
	// the active AP changed, if a connection is already active we have roamed
	// thus add the bssid to the list of seen bssids
	NMProxy* nm = NMProxy::getInstance();
	if (!nm)
		return;

	WirelessConnection* active_conn = dynamic_cast<WirelessConnection*>(nm->getActiveConnection(d->dev));
	if (active_conn && d->dev->getState() == NM_DEVICE_STATE_ACTIVATED)
	{
		if ( d->dev->getActiveAccessPoint() != d->activeAccessPoint) {
			if (!d->activeAccessPoint.isNull())
				disconnect( d->activeAccessPoint, TQT_SIGNAL(strengthChanged(TQ_UINT8)), this, TQT_SLOT(apStrengthChanged(TQ_UINT8)));

			d->activeAccessPoint = d->dev->getActiveAccessPoint();
			if ( d->activeAccessPoint ) {
				connect( d->activeAccessPoint, TQT_SIGNAL(strengthChanged(TQ_UINT8)), this, TQT_SLOT(apStrengthChanged(TQ_UINT8)));
				ConnectionSettings::Wireless* wireless = active_conn->getWirelessSetting();
				wireless->addSeenBssid(d->activeAccessPoint->getHwAddress());
			}
		}
	}
}

void WirelessDeviceTray::apStrengthChanged(TQ_UINT8 strength)
{
    kdDebug() << k_funcinfo << (uint)strength << endl;
    NMDeviceState state = device()->getState();
    if (strength > 80)
        setPixmapForState(state, "nm_signal_100");
    else if (strength > 55)
        setPixmapForState(state, "nm_signal_75");
    else if (strength > 30)
        setPixmapForState(state, "nm_signal_50");
    else if (strength > 5)
        setPixmapForState(state, "nm_signal_25");
    else
        setPixmapForState(state, "nm_signal_00");
    emit uiUpdated();
}

void WirelessDeviceTray::slotAccessPointAdded(AccessPoint* ap)
{
	KNotifyClient::event( tray()->winId(), "knm-nm-network-found", i18n("KNetworkManager New Wireless Network Found") );
}

void WirelessDeviceTray::slotAccessPointRemoved(const TQString&)
{
	KNotifyClient::event( tray()->winId(), "knm-nm-network-gone", i18n("KNetworkManager Wireless Network Disappeared") );
}

WirelessDeviceTray::WirelessDeviceTray (WirelessDevice* dev, KSystemTray * parent, const char * name)
	: DeviceTrayComponent (dev, parent, name)
{
	d = new WirelessDeviceTrayPrivate();
	d->dev = dev;

	// we want other icons for wireless devices
	setPixmapForState(NM_DEVICE_STATE_UNKNOWN, "wireless_off");
	setPixmapForState(NM_DEVICE_STATE_UNAVAILABLE, "wireless_off");
	setPixmapForState(NM_DEVICE_STATE_UNMANAGED, "wireless_off");
	setPixmapForState(NM_DEVICE_STATE_DISCONNECTED, "wireless");
	setPixmapForState(NM_DEVICE_STATE_ACTIVATED, "nm_signal_50");

	// get notified when the device state changes
	connect(dev, TQT_SIGNAL(StateChanged(NMDeviceState)), this, TQT_SLOT(slotUpdateDeviceState(NMDeviceState)));

	// if the active access point changed but not the connection we roamed to a new AP
	connect(dev, TQT_SIGNAL(propertiesChanged()), this, TQT_SLOT(slotCheckActiveAccessPoint()));

	// get notified of all AP changes
	connect(dev, TQT_SIGNAL(accessPointAdded(AccessPoint*)), this, TQT_SLOT(slotAccessPointAdded(AccessPoint*)));
	connect(dev, TQT_SIGNAL(accessPointRemoved(const TQString&)), this, TQT_SLOT(slotAccessPointRemoved(const TQString&)));
}

WirelessDeviceTray::~WirelessDeviceTray ()
{
	delete d;
}


#include "knetworkmanager-wireless_device_tray.moc"
