/***************************************************************************
 *
 * tdenetman-tray.cpp - A NetworkManager frontend for TDE
 *
 * Copyright (C) 2012 Timothy Pearson <kb9vqf@pearsoncomputing.net>
 * Copyright (C) 2005, 2006 Novell, Inc.
 *
 * Author: Timo Hoenig        <thoenig@suse.de>, <thoenig@nouse.net>
 *         Will Stephenson    <wstephenson@suse.de>, <wstephenson@kde.org>
 *         Valentine Sinitsyn <e_val@inbox.ru>
 *         Helmut Schaa       <hschaa@suse.de>, <helmut.schaa@gmx.de>
 *         Alexander Naumov   <anaumov@suse.de>, <posix.ru@gmail.com>
 * Author: Timothy Pearson <kb9vqf@pearsoncomputing.net>
 *
 * 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 <tqsignalmapper.h>
#include <tqevent.h>
#include <tqvbox.h>
#include <tqlayout.h>
#include <tqpushbutton.h>
#include <tqpixmap.h>
#include <tqpixmapcache.h>
#include <tqpainter.h>
#include <tqstyle.h>
#include <tqvaluelist.h>
#include <dcopclient.h>
#include <kdebug.h>
#include <kdialogbase.h>
#include <knotifyclient.h>
#include <knotifydialog.h>
#include <tdelocale.h>
#include <kstdguiitem.h>
#include <khelpmenu.h>
#include <kprocess.h>
#include <kiconloader.h>
#include <tdeconfig.h>
#include <tdemessagebox.h>

#include <tqpushbutton.h>
#include <tqlayout.h>
#include <tqlabel.h>
#include <tqapplication.h>
#include <tqdialog.h>
#include <tqfile.h>

#include "devicetraycomponent.h"
#include "tdenetman-tray.h"

#include "tdenetman-connection_settings_dialog.h"
#include "tdenetman-connection_editor.h"
#include "tdenetman-menu_subhead.h"

#include "tdenetman-wired_device_tray.h"
#include "tdenetman-wireless_device_tray.h"
#include "tdenetman-cellular_device_tray.h"
#include "vpn_tray_component.h"

#include <stdio.h>

unsigned int tdenetworkmanager_editor_dialog_count = 0;

TDENetworkConnectionStatus::TDENetworkConnectionStatus nm_device_state_global = TDENetworkConnectionStatus::Invalid;

NewSecretsDialog::NewSecretsDialog(TDENetworkConnection *connection, TQWidget * parent, const char * name, bool modal, TQt::WFlags f)
        : TQDialog(parent, name, modal, f)
{
	_connection = connection;
	init();
}

NewSecretsDialog::~NewSecretsDialog ()
{

}

void NewSecretsDialog::slotDialogEdit()
{
	ConnectionSettingsDialogImpl* dlg = new ConnectionSettingsDialogImpl(_connection, false, TQByteArray(), Tray::getInstance(), "Edit connection");
	dlg->show();
	close();
}

void NewSecretsDialog::reject()
{
	TQDialog::reject();
}

void NewSecretsDialog::init()
{
	TDENetworkConnection* conn = _connection;

	// if we do not have a connection bail out
	if (!conn)
	{
		reject();
		return;
	}

	// show a message to the user that the connection failed
	// and allow edit or cancel

	TQLabel* label = new TQLabel(tr("    The connection %1 could not be established    ").arg(conn->friendlyName), this);
	TQPushButton* buttonEdit = new TQPushButton(tr("&Edit"), this);
	TQPushButton* buttonCancel = new TQPushButton(tr("&Cancel"), this);
	
	TQHBoxLayout *topLeftLayout = new TQHBoxLayout();
	topLeftLayout->addWidget(buttonEdit);
	topLeftLayout->addWidget(buttonCancel);

	TQVBoxLayout *mainLayout = new TQVBoxLayout(this);
	mainLayout->setMargin(15);
	mainLayout->setSpacing(10);
	mainLayout->addWidget(label);
	mainLayout->addLayout(topLeftLayout);

	connect(buttonEdit, TQ_SIGNAL(clicked()), TQ_SLOT(slotDialogEdit()));
	connect(buttonCancel, TQ_SIGNAL(clicked()), this, TQ_SLOT(close()));
}

class TrayPrivate
{
	public:
		TrayPrivate(TQObject* parent)
			: foregroundTrayComponent(0)
		  , signalMapper(parent, "signal_mapper")
		  , current_idx(0)
		{}
		~TrayPrivate() {}

		static Tray* tray;
		TQValueList<TrayComponent*> trayComponents;
		DeviceTrayComponent * foregroundTrayComponent;
		TQSignalMapper signalMapper;
		TQMap<int, TQString> act_conn_map;
		int current_idx;
		KHelpMenu *helpMenu;
};

Tray* TrayPrivate::tray = NULL;

Tray* Tray::getInstance()
{
	if (TrayPrivate::tray)
		return TrayPrivate::tray;
	else return (TrayPrivate::tray = new Tray());
}

void Tray::slotEditConnections()
{
	TDEGlobalNetworkManager* nm = TDEGlobal::networkManager();
	if (!nm) return;
	if (tdenetworkmanager_editor_dialog_count == 0) nm->loadConnectionInformation();

	ConnectionEditorImpl* dlg = new ConnectionEditorImpl(this, NULL, TRUE, (WFlags)TQt::WDestructiveClose);
	dlg->show();
}


void Tray::slotEnableWireless()
{
	TDEGlobalNetworkManager* nm = TDEGlobal::networkManager();
	if (!nm) return;
	nm->enableWiFi(true);
}

void Tray::slotDisableWireless()
{
	TDEGlobalNetworkManager* nm = TDEGlobal::networkManager();
	if (!nm) return;
	nm->enableWiFi(false);
}

void Tray::slotOfflineMode()
{
	TDEGlobalNetworkManager* nm = TDEGlobal::networkManager();
	if (!nm) return;
	nm->enableNetworking(false);
}

void Tray::slotOnlineMode()
{
	TDEGlobalNetworkManager* nm = TDEGlobal::networkManager();
	if (!nm) return;
	nm->enableNetworking(true);
}

void Tray::slotNewVPNConnection()
{
	kdDebug() << k_funcinfo << "Creating new VPN connection";
	// create a new VPN connection
	TDEVPNConnection* conn = new TDEVPNConnection();

	// edit the new connection
	ConnectionSettingsDialogImpl* dlg = new ConnectionSettingsDialogImpl(conn, true, TQByteArray(), this, "connect_something", false, TQt::WDestructiveClose);
	dlg->show();
}

void Tray::slotVPNBannerShow(const TQString& vpnbanner)
{
	kdDebug() << k_funcinfo << "VPN banner: " << vpnbanner;
	KNotifyClient::event(winId(), "knm-nm-vpn-banner", vpnbanner);
}

void Tray::contextMenuAboutToShow (TDEPopupMenu* menu)
{
	TDEGlobalNetworkManager* nm = TDEGlobal::networkManager();

	// clear menu
	menu->clear();

	if (nm && !(nm->backendStatus() & TDENetworkGlobalManagerFlags::BackendUnavailable))
	{
		if (tdenetworkmanager_editor_dialog_count == 0)
		{
			nm->loadConnectionInformation();
		}

		// actions for each Device
		for (TQValueList<TrayComponent*>::Iterator it = d->trayComponents.begin(); it != d->trayComponents.end(); ++it) {
			(*it)->addMenuItems(menu);
		}

		// Submenu title
		Subhead* subhead = new Subhead (menu, "subhead", TQString("Connection Management"), SmallIcon("tdenetworkmanager_disabled", TQIconSet::Automatic));
		menu->insertItem (subhead, -1, -1);

		// New connection
		TDEAction * newConnAction = 0;
		int devices =  d->trayComponents.count();
		if ( devices > 1 ) {
			newConnAction = actionCollection ()->action ("new_connection_menu");
			TDEActionMenu* newConnActionMenu = static_cast<TDEActionMenu*>(newConnAction);
			newConnActionMenu->popupMenu()->clear();
			TQValueList<TrayComponent*>::Iterator it;
			for (it = d->trayComponents.begin(); it != d->trayComponents.end(); ++it) {
				DeviceTrayComponent* dev_comp = dynamic_cast<DeviceTrayComponent*> (*it);
				TDEAction * deviceNewConnAction = 0;
				if (dev_comp) {
					TDENetworkDevice* dev_comp_dev = dynamic_cast<TDENetworkDevice*>(hwdevices->findByUniqueID(dev_comp->device()));
					if (dev_comp_dev)
					{
						TQString actionName = TQString("new_connection_%1").arg(dev_comp_dev->deviceNode());
						TQString menuCaption = TQString("%1").arg(dev_comp_dev->deviceNode());
						if (menuCaption.contains("eth", FALSE) > 0)
						{
							menuCaption = menuCaption.insert(0, "Wired Connection (");
						}
						else if (menuCaption.contains("wlan", FALSE) > 0)
						{
							menuCaption = menuCaption.insert(0, "Wireless Connection (");
						}
						else if (menuCaption.contains("pan", FALSE) > 0)
						{
							menuCaption = menuCaption.insert(0, "Private Area Connection (");
						}
						else
						{
							menuCaption = menuCaption.insert(0, "Unknown Connection (");
						}
						menuCaption = menuCaption.append(")");
						deviceNewConnAction = actionCollection ()->action (actionName.utf8());
						if (!deviceNewConnAction)
						{
							deviceNewConnAction = new TDEAction (menuCaption, 0, (*it), TQ_SLOT(newConnection()), actionCollection(), actionName.utf8());
						}
						newConnActionMenu->insert(deviceNewConnAction);
					}
				}
			}
			// New VPN connection option
			++it;
			TDEAction * deviceNewConnAction = 0;
			TQString menuCaption = "VPN Connection";
			TQString actionName = TQString("new_connection_%1").arg("vpn");
			deviceNewConnAction = new TDEAction (menuCaption, 0, this, TQ_SLOT(slotNewVPNConnection()), actionCollection(), actionName.utf8());
			newConnActionMenu->insert(deviceNewConnAction);
		}
		else if ( devices == 1 )
		{
			newConnAction = actionCollection ()->action ("new_connection");
			TQObject::disconnect( newConnAction, TQ_SIGNAL(activated()) );
			TQObject::connect( newConnAction, TQ_SIGNAL(activated()), d->trayComponents[0], TQ_SLOT(newConnection()));
		}
		if (newConnAction) {
			newConnAction->plug(menu);
		}
	
		// turn things off
		if (nm) {
			TDEActionMenu* disableStuffActionMenu = static_cast<TDEActionMenu*>(actionCollection ()->action ("deactivate_menu") );
			disableStuffActionMenu->popupMenu()->clear();
			TDENetworkConnectionList* map = nm->connections();
			d->act_conn_map.clear();
	
			for (TDENetworkConnectionList::Iterator it = map->begin(); it != map->end(); ++it) {
				TDENetworkConnection* conn = (*it);

				if (!conn) {
					continue;
				}
				TDENetworkConnectionStatus::TDENetworkConnectionStatus status = nm->checkConnectionStatus(conn->UUID);
				if ((status == TDENetworkConnectionStatus::Disconnected)
					|| (status == (TDENetworkConnectionStatus::Disconnected | TDENetworkConnectionStatus::LinkUnavailable))
					|| (status & TDENetworkConnectionStatus::Invalid)
					) {
					continue;
				}

				TQString actionName = TQString("disable_connection_%1").arg(conn->UUID);
				TDEAction * deviceNewConnAction = actionCollection ()->action (actionName.utf8());
				TQString actionText = conn->friendlyName;

				if (!deviceNewConnAction) {
					deviceNewConnAction = new TDEAction (actionText, 0, &d->signalMapper, TQ_SLOT(map()), actionCollection(), actionName.utf8());
				}
				d->signalMapper.setMapping(deviceNewConnAction, d->current_idx);
				d->act_conn_map.insert(d->current_idx, conn->UUID);
				d->current_idx++;
				disableStuffActionMenu->insert(deviceNewConnAction);	
			}

			// disable wireless
			if (nm->wiFiHardwareEnabled()) {
				TDEAction* wireless = NULL;
				if (nm->wiFiEnabled()) {
					wireless = actionCollection ()->action ("disable_wireless");
				} else {
					wireless = actionCollection ()->action ("enable_wireless");
				}
				disableStuffActionMenu->insert(wireless);
			}

			// offline vs. online mode
			TDEAction* switch_mode = NULL;
			if (nm->backendStatus() & TDENetworkGlobalManagerFlags::Sleeping) {
				switch_mode = actionCollection ()->action ("online_mode");
			}
			else {
				switch_mode = actionCollection ()->action ("offline_mode");
			}
			disableStuffActionMenu->insert(switch_mode);

			disableStuffActionMenu->plug(menu);
		}
	}
	else {
		Subhead* subhead = new Subhead (menu, "subhead", i18n("NetworkManager is not running"), SmallIcon("process-stop", TQIconSet::Automatic));
		menu->insertItem (subhead, -1, -1);
	}

	// Notifications
	TDEAction* notif = actionCollection()->action("configure_notifications");
	notif->plug(menu);

	// Connection Editor
	TDEAction* edit = actionCollection ()->action ("edit_connections");
	edit->plug(menu);

	// quit
	menu->insertSeparator();
	menu->insertItem(SmallIcon("help"), i18n("&Help"), d->helpMenu->menu());
	TDEAction* quitAction = actionCollection ()->action (KStdAction::name (KStdAction::Quit));
	if (quitAction) {
		quitAction->plug (menu);
	}
}

void
Tray::setBaseStateIcon(TDENetworkGlobalManagerFlags::TDENetworkGlobalManagerFlags nm_state)
{
	// change tray icon according to NM's state
	if ((nm_state == TDENetworkGlobalManagerFlags::Unknown)
		|| (nm_state & TDENetworkGlobalManagerFlags::Sleeping)
		|| (nm_state & TDENetworkGlobalManagerFlags::EstablishingLink)
		|| (nm_state & TDENetworkGlobalManagerFlags::Disconnected)
		|| (nm_state & TDENetworkGlobalManagerFlags::BackendUnavailable)) {
		setPixmap (m_pixmapCache["tdenetworkmanager_disabled"]);
	}
	else if (nm_state & TDENetworkGlobalManagerFlags::Connected) {
		setPixmap (m_pixmapCache["tdenetworkmanager"]);
	}
}

void
Tray::slotStateChanged(TDENetworkGlobalManagerFlags::TDENetworkGlobalManagerFlags newState, TDENetworkGlobalManagerFlags::TDENetworkGlobalManagerFlags prevState)
{
	setBaseStateIcon(newState);

#ifdef DEBUG_STATE
	printf("NM state: 0x%08x\n", newState);
#endif // DEBUG_STATE

	showActiveDeviceTray();
}

void
Tray::enterEvent (TQEvent* /*e*/)
{
	// show tooltip
	TQToolTip::remove (this);
	TQString tooltip = "";

	// build up the tooltip from all tray components
	for (TQValueList<TrayComponent*>::Iterator it = d->trayComponents.begin(); it != d->trayComponents.end(); ++it)
	{
		TrayComponent* comp = *it;
		if (comp->getToolTipText().isEmpty()) {
			continue;
		}
		if (!tooltip.isEmpty()) {
			tooltip += "\n\n";
		}
		tooltip += comp->getToolTipText().join("\n");
	}
	if (!tooltip.isEmpty()) {
		TQToolTip::add (this, tooltip);
	}
}

