/***************************************************************************
 * Ported to Collection Framework: *
 * copyright            : (C) 2008 Alejandro Wainzinger <aikawarazuni@gmail.com>

 * Original Work: *
 * copyright            : (C) 2006 Andy Kelk <andy@mopoke.co.uk>
 *
 * 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) version 3 or any later version
 * accepted by the membership of KDE e.V. (or its successor approved
 * by the membership of KDE e.V.), which shall act as a proxy
 * defined in Section 14 of version 3 of the license.
 *
 * 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, see <http://www.gnu.org/licenses/>.
 **************************************************************************/

#ifndef MTPHANDLER_H
#define MTPHANDLER_H

#include <libmtp.h>

#include "Meta.h"
#include "MemoryCollection.h"
#include "MtpMeta.h"
#include "../../../statusbar/StatusBar.h"

#include "kjob.h"
#include <threadweaver/Job.h>

#include <QObject>
#include <QMap>
#include <QMultiMap>
#include <QMutex>

class QString;
class QFile;
class QDateTime;
class QMutex;
class QStringList;

class MtpCollection;

namespace Mtp
{
typedef QMultiMap<QString, Meta::TrackPtr> TitleMap;

/* The libmtp backend for all Mtp calls */
class MtpHandler : public QObject
{
    Q_OBJECT

public:
    MtpHandler( MtpCollection *mc, QObject* parent );
    ~MtpHandler();

    void init( const QString &serial ); // called by collection
    void getDeviceInfo();

    // thread-related functions
    bool iterateRawDevices( int numrawdevices, LIBMTP_raw_device_t* rawdevices, const QString &serial );
    bool privateCopyTrackToDevice( const Meta::TrackPtr &track );

    // external functions
    void copyTrackListToDevice( const Meta::TrackList tracklist );
//       bool deleteTrackFromDevice( const Meta::MtpTrackPtr &track );
    void deleteTracksFromDevice( const Meta::TrackList &tracks );
    int getTrackToFile( const uint32_t id, const QString & filename );
    void parseTracks();
    void updateTrackInDB( const Meta::MtpTrackPtr track );
    QString prettyName() const;
    void terminate();
    bool succeeded() const
    {
        return m_success;
    }

    QMap<Meta::TrackPtr, QString> tracksFailed() const
    {
        return m_tracksFailed;
    }

    // Some internal stuff that must be public due to libmtp being in C

    static int progressCallback( uint64_t const sent, uint64_t const total, void const * const data );
    // Progress Bar functions

    void setBarMaximum( int total );
    void setBarProgress( int steps );
    void endBarProgressOperation();

private:
    // file-copying related functions
    uint32_t checkFolderStructure( const Meta::TrackPtr track, bool create );
    uint32_t getDefaultParentId( void );
    uint32_t folderNameToID( char *name, LIBMTP_folder_t *folderlist );
    uint32_t subfolderNameToID( const char *name, LIBMTP_folder_t *folderlist, uint32_t parent_id );
    uint32_t createFolder( const char *name, uint32_t parent_id );
    void updateFolders( void );

    // file io functions
    bool kioCopyTrack( const KUrl &src, const KUrl &dst );
    void deleteFile( const KUrl &url );



    // internal mtp functions

    int                     readMtpMusic( void );
    void getBasicMtpTrackInfo( LIBMTP_track_t *mtptrack, Meta::MtpTrackPtr track );
    void setBasicMtpTrackInfo( LIBMTP_track_t *trackmeta, Meta::MtpTrackPtr track );

    QString getFormat( LIBMTP_track_t *mtptrack );

    // miscellaneous internal functions
    void addMtpTrackToCollection( LIBMTP_track_t *mtptrack );

    // convenience methods to avoid repetitive code

    void setupArtistMap( LIBMTP_track_t *mtptrack, Meta::MtpTrackPtr track, ArtistMap &artistMap );
    void setupAlbumMap( LIBMTP_track_t *mtptrack, Meta::MtpTrackPtr track, AlbumMap &albumMap );
    void setupGenreMap( LIBMTP_track_t *mtptrack, Meta::MtpTrackPtr track, GenreMap &genreMap );
    void setupComposerMap( LIBMTP_track_t *mtptrack, Meta::MtpTrackPtr track, ComposerMap &composerMap );
    void setupYearMap( LIBMTP_track_t *mtptrack, Meta::MtpTrackPtr track, YearMap &yearMap );

public slots:
    void fileTransferred( KJob *job );
    void fileDeleted( KJob *job );

signals:
    void succeeded();
    void failed();

    void copyTracksDone( bool success );
    void deleteTracksDone();

    void setProgress( int steps );
    void incrementProgress();
    void endProgressOperation( const QObject *owner );

private slots:
    void slotDeviceMatchSucceeded( ThreadWeaver::Job* job );
    void slotDeviceMatchFailed( ThreadWeaver::Job* job );

    void slotCopyNextTrackFailed( ThreadWeaver::Job* job );
    void slotCopyNextTrackToDevice( ThreadWeaver::Job* job );

    void copyNextTrackToDevice();

private:

    void copyTracksToDevice();


    MtpCollection *m_memColl;

    TitleMap m_titlemap;

    void deleteNextTrackFromDevice();
    void privateDeleteTrackFromDevice( const Meta::MtpTrackPtr &track );
    Meta::TrackList m_tracksToCopy;
    Meta::TrackList m_tracksToDelete;

    Meta::TrackPtr m_lastTrackCopied;
    QMap<Meta::TrackPtr, QString> m_tracksFailed;

    ProgressBar *m_statusbar;

    // mtp database

    LIBMTP_mtpdevice_t      *m_device;

    QMap<int, QString>       mtpFileTypes;

    uint32_t                m_default_parent_folder;
    LIBMTP_folder_t        *m_folders;
    QString                 m_folderStructure;
    QString                 m_format;
    QString                 m_name;
    QStringList             m_supportedFiles;

    QMutex                  m_critical_mutex;

    bool m_success; // tells if connecting worked or not
    bool             m_trackCreated;

    // KIO-related Vars (to be moved elsewhere eventually)

    bool m_copyFailed;
    bool m_isCanceled;
    bool m_wait;


};

class WorkerThread : public ThreadWeaver::Job
{
    Q_OBJECT
public:
    WorkerThread( int numrawdevices, LIBMTP_raw_device_t* rawdevices, const QString &serial, MtpHandler* handler );
    virtual ~WorkerThread();

    virtual bool success() const;

protected:
    virtual void run();

private:
    bool m_success;
    int m_numrawdevices;
    LIBMTP_raw_device_t* m_rawdevices;
    QString m_serial;
    MtpHandler *m_handler;
};

class CopyWorkerThread : public ThreadWeaver::Job
{
    Q_OBJECT
public:
    CopyWorkerThread( const Meta::TrackPtr &track, MtpHandler* handler );
    virtual ~CopyWorkerThread();

    virtual bool success() const;

protected:
    virtual void run();

private:
    bool m_success;
    Meta::TrackPtr m_track;
    MtpHandler *m_handler;
};

}
#endif
