/*
   SPDX-FileCopyrightText: 2016 (c) Matthieu Gallien <matthieu_gallien@yahoo.fr>
   SPDX-FileCopyrightText: 2019 (c) Alexander Stippich <a.stippich@gmx.net>

   SPDX-License-Identifier: LGPL-3.0-or-later
 */

#ifndef DATATYPES_H
#define DATATYPES_H

#include "elisaLib_export.h"

#include "elisautils.h"

#include <QObject>
#include <QString>
#include <QList>
#include <QVariant>
#include <QUrl>
#include <QDateTime>
#include <QMap>

class ELISALIB_EXPORT DataTypes : public QObject
{
    Q_OBJECT

public:

    enum ColumnsRoles {
        TitleRole = Qt::UserRole + 1,
        SecondaryTextRole,
        ImageUrlRole,
        ShadowForImageRole,
        ChildModelRole,
        DurationRole,
        StringDurationRole,
        ArtistRole,
        AllArtistsRole,
        HighestTrackRating,
        AlbumRole,
        AlbumArtistRole,
        IsValidAlbumArtistRole,
        TrackNumberRole,
        DiscNumberRole,
        RatingRole,
        GenreRole,
        LyricistRole,
        ComposerRole,
        CommentRole,
        YearRole,
        ChannelsRole,
        BitRateRole,
        SampleRateRole,
        ResourceRole,
        IdRole,
        ParentIdRole,
        DatabaseIdRole,
        IsSingleDiscAlbumRole,
        ContainerDataRole,
        IsPartialDataRole,
        AlbumIdRole,
        HasEmbeddedCover,
        FileModificationTime,
        FirstPlayDate,
        LastPlayDate,
        PlayCounter,
        PlayFrequency,
        ElementTypeRole,
        LyricsRole,
        FullDataRole,
        IsDirectoryRole,
        IsPlayListRole,
        FilePathRole,
    };

    Q_ENUM(ColumnsRoles)

    using DataType = QMap<ColumnsRoles, QVariant>;

public:

    class MusicDataType : public DataType
    {
    public:
        using DataType::DataType;

        bool hasDatabaseId() const
        {
            return find(key_type::DatabaseIdRole) != end();
        }

        qulonglong databaseId() const
        {
            return operator[](key_type::DatabaseIdRole).toULongLong();
        }

        bool hasElementType() const
        {
            return find(key_type::ElementTypeRole) != end();
        }

        ElisaUtils::PlayListEntryType elementType() const
        {
            return operator[](key_type::ElementTypeRole).value<ElisaUtils::PlayListEntryType>();
        }
    };

    class TrackDataType : public MusicDataType
    {
    public:

        using MusicDataType::MusicDataType;

        TrackDataType(bool aValid, QString aId, QString aParentId, QString aTitle, QString aArtist, QString aAlbumName,
                      QString aAlbumArtist, int aTrackNumber, int aDiscNumber, QTime aDuration, QUrl aResourceURI,
                      const QDateTime &fileModificationTime, QUrl aAlbumCover, int rating, bool aIsSingleDiscAlbum,
                      QString aGenre, QString aComposer, QString aLyricist, bool aHasEmbeddedCover)
            : MusicDataType({{key_type::TitleRole, std::move(aTitle)}, {key_type::AlbumRole, std::move(aAlbumName)},
                             {key_type::ArtistRole, std::move(aArtist)}, {key_type::AlbumArtistRole, std::move(aAlbumArtist)},
                             {key_type::IdRole, std::move(aId)}, {key_type::ParentIdRole, std::move(aParentId)},
                             {key_type::TrackNumberRole, aTrackNumber}, {key_type::DiscNumberRole, aDiscNumber},
                             {key_type::DurationRole, aDuration}, {key_type::ResourceRole, std::move(aResourceURI)},
                             {key_type::FileModificationTime, fileModificationTime}, {key_type::ImageUrlRole, std::move(aAlbumCover)},
                             {key_type::RatingRole, rating}, {key_type::IsSingleDiscAlbumRole, aIsSingleDiscAlbum},
                             {key_type::GenreRole, std::move(aGenre)}, {key_type::ComposerRole, std::move(aComposer)},
                             {key_type::LyricistRole, std::move(aLyricist)}, {key_type::HasEmbeddedCover, aHasEmbeddedCover},})
        {
            Q_UNUSED(aValid)
        }

        bool isValid() const
        {
            return !isEmpty() && duration().isValid();
        }

        QString title() const
        {
            return operator[](key_type::TitleRole).toString();
        }

        QString artist() const
        {
            return operator[](key_type::ArtistRole).toString();
        }

        qulonglong albumId() const
        {
            return operator[](key_type::AlbumIdRole).toULongLong();
        }

        bool hasAlbum() const
        {
            return find(key_type::AlbumRole) != end();
        }

        QString album() const
        {
            return operator[](key_type::AlbumRole).toString();
        }

        QString albumArtist() const
        {
            return operator[](key_type::AlbumArtistRole).toString();
        }