void 
Tray::slotSecretsNeeded(TDENetworkConnection* connection, const TQStringList& hints, bool request_new)
{
	// For now assume secrets are already in NM...
}

void Tray::slotAddDeviceTrayComponent(TQString dev)
{
	createDeviceTrayComponent(dev);
}

void Tray::slotRemoveDeviceTrayComponent(TQString dev)
{
	for (TQValueList<TrayComponent*>::Iterator it = d->trayComponents.begin(); it != d->trayComponents.end(); ++it)
	{
		DeviceTrayComponent* dev_comp = dynamic_cast<DeviceTrayComponent*>(*it);
		if (!dev_comp) {
			continue;
		}

		if (dev_comp->device() == dev)
		{
			if (d->foregroundTrayComponent && dev_comp->device() == d->foregroundTrayComponent->device() ) {
				d->foregroundTrayComponent = NULL;
			}

			// remove the appropriate action
			TDENetworkDevice* dev_comp_dev = dynamic_cast<TDENetworkDevice*>(hwdevices->findByUniqueID(dev_comp->device()));
			if (dev_comp_dev) {
				TQString actionName = TQString("new_connection_%1").arg(dev_comp_dev->deviceNode());
				TDEAction * deviceNewConnAction = actionCollection ()->action (actionName.utf8());
				if (!deviceNewConnAction) {
					delete deviceNewConnAction;
					deviceNewConnAction = NULL;
				}
			}

			// remove device_tray and delete it
			d->trayComponents.remove(it);
			delete dev_comp;

			if (contextMenu()->isVisible()) {
				contextMenu()->hide();
			}

			break;
		}
	}
}

