/*
   Copyright (C) 2008 Norbert Frese <nf2 at scheinwelt at>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License (LGPL) as published by the Free Software Foundation;
   either version 2 of the License, or (at your option) any later
   version.

   This library 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
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.
*/

#include "vfshalunionmanager.h"
#include "gvfsnetworkmount.h"
#include <solid/ifaces/devicemanager.h>

#include <QtCore/QDebug>
#include <QtCore/QFile>
#include <QtCore/QString>
#include <QtCore/QTimer>
#include <QtCore/QLibrary>

#include <glib.h>
#include <gio/gio.h>

using namespace KioGioBridge;
using namespace Solid;

extern "C" 
{
    // dlopen entry point
    SOLID_EXPORT QObject * solid_backend_get_manager_instance()
    {
      return new VfsHalUnionManager(0);
    }
}


class VfsHalUnionManager::Private
{
public:
    QObject *m_sub_backend;
    GVolumeMonitor* gVolumeMonitor;
    QHash<QString, GvfsNetworkMount * > mountHash;
};

QString getUdiFromGMount(GMount * gMount)
{
    GFile * gFileRoot = g_mount_get_root(gMount);
    char * pointsToUri = g_file_get_uri(gFileRoot);
    QString qstrPointsToUri = QString(pointsToUri);
    
    g_object_unref(gFileRoot);
    g_free(pointsToUri);  
    
    if (qstrPointsToUri.startsWith("file://")) return QString();
    
    return qstrPointsToUri;
}


static void _monitor_mount_added (GVolumeMonitor *volume_monitor,
    GMount         *mount,
    gpointer        user_data)
{
    
    VfsHalUnionManager * mgr = (VfsHalUnionManager*) user_data;
    mgr->allGvfsNetworkMounts();
    
    QString udi = getUdiFromGMount(mount);
    
    qDebug() << "*** mount added udi=" << udi << " ***"<< endl;
    
    if (udi.isNull()) return;
    mgr->slotDeviceAdded(udi);
  
}

static void _monitor_mount_changed (GVolumeMonitor *volume_monitor,
    GMount         *mount,
    gpointer        user_data)
{
    QString udi = getUdiFromGMount(mount);
  
    qDebug() << "*** mount changed udi=" << udi << " ***" << endl;
    VfsHalUnionManager * mgr = (VfsHalUnionManager*) user_data;
  
}

static void _monitor_mount_removed (GVolumeMonitor *volume_monitor,
    GMount         *mount,
    gpointer        user_data)
{
    VfsHalUnionManager * mgr = (VfsHalUnionManager*) user_data;
    mgr->allGvfsNetworkMounts();
    QString udi = getUdiFromGMount(mount);
  
    qDebug() << "*** mount removed udi=" << udi << " ***" << endl;
    
    if (udi.isNull()) return;
    mgr->slotDeviceRemoved(udi);
}



VfsHalUnionManager::VfsHalUnionManager(QObject *parent)
 : Solid::Ifaces::DeviceManager(parent), d(new Private)
{
    // delegate to default backend
    d->m_sub_backend = Solid::Ifaces::DeviceManager::getDeviceManagerInstance("default");
    
    connect(d->m_sub_backend, SIGNAL(deviceAdded(QString)),
            this, SLOT(slotDeviceAdded(QString)));
    connect(d->m_sub_backend, SIGNAL(deviceRemoved(QString)),
            this, SLOT(slotDeviceRemoved(QString)));  
    
    // init GType system
    g_type_init ();
    
    // get volume monitor
    d->gVolumeMonitor = g_volume_monitor_get();

    // register signals
    g_signal_connect(G_OBJECT(d->gVolumeMonitor), "mount-added", G_CALLBACK(_monitor_mount_added), this);
    g_signal_connect(G_OBJECT(d->gVolumeMonitor), "mount-changed", G_CALLBACK(_monitor_mount_changed), this);
    g_signal_connect(G_OBJECT(d->gVolumeMonitor), "mount-removed", G_CALLBACK(_monitor_mount_removed), this);
    
}

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

QStringList VfsHalUnionManager::allGvfsNetworkMounts()
{
    
    QList<QString> removeList = d->mountHash.uniqueKeys (); 
  
    // collect all network mounts
    QStringList deviceUdiList;
  
    GList* mountList = g_volume_monitor_get_mounts(d->gVolumeMonitor);
    GList* el2 = mountList;
    
    while(el2)
    {
        GMount * gMount = (GMount *) el2->data;
        
        char * mountName = g_mount_get_name(gMount);
        GFile * gFileRoot = g_mount_get_root(gMount);
        
        char * pointsToUri = g_file_get_uri(gFileRoot);
        QString qstrPointsToUri = QString(pointsToUri);
        
        qDebug() << "mountname=" << mountName << " root=" << pointsToUri << endl;
        
        if (!qstrPointsToUri.startsWith("file://"))
        {
            deviceUdiList << qstrPointsToUri;
            
            if (!d->mountHash.contains(qstrPointsToUri))
            {
                GvfsNetworkMount * o = new GvfsNetworkMount();
                o->m_uri = qstrPointsToUri;
                o->m_title = QString(mountName);
                o->gMount = gMount;   
                d->mountHash[qstrPointsToUri] = o;
            }
            removeList.removeAll(qstrPointsToUri); // it still exists -> don't remove
        }
        
        g_object_unref(gFileRoot);
        g_free(mountName);
        g_free(pointsToUri);
         
        el2 = g_list_next(el2);      
    }

    // free items that disappeared
    foreach(QString key, removeList)
    {
        GvfsNetworkMount * o = d->mountHash[key];
        d->mountHash.remove(key);
        delete o;
    }
    
    return deviceUdiList;
}

QStringList VfsHalUnionManager::allDevices()
{
    Ifaces::DeviceManager *backend = qobject_cast<Ifaces::DeviceManager *>(d->m_sub_backend);

    QStringList deviceUdiList;
    deviceUdiList << backend->allDevices();
    deviceUdiList << allGvfsNetworkMounts();
    return deviceUdiList;
}

QStringList VfsHalUnionManager::devicesFromQuery(const QString &parentUdi, Solid::DeviceInterface::Type type)
{
    Ifaces::DeviceManager *backend = qobject_cast<Ifaces::DeviceManager *>(d->m_sub_backend);

    QStringList deviceUdiList;
    deviceUdiList << backend->devicesFromQuery(parentUdi, type);
    deviceUdiList << allGvfsNetworkMounts();
    return deviceUdiList;
    
}

QObject *VfsHalUnionManager::createDevice(const QString &udi)
{
    qDebug() << "VfsHalUnionManager::createDevice(" << udi << ")" << endl;
  
    if (d->mountHash.contains(udi))
    {
      return d->mountHash[udi];
    }
  
    Ifaces::DeviceManager *backend = qobject_cast<Ifaces::DeviceManager *>(d->m_sub_backend);
    return backend->createDevice(udi);
}

void VfsHalUnionManager::slotDeviceAdded(const QString &udi)
{
    qDebug() << "VfsHalUnionManager::slotDeviceAdded(" << udi << ")" << endl;
    emit deviceAdded(udi);
}

void VfsHalUnionManager::slotDeviceRemoved(const QString &udi)
{
    qDebug() << "VfsHalUnionManager::slotDeviceRemoved" << udi << ")" << endl;
    emit deviceRemoved(udi);
}


#include "vfshalunionmanager.moc"