        bool hasAlbumArtist() const
        {
            return find(key_type::AlbumArtistRole) != end();
        }

        bool hasTrackNumber() const
        {
            return find(key_type::TrackNumberRole) != end();
        }

        int trackNumber() const
        {
            return operator[](key_type::TrackNumberRole).toInt();
        }

        bool hasDiscNumber() const
        {
            return find(key_type::DiscNumberRole) != end();
        }

        int discNumber() const
        {
            return operator[](key_type::DiscNumberRole).toInt();
        }

        QTime duration() const
        {
            return operator[](key_type::DurationRole).toTime();
        }

        QUrl resourceURI() const
        {
            return operator[](key_type::ResourceRole).toUrl();
        }

        QUrl albumCover() const
        {
            return operator[](key_type::ImageUrlRole).toUrl();
        }

        bool isSingleDiscAlbum() const
        {
            return operator[](key_type::IsSingleDiscAlbumRole).toBool();
        }

        int rating() const
        {
            return operator[](key_type::RatingRole).toInt();
        }

        QString genre() const
        {
            return operator[](key_type::GenreRole).toString();
        }

        QString composer() const
        {
            return operator[](key_type::ComposerRole).toString();
        }

        QString lyricist() const
        {
            return operator[](key_type::LyricistRole).toString();
        }

        QString lyrics() const
        {
            return operator[](key_type::LyricsRole).toString();
        }

        QString comment() const
        {
            return operator[](key_type::CommentRole).toString();
        }

        int year() const
        {
            return operator[](key_type::YearRole).toInt();
        }

        int channels() const
        {
            return operator[](key_type::ChannelsRole).toInt();
        }

        bool hasChannels() const
        {
            return find(key_type::ChannelsRole) != end();
        }

        int bitRate() const
        {
            return operator[](key_type::BitRateRole).toInt();
        }

        bool hasBitRate() const
        {
            return find(key_type::BitRateRole) != end();
        }

        int sampleRate() const
        {
            return operator[](key_type::SampleRateRole).toInt();
        }

        bool hasSampleRate() const
        {
            return find(key_type::SampleRateRole) != end();
        }


        bool hasEmbeddedCover() const
        {
            return operator[](key_type::HasEmbeddedCover).toBool();
        }

        QDateTime fileModificationTime() const
        {
            return operator[](key_type::FileModificationTime).toDateTime();
        }
    };

    using ListTrackDataType = QList<TrackDataType>;

    using ListRadioDataType = QList<TrackDataType>;

    class AlbumDataType : public MusicDataType
    {
    public:

        using MusicDataType::MusicDataType;

        QString title() const
        {
            return operator[](key_type::TitleRole).toString();
        }

        QString artist() const
        {
            return operator[](key_type::ArtistRole).toString();
        }

        bool isValidArtist() const
        {
            const auto &artistData = operator[](key_type::ArtistRole);
            return artistData.isValid() && !artistData.toString().isEmpty();
        }

        QStringList genres() const
        {
            return operator[](key_type::GenreRole).toStringList();
        }

        QUrl albumArtURI() const
        {
            return operator[](key_type::ImageUrlRole).toUrl();
        }

        bool isSingleDiscAlbum() const
        {
            return operator[](key_type::IsSingleDiscAlbumRole).toBool();
        }

        bool isValid() const
        {
            return !isEmpty();
        }

    };

    using ListAlbumDataType = QList<AlbumDataType>;

    class ArtistDataType : public MusicDataType
    {
    public:

        using MusicDataType::MusicDataType;

        QString name() const
        {
            return operator[](key_type::TitleRole).toString();
        }

        qulonglong databaseId() const
        {
            return operator[](key_type::DatabaseIdRole).toULongLong();
        }

    };

    using ListArtistDataType = QList<ArtistDataType>;

    class GenreDataType : public MusicDataType
    {
    public:

        using MusicDataType::MusicDataType;

        QString title() const
        {
            return operator[](key_type::TitleRole).toString();
        }

    };

    using ListGenreDataType = QList<GenreDataType>;

    using EntryData = std::tuple<MusicDataType, QString, QUrl>;
    using EntryDataList = QList<EntryData>;

};

Q_DECLARE_METATYPE(DataTypes::MusicDataType)
Q_DECLARE_METATYPE(DataTypes::TrackDataType)
Q_DECLARE_METATYPE(DataTypes::AlbumDataType)
Q_DECLARE_METATYPE(DataTypes::ArtistDataType)
Q_DECLARE_METATYPE(DataTypes::GenreDataType)

Q_DECLARE_METATYPE(DataTypes::ListTrackDataType)
Q_DECLARE_METATYPE(DataTypes::ListAlbumDataType)
Q_DECLARE_METATYPE(DataTypes::ListArtistDataType)
Q_DECLARE_METATYPE(DataTypes::ListGenreDataType)

Q_DECLARE_METATYPE(DataTypes::EntryData)
Q_DECLARE_METATYPE(DataTypes::EntryDataList)

#endif // DATATYPES_H