void Tray::createDeviceTrayComponent(TQString dev)
{
	bool trayExists = false;

	// check if we have already a trayicon for this device
	for (TQValueList<TrayComponent*>::Iterator it = d->trayComponents.begin(); it != d->trayComponents.end(); ++it)
	{
		DeviceTrayComponent* dev_comp = dynamic_cast<DeviceTrayComponent*> (*it);
		if (dev_comp)
			if (dev_comp->device() == dev)
			{
				trayExists = true;
				break;
			}
	}

	// create the appropriate device tray icon
	if (!trayExists)
	{
		DeviceTrayComponent* devTray = 0;
		TDENetworkDevice* new_tray_dev = dynamic_cast<TDENetworkDevice*>(hwdevices->findByUniqueID(dev));
		TDENetworkConnectionManager* deviceConnMan = (new_tray_dev ? new_tray_dev->connectionManager() : NULL);
		// different tray icons for different device types!
		if (deviceConnMan)
		{
			switch (deviceConnMan->deviceType())
			{
				case TDENetworkDeviceType::WiredEthernet:
					devTray = new WiredDeviceTray(dev, this, "wired_device_tray");
					break;
				case TDENetworkDeviceType::WiFi:
					devTray = new WirelessDeviceTray(dev, this, "wireless_device_tray");
					break;
				case TDENetworkDeviceType::Modem:
					devTray = new CellularDeviceTray(dev, this, "cellular_device_tray");
					break;
				default:
					kdWarning() << k_funcinfo << "UDI: " << dev << " has unknown devicetype: " << deviceConnMan->deviceType() << endl;
			}
		}
		if(devTray)
		{
			connect(devTray, TQ_SIGNAL(needsCenterStage(TrayComponent*, bool)), TQ_SLOT(trayComponentNeedsCenterStage(TrayComponent*, bool)));
			connect(devTray, TQ_SIGNAL(uiUpdated()), TQ_SLOT(trayUiChanged()));
			d->trayComponents.append(devTray);
			//WILLTODO: sort
		}
	}
}

