/*
 * 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 DEVICE_H
#define DEVICE_H

#include "config.h"
#include "gui/covers.h"
#include "models/musiclibraryitemroot.h"
#include "mpd-interface/song.h"
#ifdef ENABLE_DEVICES_SUPPORT
#include "deviceoptions.h"
#include "solid-lite/device.h"
#endif

class QWidget;
class QImage;
class QTemporaryFile;
class MusicLibraryModel;

// MOC requires the QObject class to be first. But due to models storing void pointers, and
// needing to cast these - the model prefers MusicLibraryItemRoot to be first!
#ifdef Q_MOC_RUN
class Device : public QObject, public MusicLibraryItemRoot
#else
class Device : public MusicLibraryItemRoot, public QObject
#endif
{
	Q_OBJECT

public:
#ifdef ENABLE_DEVICES_SUPPORT
	static const QLatin1String constNoCover;
	static const QLatin1String constEmbedCover;
	static Device* create(MusicLibraryModel* m, const QString& id);
	static bool fixVariousArtists(const QString& file, Song& song, bool applyFix);
	static void embedCover(const QString& file, Song& song, unsigned int coverMaxSize);
	static QTemporaryFile* copySongToTemp(Song& song);
	static bool isLossless(const QString& file);
#endif
	static void moveDir(const QString& from, const QString& to, const QString& base, const QString& coverFile);
	static void cleanDir(const QString& dir, const QString& base, const QString& coverFile, int level = 0);

	enum Status {
		Ok,
		FileExists,
		SongExists,
		DirCreationFaild,
		SourceFileDoesNotExist,
		SongDoesNotExist,
		Failed,
		NotConnected,
		CodecNotAvailable,
		TranscodeFailed,
		FailedToCreateTempFile,
		ReadFailed,
		WriteFailed,
		NoSpace,
		FailedToUpdateTags,
		Cancelled,
		FailedToLockDevice,

		// These are for online services...
		DownloadFailed
	};

	enum DevType {
		Ums,
		Mtp,
		RemoteFs,
		AudioCd
	};

#ifndef ENABLE_DEVICES_SUPPORT
	Device(MusicLibraryModel* m, const QString& name, const QString& id)
		: MusicLibraryItemRoot(name), update(0), needToFixVa(false), jobAbortRequested(false), transcoding(false)
	{
		Q_UNUSED(m)
		Q_UNUSED(id)
	}
#else
	Device(MusicLibraryModel* m, Solid::Device& dev, bool albumArtistSupport = true, bool flat = false);
	Device(MusicLibraryModel* m, const QString& name, const QString& id);
#endif

	~Device() override {}

	QIcon icon() const override { return icn; }
	Song fixPath(const Song& orig, bool fullPath) const override;
	virtual QString coverFile() const { return QString(); }
	virtual bool isConnected() const = 0;
	virtual void rescan(bool full = true) = 0;
	virtual void stop() = 0;
	virtual void connectionStateChanged() {}
	virtual void toggle() {}
	virtual bool isRefreshing() const = 0;
	bool isIdle() const { return isConnected() && !isRefreshing(); }
	virtual void configure(QWidget*) {}
	virtual QString path() const = 0;
	virtual void addSong(const Song& s, bool overwrite, bool copyCover) = 0;
	virtual void copySongTo(const Song& s, const QString& musicPath, bool overwrite, bool copyCover) = 0;
	virtual void removeSong(const Song& s) = 0;
	virtual void cleanDirs(const QSet<QString>& dirs) = 0;
	virtual Covers::Image requestCover(const Song&) { return Covers::Image(); }
	virtual double usedCapacity() = 0;
	virtual QString capacityString() = 0;
	virtual qint64 freeSpace() = 0;
	virtual DevType devType() const = 0;
	virtual void removeCache() {}
	bool isDevice() const override { return true; }

#ifdef ENABLE_DEVICES_SUPPORT
	virtual void saveCache();
	const QString& id() const override { return deviceId; }
	void applyUpdate();
	bool haveUpdate() const { return nullptr != update; }
	int newRows() const { return update ? update->childCount() : 0; }
	const DeviceOptions& options() const { return opts; }
	void setOptions(const DeviceOptions& o)
	{
		opts = o;
		saveOptions();
	}
	virtual void saveOptions() = 0;
	const QString& statusMessage() const { return statusMsg; }
	bool isConfigured() { return configured; }
	virtual void abortJob() { jobAbortRequested = true; }
	bool abortRequested() const { return jobAbortRequested; }
	bool canPlaySongs() const override { return false; }
	virtual bool supportsDisconnect() const { return false; }
	virtual bool isStdFs() const { return false; }
	virtual QString subText() { return QString(); }
	QModelIndex index() const override;
	void updateStatus();

public Q_SLOTS:
	void setStatusMessage(const QString& message);
	void songCount(int c);
	void updatePercentage(int pc);
#endif

Q_SIGNALS:
	void connected(const QString& id);
	void disconnected(const QString& id);
	void updating(const QString& id, bool s);
	void actionStatus(int status, bool copiedCover = false);
	void progress(int pc);
	void error(const QString&);
	void cover(const Song& song, const QImage& img);
	void cacheSaved();
	void configurationChanged();
	void play(const QList<Song>& songs);
	void updatedDetails(const QList<Song>& songs);
	void renamed();

protected:
#ifdef ENABLE_DEVICES_SUPPORT
	DeviceOptions opts;
	bool configured;
	Solid::Device solidDev;
	QString deviceId;
	Song currentSong;
#endif
	MusicLibraryItemRoot* update;
	QString currentDestFile;
	QString statusMsg;
	bool needToFixVa;
	bool jobAbortRequested;
	bool transcoding;
	QIcon icn;
};

#endif
