/* This file is part of the KDE project
 *
 * Copyright (C) 2011 Valentin Rusu <kde@rusu.info>
 *
 * 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 "ksecretsservicecollection.h"
#include "ksecretsservicecollection_p.h"
#include "dbusbackend.h"
#include "ksecretsservicecollectionjobs.h"
#include "ksecretsservicecollectionjobs_p.h"
#include "collection_interface.h"

#include <QDateTime>
#include <QtDBus/QDBusPendingReply>
#include <kcompositejob.h>
#include <QTimer>
#include <kjob.h>
#include <kdebug.h>

namespace KSecretsService {

Collection::Collection(): 
        QObject(),
        d( new CollectionPrivate( this ) )
{
    // nothing to do
}

Collection::~Collection()
{
}

Collection * KSecretsService::Collection::findCollection(const QString& collectionName,
                                                         KSecretsService::Collection::FindCollectionOptions options /* = CreateCollection */,
                                                         const QVariantMap collectionProperties /* = QVariantMap() */,
                                                         const WId &promptParentWindowId /* =0 */ )
{
    // this will simply return the C++ collection objet, without trying to connect to the daemon
    // this will be handled later on, when first calls to other methods will happen
    Collection *collection = new Collection();
    collection->d->setPendingFindCollection( promptParentWindowId, collectionName, collectionProperties, options );
    return collection;
}

ListCollectionsJob *Collection::listCollections()
{
    return new ListCollectionsJob();
}

Collection::Status Collection::status() const
{
    return d->collectionStatus;
}

KJob* Collection::deleteCollection()
{
    return new DeleteCollectionJob( this );
}

KJob* Collection::renameCollection(const QString& newName)
{
    return new RenameCollectionJob( this, newName, this );
}

SearchCollectionItemsJob* Collection::searchItems(const QStringStringMap& attributes)
{
    return new SearchCollectionItemsJob( this, attributes, this );
}

SearchCollectionSecretsJob* Collection::searchSecrets(const QStringStringMap& attributes)
{
    return new SearchCollectionSecretsJob( this, attributes, this );
}

CreateCollectionItemJob* Collection::createItem(const QString& label, const QMap< QString, QString >& attributes, const Secret& secret, CreateItemOptions options /* = DoNotReplaceExistingItem */)
{
    return new CreateCollectionItemJob( this, label, attributes, secret, options );
}

ReadCollectionItemsJob* Collection::items() const
{
    return new ReadCollectionItemsJob( const_cast< Collection* >(this) );
}

ReadCollectionPropertyJob* Collection::isLocked() const
{
    return new ReadCollectionPropertyJob( const_cast< Collection* >(this), "Locked" );
}

ReadCollectionPropertyJob* Collection::label() const
{
    return new ReadCollectionPropertyJob( const_cast< Collection* >(this), "Label" );
}

ReadCollectionPropertyJob* Collection::createdTime() const
{
    return new ReadCollectionPropertyJob( const_cast< Collection* >(this), "Created" );
}

ReadCollectionPropertyJob* Collection::modifiedTime() const
{
    return new ReadCollectionPropertyJob( const_cast< Collection* >(this), "Modified" );
}

WriteCollectionPropertyJob* Collection::setLabel(const QString& label)
{
    return new WriteCollectionPropertyJob( this, "Label", QVariant( label ) );
}

ChangeCollectionPasswordJob* Collection::changePassword()
{
    return new ChangeCollectionPasswordJob( this );
}

LockCollectionJob *Collection::lock( const WId wid /* =0 */ )
{
    return new LockCollectionJob( this, wid );
}

void Collection::emitStatusChanged()
{
    emit statusChanged( d->collectionStatus );
}

void Collection::emitContentsChanged()
{
    emit contentsChanged();
}

void Collection::emitDeleted()
{
    emit deleted();
}


QMap< QDBusObjectPath, CollectionPrivate* > CollectionPrivate::collectionMap;

CollectionPrivate::CollectionPrivate( Collection *coll ) :
        collection( coll ),
        findOptions( Collection::OpenOnly ),
        collectionStatus( Collection::Invalid ),
        collectionIf( 0 )
{
}

CollectionPrivate::~CollectionPrivate()
{
    if ( collectionMap.contains( dbusPath ) ) {
        collectionMap.remove( dbusPath );
    }
}

void CollectionPrivate::setPendingFindCollection( const WId &promptParentId,
                                                  const QString &collName, 
                                                  const QVariantMap &collProps,
                                                  Collection::FindCollectionOptions opts ) 
{
    collectionName = collName;
    collectionProperties = collProps;
    findOptions = opts;
    setStatus( Collection::Pending );
    promptParentWindowId = promptParentId;
}

void CollectionPrivate::setStatus( Collection::Status newStatus )
{
    collectionStatus = newStatus;
    collection->emitStatusChanged();
}

bool CollectionPrivate::isValid()
{
    // NOTE: do not call collectionInterface() to get the interface pointer, if not you'll get an infinite recursive call
    return 
        collectionIf && collectionIf->isValid() && (
            collectionStatus == Collection::FoundExisting ||
            collectionStatus == Collection::NewlyCreated );
}

void CollectionPrivate::setDBusPath( const QDBusObjectPath &path )
{
    collectionIf = DBusSession::createCollectionIf( path );
    if ( collectionIf->isValid() ) {
        kDebug() << "SUCCESS opening collection " << path.path();
        collectionMap.insert( path, this );
        dbusPath = path;
    }
    else {
        setStatus( Collection::NotFound );
        kDebug() << "ERROR opening collection " << path.path();
    }
}

const WId & CollectionPrivate::promptParentId() const 
{
    return promptParentWindowId;
}

OrgFreedesktopSecretCollectionInterface *CollectionPrivate::collectionInterface()
{
    if ( (collectionIf == 0) || ( !collectionIf->isValid() ) ) {
        // well, some attribute read method is now happening and we should now really open or find the collection
        // the only problem is that we'll be forced to call findJob->exec() to do this and it's evil :-)
        FindCollectionJob *findJob = new FindCollectionJob( collection, 0 );
        if ( !findJob->exec() ) {
            kDebug() << "FindCollectionJob failed!";
        }
        if ( collectionIf == 0 ) {
           kDebug() << "WARNING: returning NULL collectionInterface";
        }
    }
    return collectionIf;
}

ReadCollectionPropertyJob* Collection::isValid()
{
    return new ReadCollectionPropertyJob( this, &Collection::readIsValid, this );
}

void Collection::readIsValid( ReadCollectionPropertyJob *readPropertyJob)
{
    readPropertyJob->d->value = d->isValid();
}

void CollectionPrivate::notifyCollectionChanged( const QDBusObjectPath& path )
{
    if ( collectionMap.contains( path ) ) {
        CollectionPrivate *cp = collectionMap[ path ];
        cp->collection->emitContentsChanged();
    }
    else {
        kDebug() << "Ignoring notifyCollectionChanged for " << path.path();
    }
}

void CollectionPrivate::notifyCollectionDeleted( const QDBusObjectPath& path )
{
    if ( collectionMap.contains( path ) ) {
        CollectionPrivate *cp = collectionMap[ path ];
        cp->collection->emitDeleted();
    }
    else {
        kDebug() << "Ignoring notifyCollectionDeleted for " << path.path();
    }
}


#include "ksecretsservicecollection.moc"

    
};