void Tray::createVPNTrayComponent()
{
	bool trayExists = false;

	// check if we have already a trayicon for this device
	for (TQValueList<TrayComponent*>::Iterator it = d->trayComponents.begin(); it != d->trayComponents.end(); ++it)
	{	
		VPNTrayComponent* vpn_comp = dynamic_cast<VPNTrayComponent*> (*it);
		if (vpn_comp)
		{
			trayExists = true;
			break;
		}
	}

	// create the appropriate device tray icon
	if (!trayExists)
	{
		TrayComponent* devTray = new VPNTrayComponent(this, "vpn_device_tray");
		if(devTray)
		{
			d->trayComponents.append(devTray);
			//WILLTODO: sort
		}
	}
}

void Tray::updateDeviceTrays()
{
	// create one tray-icon for each device	
	TDEGenericHardwareList devices = hwdevices->listByDeviceClass(TDEGenericDeviceType::Network);

	// check for newly added devices
	for (TDEGenericHardwareList::iterator it = devices.begin(); it != devices.end(); ++it)
	{
		TDENetworkDevice* dev = dynamic_cast<TDENetworkDevice*>(*it);
		if (dev) {
			createDeviceTrayComponent(dev->uniqueID());
		}
		else {
			kdWarning() << k_funcinfo << "got a NULL-Device" << endl;
		}
	}

	// add the VPN componenet as it is not associated with a device
	createVPNTrayComponent();

	// show an active device if one is present
	showActiveDeviceTray();
}

void Tray::mousePressEvent( TQMouseEvent *e )
{
	if ( !rect().contains( e->pos() ) ) {
		return;
	}
	switch ( e->button() ) {
		case TQt::LeftButton:
			contextMenuAboutToShow(contextMenu());
			contextMenu()->popup(e->globalPos());
			break;
		default:
			KSystemTray::mousePressEvent( e );
			break;
	}
}

void Tray::slotDeactivateConnection(int index)
{
	TDEGlobalNetworkManager* nm = TDEGlobal::networkManager();
	if (!nm) return;

	TDENetworkConnection* conn = nm->findConnectionByUUID(d->act_conn_map[index]);
	if (conn) {
		nm->deactivateConnection(conn->UUID);
	}
}

void Tray::connectTrayDeviceManager() {
	if (d->foregroundTrayComponent) {
		TDENetworkDevice* foreground_tray_dev = dynamic_cast<TDENetworkDevice*>(hwdevices->findByUniqueID(d->foregroundTrayComponent->device()));
		if (foreground_tray_dev) {
			TDENetworkConnectionManager* deviceConnMan = foreground_tray_dev->connectionManager();
			if (deviceConnMan) {
				slotUpdateDeviceState(deviceConnMan->deviceStatus().statusFlags, d->foregroundTrayComponent->lastKnownState, TQString());
				connect(deviceConnMan, TQ_SIGNAL(networkDeviceStateChanged(TDENetworkConnectionStatus::TDENetworkConnectionStatus, TDENetworkConnectionStatus::TDENetworkConnectionStatus, TQString)), this, TQ_SLOT(slotUpdateDeviceState(TDENetworkConnectionStatus::TDENetworkConnectionStatus, TDENetworkConnectionStatus::TDENetworkConnectionStatus, TQString)));
			}
		}
	}
}

void Tray::disconnectTrayDeviceManager() {
	if (d->foregroundTrayComponent) {
		TDENetworkDevice* foreground_tray_dev = dynamic_cast<TDENetworkDevice*>(hwdevices->findByUniqueID(d->foregroundTrayComponent->device()));
		if (foreground_tray_dev) {
			TDENetworkConnectionManager* deviceConnMan = foreground_tray_dev->connectionManager();
			if (deviceConnMan) {
				slotUpdateDeviceState(deviceConnMan->deviceStatus().statusFlags, d->foregroundTrayComponent->lastKnownState, TQString());
				disconnect(deviceConnMan, TQ_SIGNAL(networkDeviceStateChanged(TDENetworkConnectionStatus::TDENetworkConnectionStatus, TDENetworkConnectionStatus::TDENetworkConnectionStatus, TQString)), this, 0);
			}
		}
	}
}

