/***************************************************************************
 *
 * knetworkmanager-devicestore.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
 *
 **************************************************************************/

#include <NetworkManager.h>

#include "knetworkmanager.h"
#include "knetworkmanager-devicestore.h"
#include "knetworkmanager-wired_device.h"
#include "knetworkmanager-wireless_device.h"
#include "knetworkmanager-gsm_device.h"
#include "knetworkmanager-cdma_device.h"
#include "knetworkmanager-device.h"
#include "knetworkmanager-nm_proxy.h"
#include "dbus/deviceproxy.h"

#include <dbus/qdbuserror.h>
#include <dbus/qdbusobjectpath.h>
#include <dbus/qdbusconnection.h>

#include <kdebug.h>

class DeviceStorePrivate
{
	public:
		DeviceStorePrivate() {}
		~DeviceStorePrivate() {}

	QMap<QString, Device*> devices;
	static DeviceStore* store;
};

DeviceStore* DeviceStorePrivate::store = NULL;

DeviceStore* DeviceStore::getInstance()
{
	if (DeviceStorePrivate::store)
		return DeviceStorePrivate::store;
	return (DeviceStorePrivate::store = new DeviceStore());
}

void DeviceStore::slotDeviceRemoved(const QDBusObjectPath & obj_path)
{
	kdDebug() << "DeviceStore::slotDeviceRemoved" << endl;
	QMap<QString, Device*>::Iterator it = d->devices.find(QString(obj_path));
	if (it != d->devices.end())
	{
		// remove this device
		Device* dev = it.data();

		emit DeviceRemoved(dev);

		d->devices.remove(it);
		delete dev;
		dev = NULL;
	}
}

void DeviceStore::slotDeviceAdded(const QDBusObjectPath & obj_path)
{
	// just start an update
	Device* dev = createDevice(obj_path);
	if (dev)
		emit DeviceAdded(dev);
}

Device* DeviceStore::createDevice(const QDBusObjectPath &obj_path)
{
	QDBusError err;
	// if we have this device already in our list goto the next one
	QMap<QString, Device*>::Iterator it = d->devices.find(obj_path);
	if ( it != d->devices.end())
		return it.data();

	// FIXME: ugly stuff is going on here, better pass the DeviceProxy to the Device's constructor instead of the object_path
	DBus::DeviceProxy* dev = new DBus::DeviceProxy(NM_DBUS_SERVICE, obj_path);
	Device* new_dev = NULL;

	if (dev)
	{
		dev->setConnection(QDBusConnection::systemBus());
		Q_UINT32 type = dev->getDeviceType(err);

		// FIXME: This should not be hardcoded, it would be better if wireless, wired etc. modules regsiter their device type
		// select the right device type and create the appropriate objects
		switch(type)
		{
			case DEVICE_TYPE_802_11_WIRELESS:
				new_dev = new WirelessDevice(obj_path);
				break;
			case DEVICE_TYPE_802_3_ETHERNET:
				new_dev = new WiredDevice(obj_path);
				break;
			case DEVICE_TYPE_GSM:
				new_dev = new GSMDevice(obj_path);
				break;
			case DEVICE_TYPE_CDMA:
				new_dev = new CDMADevice(obj_path);
				break;
			default:
				kdWarning() << k_funcinfo << "Unknown devicetype" << endl;
				new_dev = new Device(obj_path);
				break;
		}

		// insert the new device into our list	
		if (new_dev)
			d->devices.insert(obj_path, new_dev);

		delete dev;
	}
	else
		kdWarning() << k_funcinfo << "Dev is Null" << endl;

	return new_dev;
}

void DeviceStore::updateDevices()
{
	NMProxy* nm = NMProxy::getInstance();
	QValueList<QDBusObjectPath> obj_paths;
	QDBusError err;

	// get a list of NM devices
	nm->GetDevices(obj_paths, err);

	// create a list of KNM devices
	for (QValueList<QDBusObjectPath>::Iterator it = obj_paths.begin(); it != obj_paths.end(); ++it)
	{
		createDevice((*it));
	}
}

QValueList<Device*> DeviceStore::getDevices(Q_UINT32 type)
{
	updateDevices();

	if (type == 0)
		return d->devices.values();
	else
	{
		// only return devices of a special type
		QValueList<Device*> devs;
		for (QMap<QString, Device*>::Iterator it = d->devices.begin(); it != d->devices.end(); ++it)
		{
			if (it.data()->getDeviceType() == type)
				devs.append(it.data());
		}
		return devs;
	}
}

Device* DeviceStore::getDevice(QDBusObjectPath objpath)
{
	return d->devices[objpath];
}

DeviceStore::DeviceStore ( QObject * parent, const char * name ) : QObject( parent, name )
{
	d = new DeviceStorePrivate();

	// get notified from NM when devices are added or removed
	NMProxy* nm = NMProxy::getInstance();
	connect(nm, SIGNAL(DeviceAdded(const QDBusObjectPath& )), this, SLOT(slotDeviceAdded(const QDBusObjectPath&)));
	connect(nm, SIGNAL(DeviceRemoved(const QDBusObjectPath& )), this, SLOT(slotDeviceRemoved(const QDBusObjectPath&)));
}

DeviceStore::~DeviceStore ()
{
	// delete all devicepointers
	while (d->devices.begin() != d->devices.end())
		delete d->devices.begin().data();

	// delete private data
	delete d;
}


#include "knetworkmanager-devicestore.moc"
