/***************************************************************************
 *
 * knetworkmanager-tray.cpp - A NetworkManager frontend for KDE
 *
 * 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
 *
 **************************************************************************/

class WirelessDialog;

#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 <tqdbusobjectpath.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 <NetworkManager.h>
#include <NetworkManagerVPN.h>

#include <tqdbuserror.h>

#include "xmlmarshaller.h"
#include "vpn_tray_component.h"
#include "devicetraycomponent.h"
#include "knetworkmanager-cellular_device_tray.h"
#include "knetworkmanager-cellular_device.h"
#include "knetworkmanager-device.h"
#include "knetworkmanager-devicestore.h"
#include "knetworkmanager-tray.h"
#include "knetworkmanager-menu_subhead.h"
#include "knetworkmanager-nm_proxy.h"
#include "knetworkmanager-connection.h"
#include "knetworkmanager-connection_setting_info.h"
#include "knetworkmanager-connection_settings_dialog.h"
#include "knetworkmanager-connection_store.h"
#include "knetworkmanager-vpn_connection.h"
#include "knetworkmanager-connection.h"
#include "knetworkmanager-storage.h"
#include "knetworkmanager-connection_editor.h"
#include "knetworkmanager-vpnauthenticationdialog.h"
#include "knetworkmanager-wired_device.h"
#include "knetworkmanager-wired_device_tray.h"
#include "knetworkmanager-wireless_device_tray.h"
#include "knetworkmanager-wireless_device.h"

#include <stdio.h>

#define KDED_NETWORK_NAME "NMNetwork"

#if !defined(NM_CHECK_VERSION)
#define NM_CHECK_VERSION(x,y,z) 0
#endif

extern unsigned int current_vpn_state;
NMDeviceState nm_device_state_global;
extern unsigned char vpn_new_credentials_needed;

NewSecretsDialog::NewSecretsDialog(ConnectionSettings::Connection *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, NULL, Tray::getInstance(), "Edit connection");
	dlg->show();
	close();
}

void NewSecretsDialog::reject()
{
	_connection->slotSecretsError();
	TQDialog::reject();
}