void Tray::updateTrayDeviceManagerState() {
	if (d->foregroundTrayComponent) {
		TDENetworkDevice* foreground_tray_dev = dynamic_cast<TDENetworkDevice*>(hwdevices->findByUniqueID(d->foregroundTrayComponent->device()));
		if (foreground_tray_dev) {
			TDENetworkConnectionManager* deviceConnMan = foreground_tray_dev->connectionManager();
			if (deviceConnMan) {
				slotUpdateDeviceState(deviceConnMan->deviceStatus().statusFlags, d->foregroundTrayComponent->lastKnownState, TQString());
			}
		}
	}
}

void Tray::trayComponentNeedsCenterStage(TrayComponent *component, bool needsIt)
{
	TDEGlobalNetworkManager* nm = TDEGlobal::networkManager();

	DeviceTrayComponent * dtc = dynamic_cast<DeviceTrayComponent*>(component);
	if (nm && dtc)
	{
		TDENetworkDevice* dtc_comp_dev = dynamic_cast<TDENetworkDevice*>(hwdevices->findByUniqueID(dtc->device()));
		kdDebug()
			<< k_funcinfo
			<< (dtc_comp_dev ? dtc_comp_dev->deviceNode() : TQString("<unknown device>"))
			<< " : " << needsIt
			<< endl;
		if (needsIt) {
			if (d->foregroundTrayComponent) {
				disconnectTrayDeviceManager();
			}
			d->foregroundTrayComponent = dtc;
			connectTrayDeviceManager();
		}
		else {
			disconnectTrayDeviceManager();
			d->foregroundTrayComponent = NULL;

			// use active default
			TQStringList defaultDevices = nm->defaultNetworkDevices();
			if (defaultDevices.count() > 0) {
				// identify the new foreground
				for (TQValueList<TrayComponent*>::Iterator it = d->trayComponents.begin(); it != d->trayComponents.end(); ++it) {
					DeviceTrayComponent* newDtc = dynamic_cast<DeviceTrayComponent*> (*it);
					if ( newDtc && newDtc->device() == defaultDevices[0] ) {
						d->foregroundTrayComponent = newDtc;
						break;
					}
				}
				if (d->foregroundTrayComponent) {
					TDENetworkDevice* newDtc_dev = dynamic_cast<TDENetworkDevice*>(hwdevices->findByUniqueID(d->foregroundTrayComponent->device()));
					kdDebug()
						<< "  Device "
						<< (dtc_comp_dev ? dtc_comp_dev->deviceNode() : TQString("<unknown device>"))
						<< " background, new foreground device: "
						<< (newDtc_dev ? newDtc_dev->deviceNode() : TQString("<unknown device>"))
						<< endl;
					connectTrayDeviceManager();
					updateTrayDeviceManagerState();
				}
			}

			// Make sure a relevant foregroundTrayComponent is shown at all times...
			showActiveDeviceTray();
		}
	}
}

void Tray::slotUpdateDeviceState()
{
	// FIXME
}

void Tray::slotUpdateDeviceState(TDENetworkConnectionStatus::TDENetworkConnectionStatus newState, TDENetworkConnectionStatus::TDENetworkConnectionStatus prevState, TQString deviceNode)
{
#ifdef DEBUG_STATE
	printf("Device state: 0x%08x was: 0x%08x\n", newState, prevState);
#endif // DEBUG_STATE

	if (newState != prevState) {
		if (d->foregroundTrayComponent) {
			d->foregroundTrayComponent->lastKnownState = newState;
		}

		updateTrayIcon(newState);
		updateActiveConnection(newState);
	}
}

void Tray::trayUiChanged()
{
	DeviceTrayComponent * dtc = d->foregroundTrayComponent;
	if (dtc) {
		TDENetworkDevice* dtc_comp_dev = dynamic_cast<TDENetworkDevice*>(hwdevices->findByUniqueID(dtc->device()));
		if (dtc_comp_dev)
		{
			TDENetworkConnectionManager* deviceConnMan = dtc_comp_dev->connectionManager();
			if (deviceConnMan)
			{
				updateTrayIcon(deviceConnMan->deviceStatus().statusFlags);
			}
		}
	}
}

