/* This file is part of the KDE libraries
    Copyright (C) 2009 Norbert Frese <nf2.email@gmail.com>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License 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 "giohardwarestorage.h"
#include <kdebug.h>
#include <QtCore/QPointer>

static void _storage_removed (
        GVolume         *gVolume,
        gpointer        user_data)
{

    QPointer<GioHardwareStorage> * p = (QPointer<GioHardwareStorage> *)user_data;
    if (!p->isNull())
    {
        (*p)->_slotRemoved();
    }
}

static void _storage_changed (
        GVolume         *gVolume,
        gpointer        user_data)
{
    QPointer<GioHardwareStorage> * p = (QPointer<GioHardwareStorage> *)user_data;
    if (!p->isNull())
    {
        (*p)->_slotChanged();
    }
}

static void myGClosureNotify(gpointer data, GClosure *closure)
{
    QPointer<GioHardwareStorage> * p = (QPointer<GioHardwareStorage> *)data;
    delete p;
}

GioHardwareStorage::GioHardwareStorage(GVolume * gVolume)
{
    m_isValid = true;
    m_gVolume = gVolume;
    g_object_ref(m_gVolume);

    {
        QPointer<GioHardwareStorage> * p = new QPointer<GioHardwareStorage>(this);

        GClosure* closure = g_cclosure_new(G_CALLBACK(_storage_removed),
                p,
                myGClosureNotify);

        g_signal_connect_closure(m_gVolume,
                "removed",
                closure,
                TRUE);
    }

    {
        QPointer<GioHardwareStorage> * p = new QPointer<GioHardwareStorage>(this);

        GClosure* closure = g_cclosure_new(G_CALLBACK(_storage_changed),
                p,
                myGClosureNotify);

        g_signal_connect_closure(m_gVolume,
                "changed",
                closure,
                TRUE);
    }

}


GioHardwareStorage::~GioHardwareStorage()
{
    g_object_unref(m_gVolume);
}

bool GioHardwareStorage::isValid() const
{
    return m_isValid;
}

QString GioHardwareStorage::id() const
{
    char * uuid = g_volume_get_uuid(m_gVolume);

    QString uuidStr = QString(uuid);
    g_free(uuid);

    return uuidStr;
}

QString GioHardwareStorage::displayName() const
{
    char * name = g_volume_get_name(m_gVolume);

    QString nameStr = QString(name);
    g_free(name);

    return nameStr;
}

QString GioHardwareStorage::icon() const
{
    GIcon * gIcon;

    GMount * gMount = g_volume_get_mount(m_gVolume);
    if (gMount)
    {
        gIcon = g_mount_get_icon(gMount);
        g_object_unref(gMount);
    }
    else
    {
        gIcon = g_volume_get_icon(m_gVolume);
    }


    if (gIcon != NULL && G_IS_THEMED_ICON(gIcon))
    {
        GThemedIcon * gThemedIcon = G_THEMED_ICON(gIcon);
        const gchar* const * iconNames = g_themed_icon_get_names (gThemedIcon);
        guint numIconNames = g_strv_length((gchar**)iconNames);

        if (numIconNames>0)
        {
            return QString(iconNames[0]);
        }
    }
    return "drive-removable-media";
}

QStringList GioHardwareStorage::emblems() const
{
    QStringList res;

    if (isAccessible()) {
        res << "emblem-mounted";
    } else {
        res << "emblem-unmounted";
    }

//    TODO
//    if (property("volume.fsusage").toString()=="crypto") {
//        res << "security-high";
//    }

    return res;
}


QUrl GioHardwareStorage::url() const
{
    GMount * gMount = g_volume_get_mount(m_gVolume);
    if (gMount)
    {
        GFile * root = g_mount_get_root(gMount);
        char * uri = g_file_get_uri(root);
        QString uriStr = QString(uri);
        g_free(uri);
        g_object_unref(root);
        return QUrl(uriStr);
    }
    else
    {
        return QUrl();
    }

}

QString GioHardwareStorage::mountPointPath() const
{
    return url().path();
}

bool GioHardwareStorage::isRemovable() const
{
    GMount * gMount = g_volume_get_mount(m_gVolume);
    if (gMount)
    {
        bool canUnmount = g_mount_can_unmount(gMount);
        g_object_unref(gMount);
        return canUnmount;
    }
    return false;
}

bool GioHardwareStorage::isReleasable() const
{
    return g_volume_can_eject(m_gVolume);;
}

bool GioHardwareStorage::isHotpluggable() const
{
    GDrive * gDrive = g_volume_get_drive(m_gVolume);
    if (gDrive)
    {
        bool yes = g_drive_is_media_check_automatic(gDrive);
        g_object_unref(gDrive);
        return yes;
    }
    return false;

}

bool GioHardwareStorage::isAccessible() const
{
    GMount * gMount = g_volume_get_mount(m_gVolume);
    if (gMount)
    {
        g_object_unref(gMount);
        return true;
    }
    return false;
}

KDiskFreeSpaceInfo GioHardwareStorage::freeSpace() const
{
    return KDiskFreeSpaceInfo::freeSpaceInfo(url().path());
}

static void _mountReadyCallback(GObject *source_object,
        GAsyncResult *res,
        gpointer user_data)
{
    QPointer<GioHardwareStorage> * p = (QPointer<GioHardwareStorage> *)user_data;
    if (!p->isNull())
    {
        GError * gError = NULL;
        QString errorMsg;

        KStorage::ErrorType error = KStorage::NoError;

        if (!g_volume_mount_finish(G_VOLUME(source_object),
                res,
                &gError))
        {
            error = KStorage::UnauthorizedOperation;
            errorMsg = gError->message;
            g_error_free(gError);
        }

        (*p)->_slotSetupDone(KStorage(new GioHardwareStorage(G_VOLUME(source_object))), error, errorMsg);
        delete p;
    }


}

void GioHardwareStorage::setup(QObject *doneSignalTarget, const char *doneSlot)
{
    if (!isAccessible()) {

        QPointer<GioHardwareStorage> * p = new QPointer<GioHardwareStorage>(this);

        g_volume_mount(m_gVolume,
                G_MOUNT_MOUNT_NONE,
                NULL,
                NULL,
                _mountReadyCallback,
                p);

        connect(this, SIGNAL(setupDone(const KStorage &, const KStorage::ErrorType, const QString &)),
                doneSignalTarget, doneSlot);

    }
    else
    {
        emit setupDone(KStorage::lookup(id()), KStorage::UnauthorizedOperation, "setup not supported");
    }
}

////// unmount //////

static void _unmountReadyCallback(GObject *source_object,
        GAsyncResult *res,
        gpointer user_data)
{
    QPointer<GioHardwareStorage> * p = (QPointer<GioHardwareStorage> *)user_data;
    if (!p->isNull())
    {
        GError * gError = NULL;
        QString errorMsg;

        KStorage::ErrorType error = KStorage::NoError;

        if (!g_mount_unmount_finish(G_MOUNT(source_object),
                res,
                &gError))
        {
            error = KStorage::UnauthorizedOperation;
            errorMsg = gError->message;
            g_error_free(gError);
        }

        GVolume * gVolume = g_mount_get_volume(G_MOUNT(source_object));

        (*p)->_slotTeardownDone(KStorage(new GioHardwareStorage(gVolume)), error, errorMsg);

        g_object_unref(gVolume);
        delete p;
    }
}

void GioHardwareStorage::tearDown(QObject *doneSignalTarget, const char *doneSlot)
{
    GMount * gMount = g_volume_get_mount(m_gVolume);
    if (gMount)
    {
        QPointer<GioHardwareStorage> * p = new QPointer<GioHardwareStorage>(this);

        g_mount_unmount (gMount ,
                G_MOUNT_UNMOUNT_NONE,
                NULL,
                _unmountReadyCallback,
                p);

        connect(this, SIGNAL(teardownDone(const KStorage &, const KStorage::ErrorType, const QString &)),
                doneSignalTarget, doneSlot);

        g_object_unref(gMount);
    }
    else
    {
        emit teardownDone(KStorage::lookup(id()), KStorage::UnauthorizedOperation, "setup not supported");
    }
}

///// eject /////

static void _ejectReadyCallback(GObject *source_object,
        GAsyncResult *res,
        gpointer user_data)
{
    QPointer<GioHardwareStorage> * p = (QPointer<GioHardwareStorage> *)user_data;
    if (!p->isNull())
    {
        GError * gError = NULL;
        QString errorMsg;

        KStorage::ErrorType error = KStorage::NoError;

        if (!g_volume_eject_finish(G_VOLUME(source_object),
                res,
                &gError))
        {
            error = KStorage::UnauthorizedOperation;
            errorMsg = gError->message;
            g_error_free(gError);
        }

        (*p)->_slotEjectDone(KStorage(new GioHardwareStorage(G_VOLUME(source_object))), error, errorMsg);
        delete p;
    }
}

void GioHardwareStorage::eject(QObject *doneSignalTarget, const char *doneSlot)
{


    QPointer<GioHardwareStorage> * p = new QPointer<GioHardwareStorage>(this);

    g_volume_eject(m_gVolume,
            G_MOUNT_UNMOUNT_NONE,
            NULL,
            _ejectReadyCallback,
            p);

    connect(this, SIGNAL(ejectDone(const KStorage &, const KStorage::ErrorType, const QString &)),
            doneSignalTarget, doneSlot);

}

