/*
 * Cantata
 *
 * Copyright (c) 2011-2022 Craig Drummond <craig.p.drummond@gmail.com>
 *
 * ----
 *
 * 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; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#ifndef MTPDEVICE_H
#define MTPDEVICE_H

#include "config.h"
#include "fsdevice.h"
#include "mpd-interface/song.h"
#include "solid-lite/portablemediaplayer.h"
#include <libmtp.h>

class MusicLibraryItemRoot;
class Thread;
class QTemporaryFile;

class MtpConnection : public QObject {
	Q_OBJECT
public:
	struct File {
		File(const QString& n = QString(), uint64_t s = 0, uint32_t i = 0) : name(n), size(s), id(i) {}
		QString name;
		uint64_t size;
		uint32_t id;
	};

	struct Folder {
		Folder(const QString& pth = QString(), uint32_t i = 0, uint32_t p = 0, uint32_t s = 0)
			: path(pth), id(i), parentId(p), storageId(s)
		{
		}
		QString path;
		uint32_t id;
		uint32_t parentId;
		uint32_t storageId;
		QSet<uint32_t> folders;
		QMap<uint32_t, File> files;
		QMap<uint32_t, File> covers;
	};

	struct Storage : public DeviceStorage {
		Storage() : id(0), musicFolderId(0) {}
		uint32_t id;
		uint32_t musicFolderId;
		QString musicPath;
	};

	MtpConnection(const QString& id, unsigned int bus, unsigned int dev, bool aaSupport);
	virtual ~MtpConnection();

	bool isConnected() const { return 0 != device; }
	MusicLibraryItemRoot* takeLibrary();
	uint64_t capacity() const { return size; }
	uint64_t usedSpace() const { return used; }
	QList<DeviceStorage> getStorageList() const;
	void emitProgress(int percent);
	void trackListProgress(int percent);
	void requestAbort(bool r) { abortRequested = r; }
	bool abortWasRequested() const { return abortRequested; }

public Q_SLOTS:
	void connectToDevice();
	void disconnectFromDevice(bool showStatus = true);
	void updateLibrary(const DeviceOptions& opts);
	void putSong(const Song& song, bool fixVa, const DeviceOptions& opts, bool overwrite, bool copyCover);
	void getSong(const Song& song, const QString& dest, bool fixVa, bool copyCover);
	void delSong(const Song& song);
	void cleanDirs(const QSet<QString>& dirs);
	void getCover(const Song& song);
	void stop();

Q_SIGNALS:
	void statusMessage(const QString& message);
	void putSongStatus(int, const QString&, bool, bool);
	void getSongStatus(bool ok, bool copiedCover);
	void delSongStatus(bool);
	void cleanDirsStatus(bool ok);
	void libraryUpdated();
	void progress(int);
	void deviceDetails(const QString& serialNumber);
	void updatePercentage(int);
	void cover(const Song& s, const QImage& img);

private:
	File getCoverDetils(const Song& s);
	bool removeFolder(uint32_t folderId);
	void updateFilesAndFolders();
	void listFolder(uint32_t storage, uint32_t parentDir, Folder* f = 0);
	void updateStorage();
	Storage& getStorage(const QString& volumeIdentifier);
	Storage& getStorage(uint32_t id);
	uint32_t createFolder(const QString& name, const QString& fullPath, uint32_t parentId, uint32_t storageId);
	uint32_t getFolder(const QString& path, uint32_t storageId);
	QString getPath(uint32_t folderId);
	uint32_t checkFolderStructure(const QStringList& dirs, Storage& store);
	void setMusicFolder(Storage& store);
#ifdef MTP_CLEAN_ALBUMS
	void updateAlbums();
	LIBMTP_album_t* getAlbum(const Song& song);
#endif
	void destroyData();

private:
	Thread* thread;
	LIBMTP_mtpdevice_t* device;
#ifdef MTP_CLEAN_ALBUMS
	LIBMTP_album_t* albums;
#endif
	QMap<uint32_t, Folder> folderMap;
	MusicLibraryItemRoot* library;
	uint32_t defaultMusicFolder;
	QList<Storage> storage;
	uint64_t size;
	uint64_t used;
	int lastListPercent;
	bool abortRequested;
	unsigned int busNum;
	unsigned int devNum;
	bool supprtAlbumArtistTag;
};

class MtpDevice : public Device {
	Q_OBJECT

public:
	MtpDevice(MusicLibraryModel* m, Solid::Device& dev, unsigned int busNum, unsigned int devNum);
	virtual ~MtpDevice();

	bool isConnected() const;
	bool isRefreshing() const { return mtpUpdating; }
	void stop();
	void configure(QWidget* parent);
	QString path() const { return QString(); }// audioFolder; }
	void addSong(const Song& s, bool overwrite, bool copyCover);
	void copySongTo(const Song& s, const QString& musicPath, bool overwrite, bool copyCover);
	void removeSong(const Song& s);
	void cleanDirs(const QSet<QString>& dirs);
	Covers::Image requestCover(const Song& song);
	double usedCapacity();
	QString capacityString();
	qint64 freeSpace();
	DevType devType() const { return Mtp; }
	void saveOptions();
	void abortJob() { requestAbort(true); }

Q_SIGNALS:
	// These are for talking to connection thread...
	void updateLibrary(const DeviceOptions& opts);
	void putSong(const Song& song, bool fixVa, const DeviceOptions& opts, bool overwrite, bool copyCover);
	void getSong(const Song& song, const QString& dest, bool fixVa, bool copyCover);
	void delSong(const Song& song);
	void cleanMusicDirs(const QSet<QString>& dirs);
	void getCover(const Song& s);
	void cover(const Song& s, const QImage& img);

private Q_SLOTS:
	void deviceDetails(const QString& s);
	void libraryUpdated();
	void rescan(bool full = true);
	void putSongStatus(int status, const QString& file, bool fixedVa, bool copiedCover);
	void transcodeSongResult(int status);
	void transcodePercent(int percent);
	void emitProgress(int);
	void getSongStatus(bool ok, bool copiedCover);
	void delSongStatus(bool ok);
	void cleanDirsStatus(bool ok);
	void saveProperties(const QString& newPath, const DeviceOptions& opts);
	void saveProperties();

private:
	void deleteTemp();
	void requestAbort(bool r)
	{
		if (connection) connection->requestAbort(r);
		jobAbortRequested = r;
	}

private:
	Solid::PortableMediaPlayer* pmp;
	MtpConnection* connection;
	QTemporaryFile* tempFile;
	Song currentSong;
	bool mtpUpdating;
	QString serial;
	QString defaultName;
	friend class MtpConnection;
};

#endif