void Tray::showActiveDeviceTray() {
	// If the current foreground tray device is deactivated, find the first active device and use it instead
	bool needsNewDeviceTrayComponent = true;
	if (d->foregroundTrayComponent)
	{
		needsNewDeviceTrayComponent = false;
		TDENetworkDevice* dtc_comp_dev = dynamic_cast<TDENetworkDevice*>(hwdevices->findByUniqueID(d->foregroundTrayComponent->device()));
		TDENetworkConnectionManager* deviceConnMan = (dtc_comp_dev ? dtc_comp_dev->connectionManager() : NULL);
		if (deviceConnMan)
		{
			TDENetworkConnectionStatus::TDENetworkConnectionStatus statusFlags = deviceConnMan->deviceStatus().statusFlags;
			if ((statusFlags == TDENetworkConnectionStatus::Disconnected)
				|| (statusFlags == (TDENetworkConnectionStatus::Disconnected | TDENetworkConnectionStatus::LinkUnavailable))
				|| (statusFlags & TDENetworkConnectionStatus::Invalid))
			{
				needsNewDeviceTrayComponent = true;
			}
		}
	}

	if (needsNewDeviceTrayComponent) {
		disconnectTrayDeviceManager();

		d->foregroundTrayComponent = NULL;

		for (TQValueList<TrayComponent*>::Iterator it = d->trayComponents.begin(); it != d->trayComponents.end(); ++it) {
			DeviceTrayComponent* newDtc = dynamic_cast<DeviceTrayComponent*> (*it);
			if (newDtc) {
				TDENetworkDevice* dtc_comp_dev = dynamic_cast<TDENetworkDevice*>(hwdevices->findByUniqueID(newDtc->device()));
				TDENetworkConnectionManager* deviceConnMan = (dtc_comp_dev ? dtc_comp_dev->connectionManager() : NULL);
				if (deviceConnMan)
				{
					TDENetworkConnectionStatus::TDENetworkConnectionStatus statusFlags = deviceConnMan->deviceStatus().statusFlags;
					if ((statusFlags == TDENetworkConnectionStatus::Disconnected)
						|| (statusFlags == (TDENetworkConnectionStatus::Disconnected | TDENetworkConnectionStatus::LinkUnavailable))
						|| (statusFlags & TDENetworkConnectionStatus::Invalid)
						)
					{
						continue;
					}
					else
					{
						d->foregroundTrayComponent = newDtc;
						break;
					}
				}
			}
		}

		if (d->foregroundTrayComponent != NULL) {
			connectTrayDeviceManager();
			updateTrayDeviceManagerState();
		}
	}

	trayUiChanged();
}

void Tray::updateTrayIcon(TDENetworkConnectionStatus::TDENetworkConnectionStatus state)
{
	// Get all active connections
	char active_vpn=0;
	char found_any_active_connection=0;

	TDEGlobalNetworkManager* nm = TDEGlobal::networkManager();

	// Make sure the current state will be displayed
	if (d->foregroundTrayComponent) {
		TDENetworkDevice* foreground_tray_dev = dynamic_cast<TDENetworkDevice*>(hwdevices->findByUniqueID(d->foregroundTrayComponent->device()));
		if (foreground_tray_dev) {
			TDENetworkConnectionManager* deviceConnMan = foreground_tray_dev->connectionManager();
			if (deviceConnMan) {
				state = deviceConnMan->deviceStatus().statusFlags;
			}
		}
	}

	TDENetworkGlobalManagerFlags::TDENetworkGlobalManagerFlags globalState = (nm ? nm->backendStatus() : TDENetworkGlobalManagerFlags::Unknown);

	// get all available VPN Connections
	active_vpn = 0;
	if ((globalState & TDENetworkGlobalManagerFlags::VPNConnected) | ((globalState & TDENetworkGlobalManagerFlags::VPNDisconnected) && (globalState & TDENetworkGlobalManagerFlags::VPNEstablishingLink))) {
		active_vpn = 1;
	}

	found_any_active_connection = 0;
	if ((globalState & TDENetworkGlobalManagerFlags::Connected) | ((globalState & TDENetworkGlobalManagerFlags::Disconnected) && (globalState & TDENetworkGlobalManagerFlags::EstablishingLink))) {
		found_any_active_connection = 1;
	}

#ifdef DEBUG_STATE
// 	if (found_any_active_connection == 1) {
// 		printf("Active connection found\n");
// 	}
#endif // DEBUG_STATE

	TDENetworkGlobalManagerFlags::TDENetworkGlobalManagerFlags current_vpn_state = (globalState & TDENetworkGlobalManagerFlags::VPNMask);
	if ((current_vpn_state & TDENetworkGlobalManagerFlags::VPNFailed) || (current_vpn_state & TDENetworkGlobalManagerFlags::VPNDisconnected)) {
		active_vpn = 0;
	}

	// stop the old movie to avoid unnecessary wakups
	DeviceTrayComponent * dtc = d->foregroundTrayComponent;

	if (movie()) {
		movie()->pause();
	}

	if (active_vpn == 0) {
		if ((dtc) && (found_any_active_connection == 1)) {
			TQMovie stateMovie = dtc->movieForState(state);
			if (!stateMovie.isNull()) {
				// animation desired
				int frame = -1;
				if (movie()) {
					frame = movie()->frameNumber();
				}
	
				// set the movie
				setMovie(stateMovie);
	
				// start at the same frame as the movie before
				if (frame > 0) {
					movie()->step(frame);
				}
	
				// start the animation
				movie()->unpause();
			}
			else {
				TQPixmap statePixmap = dtc->pixmapForState(state);
				if (!statePixmap.isNull()) {
					setPixmap(statePixmap);
				}
				else {
					setPixmap(m_pixmapCache["tdenetworkmanager"]);
				}
			}
		}
		else {
			setBaseStateIcon(globalState);
		}
	}
	else {
		TDENetworkGlobalManagerFlags::TDENetworkGlobalManagerFlags current_vpn_state = (globalState & TDENetworkGlobalManagerFlags::VPNMask);
#ifdef DEBUG_STATE
		printf("VPN state: 0x%08x\n", current_vpn_state);
		//printf("Activated is: %d\n", TDENetworkConnectionStatus::Connected);
#endif // DEBUG_STATE
		// stop the old movie to avoid unnecessary wakups
		DeviceTrayComponent * dtc = d->foregroundTrayComponent;
	
		if (movie())
			movie()->pause();
	
		if (dtc) {
			if (current_vpn_state & TDENetworkGlobalManagerFlags::VPNConnected) {
				setPixmap(m_pixmapCache["nm_device_vpn"]);
			}
			if ((current_vpn_state & TDENetworkGlobalManagerFlags::VPNEstablishingLink)
				|| (current_vpn_state & TDENetworkGlobalManagerFlags::VPNNeedAuthorization)
				|| (current_vpn_state & TDENetworkGlobalManagerFlags::VPNConfiguringProtocols)
				|| (current_vpn_state & TDENetworkGlobalManagerFlags::VPNVerifyingProtocols)) {
				int frame = -1;
				if (movie()) {
					frame = movie()->frameNumber();
				}
	
				// set the movie
				if ((current_vpn_state & TDENetworkGlobalManagerFlags::VPNEstablishingLink)
					|| (current_vpn_state & TDENetworkGlobalManagerFlags::VPNNeedAuthorization)) {
					setMovie(TQMovie(m_movieCache["nm_stage02_connecting_vpn"]));
				}
				if ((current_vpn_state & TDENetworkGlobalManagerFlags::VPNConfiguringProtocols)
					|| (current_vpn_state & TDENetworkGlobalManagerFlags::VPNVerifyingProtocols)) {
					setMovie(TQMovie(m_movieCache["nm_stage03_connecting_vpn"]));
				}
	
				// start at the same frame as the movie before
				if (frame > 0) {
					movie()->step(frame);
				}
	
				// start the animation
				movie()->unpause();
			}
		}
	}

	nm_device_state_global = state;
#ifdef DEBUG_STATE
	//printf("Device state: 0x%08x\n", nm_device_state_global);
#endif // DEBUG_STATE
}

