#pragma once

/*
 * SPDX-FileCopyrightText: 2003-2007 Craig Drummond <craig@kde.org>
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#include "Family.h"
#include "FcQuery.h"
#include "File.h"
#include "FontFilter.h"
#include "FontInst.h"
#include "JobRunner.h"
#include "Misc.h"
#include "Style.h"
#include <KFileItem>
#include <KIO/Job>
#include <QAbstractItemModel>
#include <QHash>
#include <QList>
#include <QModelIndex>
#include <QSet>
#include <QSortFilterProxyModel>
#include <QTreeView>
#include <QUrl>
#include <QVariant>

class QMenu;
class QMimeData;
class QTimer;

inline constexpr QLatin1StringView KFI_FONT_DRAG_MIME("kfontinst/fontlist");

namespace KFI
{
class CFontItem;
class CFontItem;
class CFamilyItem;
class CGroupListItem;
class Style;

enum EColumns {
    COL_FONT,
    COL_STATUS,

    NUM_COLS,
};

typedef QList<CFamilyItem *> CFamilyItemCont;
typedef QList<CFontItem *> CFontItemCont;
typedef QHash<QString, CFamilyItem *> CFamilyItemHash;

class CFontList : public QAbstractItemModel
{
    Q_OBJECT

private:
    enum EMsgType {
        MSG_ADD,
        MSG_DEL,

        NUM_MSGS_TYPES,
    };

public:
    static const QStringList fontMimeTypes;

public:
    static QStringList compact(const QStringList &fonts);

    CFontList(QWidget *parent = nullptr);
    ~CFontList() override;

    QVariant data(const QModelIndex &index, int role) const override;
    Qt::ItemFlags flags(const QModelIndex &index) const override;
    Qt::DropActions supportedDropActions() const override;
    QMimeData *mimeData(const QModelIndexList &indexes) const override;
    QStringList mimeTypes() const override;
    QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
    QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
    QModelIndex parent(const QModelIndex &index) const override;
    int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    int columnCount(const QModelIndex &parent = QModelIndex()) const override;
    int row(const CFamilyItem *fam) const
    {
        return m_families.indexOf((CFamilyItem *)fam);
    }
    void forceNewPreviews();
    const CFamilyItemCont &families() const
    {
        return m_families;
    }
    QModelIndex createIndex(int row, int column, void *data = nullptr) const
    {
        return QAbstractItemModel::createIndex(row, column, data);
    }
    bool hasFamily(const QString &family)
    {
        return nullptr != findFamily(family);
    }
    void refresh(bool allowSys, bool allowUser);
    bool allowSys() const
    {
        return m_allowSys;
    }
    bool allowUser() const
    {
        return m_allowUser;
    }
    void getFamilyStats(QSet<QString> &enabled, QSet<QString> &disabled, QSet<QString> &partial);
    void getFoundries(QSet<QString> &foundries) const;
    QString whatsThis() const;
    void setSlowUpdates(bool slow);
    bool slowUpdates() const
    {
        return m_slowUpdates;
    }

Q_SIGNALS:

    void listingPercent(int p);

public Q_SLOTS:

    void unsetSlowUpdates();
    void load();

private Q_SLOTS:

    void dbusServiceOwnerChanged(const QString &name, const QString &from, const QString &to);
    void fontList(int pid, const QList<KFI::Families> &families);
    void fontsAdded(const KFI::Families &families);
    void fontsRemoved(const KFI::Families &families);

private:
    void storeSlowedMessage(const Families &families, EMsgType type);
    void actionSlowedUpdates(bool sys);
    void addFonts(const FamilyCont &families, bool sys);
    void removeFonts(const FamilyCont &families, bool sys);
    CFamilyItem *findFamily(const QString &familyName);

private:
    CFamilyItemCont m_families;
    CFamilyItemHash m_familyHash;
    bool m_blockSignals, m_allowSys, m_allowUser, m_slowUpdates;
    static int theirPreviewSize;
    FamilyCont m_slowedMsgs[NUM_MSGS_TYPES][FontInst::FOLDER_COUNT];
};

class CFontModelItem
{
public:
    CFontModelItem(CFontModelItem *p)
        : m_parent(p)
        , m_isSystem(false)
    {
    }
    virtual ~CFontModelItem() = default;

    CFontModelItem *parent() const
    {
        return m_parent;
    }
    bool isFamily() const
    {
        return nullptr == m_parent;
    }
    bool isFont() const
    {
        return nullptr != m_parent;
    }
    bool isSystem() const
    {
        return m_isSystem;
    }
    void setIsSystem(bool sys)
    {
        m_isSystem = sys;
    }
    virtual int rowNumber() const = 0;

protected:
    CFontModelItem *m_parent;
    bool m_isSystem;
};

class CFamilyItem : public CFontModelItem
{
public:
    enum EStatus {
        ENABLED,
        PARTIAL,
        DISABLED,
    };

    CFamilyItem(CFontList &p, const Family &f, bool sys);
    ~CFamilyItem() override;

    bool operator==(const CFamilyItem &other) const
    {
        return m_name == other.m_name;
    }

    bool addFonts(const StyleCont &styles, bool sys);
    const QString &name() const
    {
        return m_name;
    }
    const CFontItemCont &fonts() const
    {
        return m_fonts;
    }
    void addFont(CFontItem *font, bool update = true);
    void removeFont(CFontItem *font, bool update);
    void refresh();
    bool updateStatus();
    bool updateRegularFont(CFontItem *font);
    CFontItem *findFont(quint32 style, bool sys);
    int rowNumber() const override
    {
        return m_parent.row(this);
    }
    int row(const CFontItem *font) const
    {
        return m_fonts.indexOf((CFontItem *)font);
    }
    EStatus status() const
    {
        return m_status;
    }
    EStatus realStatus() const
    {
        return m_realStatus;
    }
    CFontItem *regularFont()
    {
        return m_regularFont;
    }
    int fontCount() const
    {
        return m_fontCount;
    }
    void getFoundries(QSet<QString> &foundries) const;
    bool slowUpdates() const
    {
        return m_parent.slowUpdates();
    }

private:
    bool usable(const CFontItem *font, bool root);

private:
    QString m_name;
    CFontItemCont m_fonts;
    int m_fontCount;
    EStatus m_status, m_realStatus;
    CFontItem *m_regularFont; // 'RegularFont' is font nearest to 'Regular' style, and used for previews.
    CFontList &m_parent;
};

class CFontItem : public CFontModelItem
{
public:
    CFontItem(CFontModelItem *p, const Style &s, bool sys);
    ~CFontItem() override = default;

    void refresh();
    QString name() const
    {
        return family() + QString::fromLatin1(", ") + m_styleName;
    }
    bool isEnabled() const
    {
        return m_enabled;
    }
    bool isHidden() const
    {
        return !m_enabled;
    }
    bool isBitmap() const
    {
        return !m_style.scalable();
    }
    const QString &fileName() const
    {
        return (*m_style.files().begin()).path();
    }
    const QString &style() const
    {
        return m_styleName;
    }
    quint32 styleInfo() const
    {
        return m_style.value();
    }
    int index() const
    {
        return (*m_style.files().begin()).index();
    }
    const QString &family() const
    {
        return (static_cast<CFamilyItem *>(parent()))->name();
    }
    int rowNumber() const override
    {
        return (static_cast<CFamilyItem *>(parent()))->row(this);
    }
    const FileCont &files() const
    {
        return m_style.files();
    }
    qulonglong writingSystems() const
    {
        return m_style.writingSystems();
    }
    QUrl url() const
    {
        return CJobRunner::encode(family(), styleInfo(), isSystem());
    }
    void removeFile(const File &f)
    {
        m_style.remove(f);
    }
    void removeFiles(const FileCont &f)
    {
        m_style.removeFiles(f);
    }
    void addFile(const File &f)
    {
        m_style.add(f);
    }
    void addFiles(const FileCont &f)
    {
        m_style.addFiles(f);
    }

private:
    QString m_styleName;
    Style m_style;
    bool m_enabled;
};

class CFontListSortFilterProxy : public QSortFilterProxyModel
{
    Q_OBJECT

public:
    CFontListSortFilterProxy(QObject *parent, QAbstractItemModel *model);
    ~CFontListSortFilterProxy() override = default;

    QVariant data(const QModelIndex &idx, int role) const override;
    bool acceptFont(CFontItem *fnt, bool checkFontText) const;
    bool acceptFamily(CFamilyItem *fam) const;
    bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
    bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
    void setFilterGroup(CGroupListItem *grp);
    CGroupListItem *filterGroup()
    {
        return m_group;
    }

    void setFilterText(const QString &text);
    void setFilterCriteria(CFontFilter::ECriteria crit, qulonglong ws, const QStringList &ft);

private Q_SLOTS:

    void timeout();
    void fcResults();

Q_SIGNALS:

    void refresh();

private:
    QString filterText() const
    {
        return CFontFilter::CRIT_FONTCONFIG == m_filterCriteria ? (m_fcQuery ? m_fcQuery->font() : QString()) : m_filterText;
    }

private:
    CGroupListItem *m_group;
    QString m_filterText;
    CFontFilter::ECriteria m_filterCriteria;
    qulonglong m_filterWs;
    QStringList m_filterTypes;
    QTimer *m_timer;
    CFcQuery *m_fcQuery;
};

class CFontListView : public QTreeView
{
    Q_OBJECT

public:
    CFontListView(QWidget *parent, CFontList *model);
    ~CFontListView() override = default;

    void getFonts(CJobRunner::ItemList &urls, QStringList &fontNames, QSet<Misc::TFont> *fonts, bool selected, bool getEnabled = true, bool getDisabled = true);
    QSet<QString> getFiles();
    void getPrintableFonts(QSet<Misc::TFont> &items, bool selected);
    void setFilterGroup(CGroupListItem *grp);
    void stats(int &enabled, int &disabled, int &partial);
    void selectedStatus(bool &enabled, bool &disabled);
    QModelIndexList allFonts();
    void selectFirstFont();
    QModelIndexList getSelectedItems();

Q_SIGNALS:

    void del();
    void print();
    void enable();
    void disable();
    void fontsDropped(const QSet<QUrl> &);
    void itemsSelected(const QModelIndexList &);
    void refresh();
    void reload();

public Q_SLOTS:

    void listingPercent(int percent);
    void refreshFilter();
    void filterText(const QString &text);
    void filterCriteria(int crit, qulonglong ws, const QStringList &ft);

private Q_SLOTS:

    void setSortColumn(int col);
    void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) override;
    void itemCollapsed(const QModelIndex &index);
    void view();

private:
    QModelIndexList allIndexes();
    void startDrag(Qt::DropActions supportedActions) override;
    void dragEnterEvent(QDragEnterEvent *event) override;
    void dropEvent(QDropEvent *event) override;
    void contextMenuEvent(QContextMenuEvent *ev) override;
    bool viewportEvent(QEvent *event) override;

private:
    CFontListSortFilterProxy *m_proxy;
    CFontList *m_model;
    QMenu *m_menu;
    QAction *m_deleteAct, *m_enableAct, *m_disableAct, *m_printAct, *m_viewAct;
    bool m_allowDrops;
};

}