void NewSecretsDialog::init()
{
	ConnectionSettings::GenericConnection* conn = dynamic_cast<ConnectionSettings::GenericConnection*>(_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(tqtr("    The connection %1 could not be established    ").arg(conn->getInfoSetting()->getName()), 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, TQT_SIGNAL(clicked()), TQT_SLOT(slotDialogEdit()));
	connect(buttonCancel, TQT_SIGNAL(clicked()), this, TQT_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, TQPair<ConnectionSettings::Connection*, Device*> > act_conn_map;
		int current_idx;
};

Tray* TrayPrivate::tray = NULL;

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

void Tray::slotEditConnections()
{
	ConnectionEditorImpl* dlg = new ConnectionEditorImpl(this);
	dlg->show();
}


void Tray::slotEnableWireless()
{
	NMProxy* nm = NMProxy::getInstance();
	TQT_DBusError err;
	if (!nm) return;

	nm->setWirelessEnabled(true, err);
}

void Tray::slotDisableWireless()
{
	NMProxy* nm = NMProxy::getInstance();
	TQT_DBusError err;
	if (!nm) return;

	nm->setWirelessEnabled(false, err);
}

void Tray::slotOfflineMode()
{
	NMProxy* nm = NMProxy::getInstance();
	TQT_DBusError err;
	if (!nm) return;

	nm->Sleep(true, err);
}

void Tray::slotOnlineMode()
{
	NMProxy* nm = NMProxy::getInstance();
	TQT_DBusError err;
	if (!nm) return;

	nm->Sleep(false, err);
}

void Tray::slotNewVPNConnection()
{
	printf("Creating new VPN connection\n");
	// create a new VPN connection
	Connection* conn = new VPNConnection();

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

void Tray::contextMenuAboutToShow (TDEPopupMenu* menu)
{
	TQT_DBusError err;
	NMProxy* nm = NMProxy::getInstance();

	// clear menu
	menu->clear();

	if (nm->isNMRunning())
	{

    // 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("knetworkmanager_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)
				{
					TQString actionName = TQString("new_connection_%1").arg(dev_comp->device()->getInterface());
					TQString menuCaption = TQString("%1").arg(dev_comp->device()->getInterface());
					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);
					if (!deviceNewConnAction) {
						deviceNewConnAction = new TDEAction (menuCaption, 0, (*it), TQT_SLOT(newConnection()), actionCollection(), actionName);
					}
					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, TQT_TQOBJECT(this), TQT_SLOT(slotNewVPNConnection()), actionCollection(), actionName);
			newConnActionMenu->insert(deviceNewConnAction);
 		} else if ( devices == 1 ) {
			newConnAction = actionCollection ()->action ("new_connection");
			TQT_BASE_OBJECT_NAME::disconnect( newConnAction, TQT_SIGNAL(activated()) );
			TQT_BASE_OBJECT_NAME::connect( newConnAction, TQT_SIGNAL(activated()), d->trayComponents[0], TQT_SLOT(newConnection()));
		}
		if (newConnAction) {
			newConnAction->plug(menu);
		}
	
	  // turn things off
		if (nm)
		{
			TDEActionMenu* disableStuffActionMenu = static_cast<TDEActionMenu*>(actionCollection ()->action ("deactivate_menu") );
			disableStuffActionMenu->popupMenu()->clear();
			TQValueList<TQPair<ConnectionSettings::Connection*, Device*> > map = nm->getActiveConnectionsMap();
			d->act_conn_map.clear();
	
			for (TQValueList<TQPair<ConnectionSettings::Connection*, Device*> >::Iterator it = map.begin(); it != map.end(); ++it)
			{
				ConnectionSettings::GenericConnection* conn = dynamic_cast<ConnectionSettings::GenericConnection*>((*it).first);
				Device* dev = (*it).second;

				if (!conn)
					continue;

				TQString actionName = TQString("disable_connection_%1_%2").arg(conn->getID()).arg(dev ? dev->getInterface() : "");
				TDEAction * deviceNewConnAction = actionCollection ()->action (actionName);
				TQString actionText = conn->getInfoSetting()->getName();
				if (dev)
					actionText += TQString(" (%1)").arg(dev->getInterface());

				if (!deviceNewConnAction) {
					deviceNewConnAction = new TDEAction (actionText, 0, &d->signalMapper, TQT_SLOT(map()), actionCollection(), actionName);
				}
				d->signalMapper.setMapping(deviceNewConnAction, d->current_idx);
				d->act_conn_map.insert(d->current_idx, TQPair<ConnectionSettings::Connection*, Device*> (conn, dev));
				d->current_idx++;
				disableStuffActionMenu->insert(deviceNewConnAction);	
			}

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

			// offline vs. online mode
			TDEAction* switch_mode = NULL;
			if (nm->getState(err) != NM_STATE_ASLEEP) {
				switch_mode = actionCollection ()->action ("offline_mode");
			}
			else {
				switch_mode = actionCollection ()->action ("online_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 ();
	TDEAction* quitAction = actionCollection ()->action (KStdAction::name (KStdAction::Quit));
	if (quitAction)
		quitAction->plug (menu);
}


void
Tray::slotStateChanged(TQ_UINT32 state)
{
	NMState nm_state = (NMState) state;
	// change tray icon according to NM's state
	switch(nm_state)
	{
		case NM_STATE_UNKNOWN:
		case NM_STATE_ASLEEP:
		case NM_STATE_CONNECTING:
		case NM_STATE_DISCONNECTED:
			setPixmap (loadIcon ("knetworkmanager_disabled"));
			break;
		case NM_STATE_CONNECTED:
			setPixmap (loadIcon ("knetworkmanager"));
			break;
	}
	printf("NM state: %d\n", nm_state);
}

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::slotVPNSecretsNeeded(ConnectionSettings::Connection* connection, ConnectionSettings::ConnectionSetting* setting, const TQStringList& hints, bool request_new)
{
#warning Implement Tray::slotVPNSecretsNeeded to handle parms properly
	Q_UNUSED(hints);

	Storage* storage = Storage::getInstance();
	bool hasSecretsStored = storage->hasSecretsStored(connection, setting);

	printf("Tray::slotVPNSecretsNeeded\n");
	kdDebug() << "Tray::slotVPNSecretsNeeded" << endl;

	// default secrets handling for all other connection types
	// 1) if we have secrets stored, restore them and send them back to NM
	// 2) if NM requests new secrets we should allow the user to retry the
	//    connection or to edit it

	//if (hasSecretsStored && (!request_new))
 	if (hasSecretsStored)
 	{
 		printf("Tray::slotVPNSecretsNeeded: Restoring saved secrets\n");
 		// We have secrets stored, restore them
//  		if (storage->restoreVPNSecrets(connection, setting))
//  		{
			int number_of_secrets_found = 0;
			ConnectionSettings::VPNConnection* conn = dynamic_cast<ConnectionSettings::VPNConnection*>(connection);
			TQString id = connection->getID();
			TQString type = setting->getType();
		
			printf("restoreVPNSecrets\n");
			// ID is necessary
			if (id.isEmpty()) {
				printf("VPN connection ID is empty!\n");
			}
			else {
				// Get a group for this setting
				TQString setting_grp = TQString("ConnectionSecrets_%1_%2").arg(id).arg(type);
			
				// Restore the settings
				printf("Restoring VPN secret: %s\n", setting_grp.ascii());
			
				TDEConfigGroup secrets_grp(TDEGlobal::config(), setting_grp);
				TQMap<TQString, TQString> config_map = TDEGlobal::config()->entryMap(setting_grp);
				TQString typetwo = secrets_grp.readEntry("Type");
					
	//			// get the appropriate setting from the connection
	// 			ConnectionSetting* setting = conn->getSetting(typetwo);
	// 			if (!setting)
	// 			{
	// 				printf("Secrets cannot be restored!\n");
	// 			}
			
				// Read the SettingsMap from tdeconfig
				// This loop reads the secrets information map only
				TQMap<TQString, TQString> map;
				for(TQMap<TQString, TQString>::ConstIterator it = config_map.begin(); it != config_map.end(); ++it)
				{
					if (!it.key().startsWith("Value_"))
						continue;
						
					TQString key = it.key();
					// Get the original key name
					key.replace("Value_", "");
			
					TQString xmldata = it.data();
					// Remove the annoying XML <string> stuff
					xmldata.replace("<string>", "");
					xmldata.replace("</string>", "");
					//printf("Got %s with value %s\n", key.ascii(), xmldata.ascii());
					map.insert(key, xmldata);
					number_of_secrets_found++;
				}
				if (number_of_secrets_found > 0) {
					printf("Got secrets from file, continuing...\n");
					
					// Good, we have new secrets now, update the settings
					//map = _vpnAuthWidget->getPasswords();
					ConnectionSetting* propcore = conn->getVPNSettingConnectionCore();
					SettingsMap othersettingsmap = propcore->toMap();
				
					// Pull the username and gateway out of map to stuff in the NM standard settings matrix
					othersettingsmap.insert("user", TQT_DBusData::fromString(map["user"]));
					map.erase("user");
					othersettingsmap.insert("domain", TQT_DBusData::fromString(map["domain"]));
					map.erase("domain");

					if (!request_new) {
						propcore->fromMap(othersettingsmap);
						VPN* prop = dynamic_cast<VPN*>(propcore);
						prop->setSecrets(map);
						conn->slotSecretsProvided(prop);
					}
					else {
						printf("Tray::slotVPNSecretsNeeded: New secrets requested\n");
						// OK, NM requests new secrets...do something!
						ConnectionSettings::VPNConnection* conn = dynamic_cast<ConnectionSettings::VPNConnection*>(connection);
						VPNAuthenticationDialog* auth = new VPNAuthenticationDialog(conn, this, "vpnauth");
						// Prefill the password dialog with cached credentials
						TQString passdata;
						for(TQMap<TQString, TQString>::ConstIterator it = map.begin(); it != map.end(); ++it)
						{
							passdata = it.data();
							// Remove any non-typable characters from the string!
							passdata.remove("\r");
							passdata.remove("\n");
							passdata.remove("\t");
							//printf("Trying to set %s to value %s\n", it.key().ascii(), passdata.ascii());
							auth->setPasswords(it.key(), passdata);
						}
						auth->show();
					}
		
					//connection->slotSecretsProvided(setting);
				}
				else {
					printf("Tray::slotVPNSecretsNeeded: New secrets needed\n");
					// OK, NM needs new secrets...do something!
					ConnectionSettings::VPNConnection* conn = dynamic_cast<ConnectionSettings::VPNConnection*>(connection);
					VPNAuthenticationDialog* auth = new VPNAuthenticationDialog(conn, this, "vpnauth");
					auth->show();
				}
			}
//  		}
 	}
 	else
 	{
		printf("Tray::slotVPNSecretsNeeded: New secrets needed\n");
		// OK, NM needs new secrets...do something!
		ConnectionSettings::VPNConnection* conn = dynamic_cast<ConnectionSettings::VPNConnection*>(connection);
		VPNAuthenticationDialog* auth = new VPNAuthenticationDialog(conn, this, "vpnauth");
		auth->show();
	}
}

void 
Tray::slotSecretsNeeded(ConnectionSettings::Connection* connection, ConnectionSettings::ConnectionSetting* setting, const TQStringList& hints, bool request_new)
{
	Storage* storage = Storage::getInstance();
	bool hasSecretsStored = storage->hasSecretsStored(connection, setting);

	// FIXME ugly secrets handling for VPN
	if (connection->getType() == NM_SETTING_VPN_SETTING_NAME)
	{
		if (vpn_new_credentials_needed == 1) {
			printf("VPN connection failed with bad credentials\n");
			vpn_new_credentials_needed = 0;
			request_new = 1;
		}
		slotVPNSecretsNeeded(connection, setting, hints, request_new);
		return;
	}

	// default secrets handling for all other connection types
	// 1) if we have secrets stored, restore them and send them back to NM
	// 2) if NM requests new secrets we should allow the user to retry the
	//    connection or to edit it

	if (hasSecretsStored && !request_new)
	{
		// We have secrets stored, restore them
		if (storage->restoreSecrets(connection, setting))
		{
			connection->slotSecretsProvided(setting);
		}
	}
	else
	{
		// ok, NM requests new secrets, let's ask the user if he wants to retry
		// or edit the connection
		NewSecretsDialog* dlg = new NewSecretsDialog(connection, this, "knetworkmanager");
		dlg->show();
	}
}

void Tray::slotAddDeviceTrayComponent(Device* dev)
{
	if (dev)
		createDeviceTrayComponent(dev);
}

void Tray::slotRemoveDeviceTrayComponent(Device* 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 = 0;
			}

			// remove the appropriate action
			TQString actionName = TQString("new_connection_%1").arg(dev_comp->device()->getInterface());
			TDEAction * deviceNewConnAction = actionCollection ()->action (actionName);
			
			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(Device* dev)
{
	bool trayExists = false;

	if (!dev) return;

	// 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;
		// different tray icons for different device types!
		switch (dev->getDeviceType())
		{
#if NM_CHECK_VERSION(0,8,992)
			case NM_DEVICE_TYPE_ETHERNET:
#else
			case DEVICE_TYPE_802_3_ETHERNET:
#endif
                devTray = new WiredDeviceTray(dynamic_cast<WiredDevice*>(dev), this, "wired_device_tray");
				break;
#if NM_CHECK_VERSION(0,8,992)
			case NM_DEVICE_TYPE_WIFI:
#else
			case DEVICE_TYPE_802_11_WIRELESS:
#endif
                devTray = new WirelessDeviceTray(static_cast<WirelessDevice*>(dev), this, "wireless_device_tray");
				break;
#if NM_CHECK_VERSION(0,8,992)
			case NM_DEVICE_TYPE_MODEM:
#else
			case DEVICE_TYPE_GSM:
			case DEVICE_TYPE_CDMA:
#endif
                devTray = new CellularDeviceTray(static_cast<CellularDevice*>(dev), this, "cellular_device_tray");
				break;
			default:
				kdWarning() << k_funcinfo << "UDI: " << dev->getUdi() << " has unknown devicetype: " << dev->getDeviceType() << endl;
		}
		if(devTray)
		{
			connect( devTray, TQT_SIGNAL(needsCenterStage(TrayComponent*,bool)),
					TQT_SLOT(trayComponentNeedsCenterStage(TrayComponent*,bool)));
			connect( devTray, TQT_SIGNAL(uiUpdated()), TQT_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
	DeviceStore* store = DeviceStore::getInstance();	
	TQValueList<Device*> devices = store->getDevices();

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

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

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

void Tray::slotDeactivateConnection(int index)
{
	ConnectionSettings::Connection* conn = d->act_conn_map[index].first;
	Device* dev = d->act_conn_map[index].second;
	NMProxy* nm = NMProxy::getInstance();

	if (conn) {
		TQString actionText = conn->getObjectPath().ascii();
		nm->deactivateConnection(conn, dev);
	}
}

void Tray::trayComponentNeedsCenterStage(TrayComponent *component, bool needsIt)
{
	DeviceTrayComponent * dtc = dynamic_cast<DeviceTrayComponent*>(component);
	if (dtc) {
		kdDebug() << k_funcinfo << dtc->device()->getInterface() << " : " << needsIt << endl;
		Device * device = dtc->device();
		if (needsIt) {
			if (d->foregroundTrayComponent) {
				disconnect(d->foregroundTrayComponent->device(), TQT_SIGNAL(StateChanged(NMDeviceState)), this, 0 );
			}
			d->foregroundTrayComponent = dtc;
			connect(device, TQT_SIGNAL(StateChanged(NMDeviceState)),
					TQT_SLOT(slotUpdateDeviceState(NMDeviceState)));
		} else {
			disconnect(device, TQT_SIGNAL(StateChanged(NMDeviceState)), this, 0 );
			//use active default
			NMProxy* nm = NMProxy::getInstance();
			device = nm->getDefaultDevice();
			if ( device ) {
				// 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() == device ) {
						d->foregroundTrayComponent = newDtc;
						break;
					}
				}
				kdDebug() << "  Device " << dtc->device()->getInterface() << " background, new foreground device: " << device->getInterface() << endl;
				connect(device, TQT_SIGNAL(StateChanged(NMDeviceState)),
						TQT_SLOT(slotUpdateDeviceState(NMDeviceState)));
				slotUpdateDeviceState(device->getState());
			}
		}
	}
}

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

void Tray::slotUpdateDeviceState(NMDeviceState state)
{
	updateTrayIcon(state);
	updateActiveConnection(state);
}

void Tray::trayUiChanged()
{
	DeviceTrayComponent * dtc = d->foregroundTrayComponent;
	if (dtc) {
		updateTrayIcon(dtc->device()->getState());
	}
}
void Tray::updateTrayIcon(NMDeviceState state)
{
	// Get all active connections
	char active_vpn=0;
	char found_any_active_connection=0;

	ConnectionStore* connectionStore = ConnectionStore::getInstance();
	NMProxy* nm = NMProxy::getInstance();
	TQValueList<TQPair<ConnectionSettings::Connection*, Device*> > map = nm->getActiveConnectionsMap();

	// get all available VPN Connections
	TQValueList<Connection*> connections = connectionStore->getConnections(NM_SETTING_VPN_SETTING_NAME);
	if (!connections.empty())
	{
		for (TQValueList<Connection*>::iterator it = connections.begin(); it != connections.end(); ++it)
		{
			VPNConnection* vpnconn = dynamic_cast<VPNConnection*>(*it);
			if (vpnconn)
			{
				// VPN connection found :)
				Info* info = vpnconn->getInfoSetting();

				// lets create a nice name for this connection
				if (info)
				{
					TQString title = info->getName();
					for (TQValueList<TQPair<ConnectionSettings::Connection*, Device*> >::Iterator it = map.begin(); it != map.end(); ++it)
					{
						ConnectionSettings::GenericConnection* conn = dynamic_cast<ConnectionSettings::GenericConnection*>((*it).first);
				
						if (conn) {
							if (strcmp(info->getName(),  conn->getInfoSetting()->getName()) == 0) {
								active_vpn = 1;
							}
						}
					}
				}
			}
		}
	}

	found_any_active_connection = 0;
	// Get all active connections
	TQValueList<TQPair<ConnectionSettings::Connection*, Device*> > allconnmap = nm->getActiveConnectionsMap();
	for (TQValueList<TQPair<ConnectionSettings::Connection*, Device*> >::Iterator it = allconnmap.begin(); it != allconnmap.end(); ++it)
	{
		ConnectionSettings::GenericConnection* conn = dynamic_cast<ConnectionSettings::GenericConnection*>((*it).first);

		if (!conn)
			continue;

		// Found an active connection
		found_any_active_connection = 1;
	}

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

	if ((current_vpn_state == NM_VPN_CONNECTION_STATE_FAILED) || (current_vpn_state == NM_VPN_CONNECTION_STATE_DISCONNECTED)) {
		active_vpn = 0;
	}

	if (active_vpn == 0) {
		// stop the old movie to avoid unnecessary wakups
		DeviceTrayComponent * dtc = d->foregroundTrayComponent;
	
		if (movie())
			movie()->pause();
	
		if ((dtc) && (found_any_active_connection == 1)) {
	
			if (!dtc->movieForState(state).isNull())
			{
				// animation desired
				int frame = -1;
				if (movie())
					frame = movie()->frameNumber();
	
				// set the movie
				setMovie(dtc->movieForState(state));
	
				// start at the same frame as the movie before
				if (frame > 0)
					movie()->step(frame);
	
				// start the animation
				movie()->unpause();
			}
			else if (!dtc->pixmapForState(state).isNull())
				setPixmap(dtc->pixmapForState(state));
			else
				setPixmap(loadIcon("knetworkmanager"));
		}
		else {
			setPixmap(loadIcon("knetworkmanager"));
		}
	}
	else {
		printf("VPN state: %d\n", current_vpn_state);
		//printf("Activated is: %d\n", NM_VPN_CONNECTION_STATE_ACTIVATED);
		// stop the old movie to avoid unnecessary wakups
		DeviceTrayComponent * dtc = d->foregroundTrayComponent;
	
		if (movie())
			movie()->pause();
	
		if (dtc) {
			if (current_vpn_state == NM_VPN_CONNECTION_STATE_ACTIVATED) {
				setPixmap(loadIcon("nm_device_vpn"));
			}
			if ((current_vpn_state == NM_VPN_CONNECTION_STATE_PREPARE) || (current_vpn_state == NM_VPN_CONNECTION_STATE_NEED_AUTH) || (current_vpn_state == NM_VPN_CONNECTION_STATE_CONNECT) || (current_vpn_state == NM_VPN_CONNECTION_STATE_IP_CONFIG_GET)) {
				int frame = -1;
				if (movie())
					frame = movie()->frameNumber();
	
				// set the movie
				if ((current_vpn_state == NM_VPN_CONNECTION_STATE_PREPARE) || (current_vpn_state == NM_VPN_CONNECTION_STATE_NEED_AUTH)) {
					setMovie(TQMovie(TDEGlobal::iconLoader()->moviePath("nm_stage02_connecting_vpn", TDEIcon::Panel)));
				}
				if ((current_vpn_state == NM_VPN_CONNECTION_STATE_CONNECT) || (current_vpn_state == NM_VPN_CONNECTION_STATE_IP_CONFIG_GET)) {
					setMovie(TQMovie(TDEGlobal::iconLoader()->moviePath("nm_stage03_connecting_vpn", TDEIcon::Panel)));
				}
	
				// 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;
	//printf("Device state: %d\n", nm_device_state_global);
}

void Tray::updateActiveConnection(NMDeviceState state)
{
	if (state != NM_DEVICE_STATE_ACTIVATED)
		return;

	NMProxy* nm = NMProxy::getInstance();
	if (d->foregroundTrayComponent) {
		Connection* active_conn = nm->getActiveConnection(d->foregroundTrayComponent->device());
		if (active_conn)
		{
			Info* info = dynamic_cast<Info*>(active_conn->getSetting(NM_SETTING_CONNECTION_SETTING_NAME));
			if (info)
				info->setTimestamp(TQDateTime::currentDateTime());
		}
	}
}

void Tray::slotDeviceAddedNotify(Device* dev)
{
	kdDebug() << "Tray::slotDeviceAddedNotify" << endl;
	KNotifyClient::event( winId(), "knm-nm-device-added", i18n("New network device %1 found").arg(dev->getInterface()) );
}

void Tray::slotDeviceRemovedNotify(Device* dev)
{
	kdDebug() << "Tray::slotDeviceRemovedNotify" << endl;
	KNotifyClient::event( winId(), "knm-nm-device-removed", i18n("Network device %1 removed").arg(dev->getInterface()) );
}

void Tray::slotVPNBannerShow(const TQString& vpnbanner)
{
	printf("VPN banner: %s\n", vpnbanner.ascii());
	KNotifyClient::event(winId(), "knm-nm-vpn-banner", vpnbanner);
}

void Tray::slotStateChangedNotify(TQ_UINT32 state)
{
	NMState nm_state = (NMState) state;
	// change tray icon according to NM's state
	switch(nm_state)
	{
		case NM_STATE_CONNECTING:
			KNotifyClient::event( winId(), "knm-nm-connecting", i18n("NetworkManager is connecting") );
			break;
		case NM_STATE_DISCONNECTED:
			KNotifyClient::event( winId(), "knm-nm-disconnected", i18n("NetworkManager is now disconnected") );
			break;
		case NM_STATE_CONNECTED:
			KNotifyClient::event( winId(), "knm-nm-connected", i18n("NetworkManager is now connected") );
			break;
		case NM_STATE_ASLEEP:
			KNotifyClient::event( winId(), "knm-nm-sleeping", i18n("KNetworkManager Offline") );
			break;
		case NM_STATE_UNKNOWN:

		default:
			break;
	}
}

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

Tray::Tray () : KSystemTray ()
{
	d = new TrayPrivate(TQT_TQOBJECT(this));

	connect(&d->signalMapper, TQT_SIGNAL(mapped(int)), this, TQT_SLOT(slotDeactivateConnection(int)));

	setPixmap (loadIcon ("knetworkmanager"));
	setMouseTracking (true);

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

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

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

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

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

	new TDEAction (i18n ("Configure Notifications"),
					     SmallIcon ("knotify",  TQIconSet::Automatic), 0,
					     TQT_TQOBJECT(this), TQT_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,
					     TQT_TQOBJECT(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
	NMProxy* nm = NMProxy::getInstance();
	connect(nm, TQT_SIGNAL(StateChange(TQ_UINT32)), this, TQT_SLOT(slotStateChanged(TQ_UINT32)));

	// get notifier when NM requests new secrets
	ConnectionStore* cstore = ConnectionStore::getInstance();
	connect(cstore, TQT_SIGNAL(SecretsNeeded(ConnectionSettings::Connection*, ConnectionSettings::ConnectionSetting*, const TQStringList&, bool)), this, TQT_SLOT(slotSecretsNeeded(ConnectionSettings::Connection*, ConnectionSettings::ConnectionSetting*, const TQStringList&, bool)));

	// get notified about new/removed devices
	DeviceStore* store = DeviceStore::getInstance();
	connect(store, TQT_SIGNAL(DeviceStoreChanged()), this, TQT_SLOT(updateDeviceTrays()));	
	connect(store, TQT_SIGNAL(DeviceAdded(Device*)), this, TQT_SLOT(slotAddDeviceTrayComponent(Device*)));	
	connect(store, TQT_SIGNAL(DeviceRemoved(Device*)), this, TQT_SLOT(slotRemoveDeviceTrayComponent(Device*)));	

	// Notifications
	connect(store, TQT_SIGNAL(DeviceAdded(Device*)), this, TQT_SLOT(slotDeviceAddedNotify(Device*)));	
	connect(store, TQT_SIGNAL(DeviceRemoved(Device*)), this, TQT_SLOT(slotDeviceRemovedNotify(Device*)));	
	connect(nm, TQT_SIGNAL(StateChange(TQ_UINT32)), this, TQT_SLOT(slotStateChangedNotify(TQ_UINT32)));


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

	TQT_DBusError err;
	slotStateChanged(nm->getState(err));
}

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

#include "knetworkmanager-tray.moc"