void Tray::updateActiveConnection(TDENetworkConnectionStatus::TDENetworkConnectionStatus state)
{
	if (state != TDENetworkConnectionStatus::Connected) {
		return;
	}
}

void Tray::slotDeviceAddedNotify(TDENetworkDevice* dev)
{
	kdDebug() << "Tray::slotDeviceAddedNotify" << endl;
	if( dev && !dev->deviceNode().isEmpty() ) {
		KNotifyClient::event( winId(), "tdenm-nm-device-added", i18n("New network device %1 found").arg(dev->deviceNode()) );
	}
}

void Tray::slotDeviceRemovedNotify(TDENetworkDevice* dev)
{
	kdDebug() << "Tray::slotDeviceRemovedNotify" << endl;
	if( dev && !dev->deviceNode().isEmpty() ) {
		KNotifyClient::event( winId(), "tdenm-nm-device-removed", i18n("Network device %1 removed").arg(dev->deviceNode()) );
	}
}

void Tray::slotStateChangedNotify(TDENetworkGlobalManagerFlags::TDENetworkGlobalManagerFlags newState, TDENetworkGlobalManagerFlags::TDENetworkGlobalManagerFlags prevState)
{
	TDENetworkGlobalManagerFlags::TDENetworkGlobalManagerFlags nm_state = newState;

	// popup notification according to NM's state
	if (nm_state & TDENetworkGlobalManagerFlags::EstablishingLink) {
		KNotifyClient::event( winId(), "tdenm-nm-connecting", i18n("NetworkManager is connecting") );
	}
	else if (nm_state & TDENetworkGlobalManagerFlags::Disconnected) {
		KNotifyClient::event( winId(), "tdenm-nm-disconnected", i18n("NetworkManager is now disconnected") );
	}
	else if (nm_state & TDENetworkGlobalManagerFlags::Connected) {
		KNotifyClient::event( winId(), "tdenm-nm-connected", i18n("NetworkManager is now connected") );
	}
	else if (nm_state & TDENetworkGlobalManagerFlags::Sleeping) {
		KNotifyClient::event( winId(), "tdenm-nm-sleeping", i18n("TDENetworkManager Offline") );
	}
	else if (nm_state & TDENetworkGlobalManagerFlags::Unknown) {
		//
	}
}

void Tray::slotEditNotifications()
{
	KNotifyDialog::configure(this);
}

void Tray::slotGenericDeviceAdded(TDEGenericDevice *dev)
{
	TDENetworkDevice* netdev = dynamic_cast<TDENetworkDevice*>(dev);
	if (netdev) {
		slotAddDeviceTrayComponent(netdev->uniqueID());
		slotDeviceAddedNotify(netdev);
	}
}

void Tray::slotGenericDeviceRemoved(TDEGenericDevice *dev)
{
	TDENetworkDevice* netdev = dynamic_cast<TDENetworkDevice*>(dev);
	if (netdev) {
		slotRemoveDeviceTrayComponent(netdev->uniqueID());
		slotDeviceRemovedNotify(netdev);
	}
}

void Tray::slotVPNEventHandler(TDENetworkVPNEventType::TDENetworkVPNEventType event, TQString message)
{
	if (event == TDENetworkVPNEventType::LoginBanner) {
		slotVPNBannerShow(message);
	}
}

TQPixmap Tray::pixmapForName(TQString pixmapPath)
{
	if (pixmapPath != "") {
		return KSystemTray::loadIcon(pixmapPath);
	}
	else {
		return TQPixmap();
	}
}

TQMovie Tray::movieForName(TQString moviePath)
{
	if (moviePath != "") {
		return TQMovie(TDEGlobal::iconLoader()->moviePath(moviePath, TDEIcon::Panel, width()));
	}
	else {
		return TQMovie();
	}
}

TQByteArray Tray::movieFileData(TQString movieName) {
	TQByteArray ret;
	TQString fileName = TDEGlobal::iconLoader()->moviePath(movieName, TDEIcon::Panel, width());
	if (fileName != "") {
		TQFile file(fileName);
		if (file.exists()) {
			if (file.open(IO_ReadOnly)) {
				ret = file.readAll();
				file.close();
			}
		}
	}
	return ret;
}

void Tray::updateGraphicsCache() {
	m_pixmapCache["tdenetworkmanager"] = pixmapForName("tdenetworkmanager");
	m_pixmapCache["tdenetworkmanager_disabled"] = pixmapForName("tdenetworkmanager_disabled");
	m_pixmapCache["nm_no_connection"] = pixmapForName("nm_no_connection");
	m_pixmapCache["nm_device_vpn"] = pixmapForName("nm_device_vpn");
	m_pixmapCache["ok"] = pixmapForName("ok");
	m_pixmapCache["nm_signal_00"] = pixmapForName("nm_signal_00");
	m_pixmapCache["nm_signal_25"] = pixmapForName("nm_signal_25");
	m_pixmapCache["nm_signal_50"] = pixmapForName("nm_signal_50");
	m_pixmapCache["nm_signal_75"] = pixmapForName("nm_signal_75");
	m_pixmapCache["nm_signal_100"] = pixmapForName("nm_signal_100");

	m_movieCache["nm_stage01_connecting"] = movieFileData("nm_stage01_connecting");
	m_movieCache["nm_stage02_connecting"] = movieFileData("nm_stage02_connecting");
	m_movieCache["nm_stage03_connecting"] = movieFileData("nm_stage03_connecting");
	m_movieCache["nm_stage02_connecting_vpn"] = movieFileData("nm_stage02_connecting_vpn");
	m_movieCache["nm_stage03_connecting_vpn"] = movieFileData("nm_stage03_connecting_vpn");
}

void Tray::resizeEvent(TQResizeEvent *) {
	updateGraphicsCache();
}

Tray::Tray() : KSystemTray()
{
	hwdevices = TDEGlobal::hardwareDevices();

	updateGraphicsCache();

	d = new TrayPrivate(this);
  d->helpMenu = new KHelpMenu(0, TDEGlobal::instance()->aboutData(), false);
  
	connect(&d->signalMapper, TQ_SIGNAL(mapped(int)), this, TQ_SLOT(slotDeactivateConnection(int)));

	setPixmap (m_pixmapCache["tdenetworkmanager"]);
	setMouseTracking (true);

	// Actions used for plugging into the menu
	new TDEAction (i18n ("Switch to offline mode"),
					SmallIcon ("no",  TQIconSet::Automatic), 0,
					this, TQ_SLOT (slotOfflineMode()), actionCollection (), "offline_mode");

	new TDEAction (i18n ("Switch to online mode"),
					SmallIcon ("ok",  TQIconSet::Automatic), 0,
					this, TQ_SLOT (slotOnlineMode()), actionCollection (), "online_mode");

	new TDEAction (i18n ("Disable Wireless"),
					SmallIcon ("wireless_off",  TQIconSet::Automatic), 0,
					this, TQ_SLOT (slotDisableWireless()), actionCollection (), "disable_wireless");

	new TDEAction (i18n ("Enable Wireless"),
					SmallIcon ("wireless",  TQIconSet::Automatic), 0,
					this, TQ_SLOT (slotEnableWireless()), actionCollection (), "enable_wireless");

	new TDEAction (i18n ("Edit Connections"),
					SmallIcon ("edit",  TQIconSet::Automatic), 0,
					this, TQ_SLOT (slotEditConnections()), actionCollection (), "edit_connections");

	new TDEAction (i18n ("Configure Notifications"),
					SmallIcon ("knotify",  TQIconSet::Automatic), 0,
					this, TQ_SLOT (slotEditNotifications()), actionCollection (), "configure_notifications");

	// this action is only connected when the menu is shown, hence the 0 receiver
	new TDEAction (i18n ("New connection ..."),
					SmallIcon ("document-new",  TQIconSet::Automatic), 0,
					this, 0, actionCollection (), "new_connection");

	new TDEActionMenu (i18n ("New connection ..."),
					SmallIcon ("document-new",  TQIconSet::Automatic),
					actionCollection(), "new_connection_menu");

	new TDEActionMenu (i18n ("Deactivate connection..."),
					SmallIcon ("no",  TQIconSet::Automatic),
					actionCollection (), "deactivate_menu");

	// get notified when NM's state changes
	connect(TDEGlobal::networkManager(), TQ_SIGNAL(networkConnectionStateChanged(TDENetworkGlobalManagerFlags::TDENetworkGlobalManagerFlags, TDENetworkGlobalManagerFlags::TDENetworkGlobalManagerFlags)), this, TQ_SLOT(slotStateChanged(TDENetworkGlobalManagerFlags::TDENetworkGlobalManagerFlags, TDENetworkGlobalManagerFlags::TDENetworkGlobalManagerFlags)));

	// get notified about new/removed devices
	connect(hwdevices, TQ_SIGNAL(hardwareAdded(TDEGenericDevice*)), this, TQ_SLOT(slotGenericDeviceAdded(TDEGenericDevice*)));
	connect(hwdevices, TQ_SIGNAL(hardwareRemoved(TDEGenericDevice*)), this, TQ_SLOT(slotGenericDeviceRemoved(TDEGenericDevice*)));

	// Notifications
	connect(TDEGlobal::networkManager(), TQ_SIGNAL(networkConnectionStateChanged(TDENetworkGlobalManagerFlags::TDENetworkGlobalManagerFlags, TDENetworkGlobalManagerFlags::TDENetworkGlobalManagerFlags)), this, TQ_SLOT(slotStateChangedNotify(TDENetworkGlobalManagerFlags::TDENetworkGlobalManagerFlags, TDENetworkGlobalManagerFlags::TDENetworkGlobalManagerFlags)));
	connect(TDEGlobal::networkManager(), TQ_SIGNAL(vpnEvent(TDENetworkVPNEventType::TDENetworkVPNEventType, TQString)), this, TQ_SLOT(slotVPNEventHandler(TDENetworkVPNEventType::TDENetworkVPNEventType, TQString)));

	// Get connection list
	TDEGlobalNetworkManager* nm = TDEGlobal::networkManager();
	if (nm) {
		if (tdenetworkmanager_editor_dialog_count == 0) nm->loadConnectionInformation();
	}

	// initial setup of the device-trays
	updateDeviceTrays();

	slotStateChanged(nm ? nm->backendStatus() : TDENetworkGlobalManagerFlags::Unknown, TDENetworkGlobalManagerFlags::Unknown);
}

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

#include "tdenetman-tray.moc"
