#ifndef TABLETDESKTOPBACKEND_H
#define TABLETDESKTOPBACKEND_H

#include "database.h"
#include "tabletdesktopbackendglobal.h"
#include "types.h"
#include "icontheme.h"
#include "ukuisettings.h"
#include <QGSettings>
#include <QObject>
#include <KDirLister>
#include <QFileSystemWatcher>
#include "utils.h"
#include <ukui/kappwidgetmanager.h>

#define DESKTOP_ROW 4
#define DESKTOP_COLUMN 6
#define DESKTOP_CONF_FILE "/etc/ukui-tablet-desktop.conf"

class QString;

///
/// \brief 应用转换存储位置，该结构体中存储着需要传递给前端的内容信息
///
typedef struct appChangePositionInfo
{
    bool isTaskbarDeleteApp = false;    //taskbar是否是删除应用操作：false：不是删除操作，是增加操作 true：是删除操作
    bool isDesktopDeleteApp = false;    //desktop是否是删除应用操作：false：不是删除操作，是增加操作 true：是删除操作
    bool isExchangePosition = false;    //任务栏的应用是否交换位置
    int itemId;                         //应用在数据库中的id
    int itemFromPage;                   //应用来自于哪页
    int itemToPage;                     //应用移动到哪页
    int itemFromPageIndex;              //应用来自于哪页的那个位置
    int itemToPageIndex;                //应用移动到哪页的哪个位置
    int itemFromSet;                    //返回来自哪个集合id,-1代表非集合，其他数字代表集合id
    int itemToSet;                      //返回移动到哪个集合的id
}appChangeInfo;

namespace UKUI {

/*
 * Readme：
 * 1. 分组内的空间大小不需要后台去管理，由前台自己调整为num1 x num2，或者是通过滑动查找。后台代码对分组只提供存储功能。
*/
class KYLINTABLETDESKTOPBACKEND_EXPORT TabletDesktopBackend : public QObject
{
    Q_OBJECT
public:
    explicit TabletDesktopBackend();
    ~TabletDesktopBackend();
public:
    ///
    /// \brief getLauncherLine：获取Launcher界面的行数
    /// \return
    ///
    quint32 getLauncherLine() {
        return m_line;
    }

    ///
    /// \brief getLauncherColumn：获取Launcher界面的列数
    /// \return
    ///
    quint32 getLauncherColumn() {
        return m_column;
    }

    ///
    /// \brief              获取所有 item 信息，包括 icon、group、container、widget等
    ///
    QMap<quint32, BaseItem *> getAllItemData() {
        return m_baseItemList;
    }

    ///
    /// \brief              获取所有 page 页的 item id列表
    ///
    QMap<quint32, QList<quint32>> getAllPageData() {
        return allPage;
    }

    ///
    /// \brief getAllFlipSetsData：获取所有可以翻页的 container 或者 group 的 item id 列表
    ///
    QMap<quint32, QList<QList<quint32>>> getAllFlipSetsData() {
        return allFlipSets;
    }

    ///
    /// \brief getAllScrollSetsData：获取所有可以滚动的 container 或者 group 的 item id 列表
    /// \return
    ///
    QMap<quint32, QList<quint32>> getAllScrollSetsData() {
        return allScrollSets;
    }

    ///
    /// \brief              获取所有应用分类
    /// \return             返回分类的列表 和 图标名称
    ///
    QMap<Categories, QString> getAllAppCategories();

    ///
    /// \brief              根据传入的分类类型获取应用 id 列表
    /// \param              type 分类类型
    /// \return             应用 id 列表
    ///
    QList<quint32> getAppIdListAccordingCategory(Categories type);

    ///
    /// \brief              获取所有应用的 id 列表，列表按字母排序
    /// \return             id 列表
    ///
    QList<quint32> getAllAppIdList();

    ///
    /// \brief              根据传入的图标名字从主题获取图标的绝对路径
    /// \param              iconName 图标名字
    /// \param              size 图标大小，例如：16x16，16x16@2x，24x24，32x32，48x48，64x64，96x96，128x128，256x256
    /// \return             图标的绝对路径
    ///
    QString getAbsoluteIconPath(QString iconName, quint32 size);

    ///
    /// \brief              后台数据库新建应用页
    /// \param page         新加页的页数
    ///
    void addLauncherPage(int page);

    ///
    /// \brief              当 page 页为空时，删除该应用页
    /// \param page         删除页的页数
    ///
    void deleteLauncherPage(int page);

    ///
    /// \brief              修改第 pageId 页的itemList
    /// \param pageID       修改的 page 页的页数
    /// \param itemIdList   page页的item id列表
    ///
    ///
    ///
    /// \brief              同页内移动图标 / 组时
    ///                         前台可以直接调用 setPageData(quint32 pageId, const QList<quint32> &itemList)
    ///
    /// \brief              不同页间移动图标 / 组时
    ///                         前台可以直接调用 setPageData(quint32 fromPageId, const QList<quint32> &itemList)
    ///                         和             setPageData(quint32 toPageId, const QList<quint32> &itemList)
    ///
    bool setPageData(quint32 pageId, const QList<quint32> &itemIdList);

    ///
    /// \brief              修改 id 为 setId 的 sets 页的 itemIdList，存放到Set中的应用通过翻页查找
    /// \param              setId 待修改的 group/container 的id
    /// \param              itemIdList group/container 的item id列表
    ///
    bool setFlipSetData(quint32 setId, const QList<QList<quint32>> &itemIdList);

    ///
    /// \brief              修改 id 为 setId 的 sets 页的 itemIdList，存放到Set中的应用可以上下滑动查找
    /// \param              setId 待修改的 group/container 的id
    /// \param              itemIdList group/container 的item id列表
    ///
    bool setScrollSetData(quint32 setId, const QList<quint32> &itemIdList);

    ///
    /// \brief              修改 container 或者 group 的名字
    /// \param              setId 待修改的 group/container 的id
    /// \return             true为修改成功， false修改失败
    ///
    bool modifySetName(quint32 setId, QString name);

    ///
    /// \brief updateExistIdlePage：更新第一个空闲位置的 Item 所在的页数
    /// \return 返回存在空闲位置的page页的空闲位置个数
    ///
    quint32 updateExistIdlePage();

    ///
    /// \brief getIdleItemID：获取未被使用的 item 的 ID
    /// \return
    ///
    quint32 getIdleItemID();

    ///
    /// \brief createGroup：两个应用合并时，新建分组
    /// \param groupId
    /// \param itemIdList
    /// \param groupName
    /// \param scrollable
    ///
    void createGroup(quint32 groupId, QList<quint32> itemIdList, const QString groupName = QString(), bool scrollable = false);

    ///
    /// \brief createGroup：两个应用合并时，新建分组
    /// \param fromItemId
    /// \param toItemId
    /// \param groupName：分组的名字，可以为空
    /// \return
    ///
//    quint32 createGroup(quint32 fromItemId, quint32 toItemId, const QString &groupName = QString(), bool scrollable = false);

    ///
    /// \brief deleteGroup delete group
    /// \param setId
    ///
    void deleteGroup(quint32 setId);

    ///
    /// \brief uninsatllApp 将需要卸载的应用保存起来
    /// \param desktopFile 应用的desktopFile
    /// \return
    ///
    void uninsatllApp(const QString &desktopFile);

public slots:
    ///
    /// \brief taskbarAppChange 任务栏的应用变化
    ///
    void taskBarAppChange();

    ///
    /// \brief taskbarAppChange 任务栏的应用变化
    ///
    void taskBarAppChange(QList<quint32> &taskBarAppData);

    ///
    /// \brief appUninstallComplete 应用卸载完成后状态
    /// \param state 应用卸载成功 or 失败
    /// \param unloadstate 卸载信息
    ///
    void appUninstallComplete(bool state, QString unloadstate);

private slots:
    ///
    /// \brief              将应用存储到数据库
    ///
    void onItemsAdded(const QUrl &directoryUrl, const KFileItemList &items);
    void onItemsDeleted(const KFileItemList &items);

signals:
    void filechanged();

    ///
    /// \brief              新增 item 后触发此信号
    ///
    void itemAdded(quint32 id, quint32 pageId);

    ///
    /// \brief              从 Page 页删除 item 后触发此信号
    ///
    void itemDeletedFromPage(quint32 id, quint32 pageId);

    ///
    /// \brief              从 Sets 删除 item 后触发此信号
    ///
    void itemDeletedFromSet(quint32 id, quint32 setId);

    ///
    /// \brief              图标主题改变信号
    ///
    void iconThemeChanged();

    ///
    /// \brief              所有应用遍历完成后发送此信号, 后台专用，前台没必要监听
    ///
    void firstTraversalCompleted();

    ///
    /// \brief taskBarAppChanged：任务栏应用数据改变信号
    /// 结构体存储被移动的应用信息
    ///
    void taskBarAppChanged(struct appChangePositionInfo &appChangePosition);


private:
    ///创建结构体对象
//    appChangeInfo appChangePositionData;

    ///
    /// \brief              监听 desktop 文件的增删
    ///
    void desktopFileWatcher();

    ///
    /// \brief panelConfFileWatcher 监听panel.conf的变化
    ///
    void panelConfFileWatcher();

    ///
    /// \brief              读取数据库，获取应用信息
    ///
    void loadPositions();

    ///
    /// \brief loadTaskBarData：加载任务栏的应用数据，如果有变化的话，同步修改到数据库
    ///
    void loadTaskBarData();

    ///
    /// \brief              当图标主题发生变化时，修改内存中保存的图标路径；并向前台发送信号；
    ///
    void monitorIconThemeChange();

    ///
    /// \brief              删除 / 增加空白页
    ///
    void refreshPageDataBase();

    ///
    /// \brief              更新应用信息到数据库
    ///
    void refreshAllLocation();

    ///
    /// \brief              用来过滤掉不应该显示到桌面的应用，比如fcitx、wps配置应用，驱动应用等
    ///
    QStringList getBlackListApp();

    ///
    /// \brief              匹配应用 Category
    /// \param              应用的分类
    /// \param              教育、网络、社交等分类列表
    /// \return
    ///
    QList<int> matchingAppCategories(QString category);

    ///
    /// \brief              sortAppIdAccordingLetter 按字母排序
    /// \param              unorderedIdList 无序AppId列表
    /// \return             有序AppId列表
    ///
    QList<quint32> sortAppIdAccordingLetter(QList<quint32> &unorderedIdList);

    ///
    /// \brief              获取数据库中的所有被删除应用的 id，要将这部分应用从数据库中删除
    ///
    void checkIfDatabaseExistDeletedApp();

    ///
    /// \brief              删除该项时，可能出现删除页该项后为空的情况，所以需要判断是否存在空页
    ///                     如果出现空页，删除空页，并更新位置配置文件
    /// \param              id baseItem 的id
    ///
    void removeLauncherItem(quint32 id);

    ///
    /// \brief              从set中删除item
    /// \param              id baseItem 的id
    ///
    void removeItemFromSet(quint32 id);

    quint32 getAppIdAccordingString(const QString desktopName);

    ///
    /// \brief readPanelConf 读取.cache/ukui/panel.conf
    ///
    QList<quint32> readPanelConf();

    ///
    /// \brief list是否存在重复id
    /// \param itemIdList
    /// \return
    ///
    bool isListExistRepeatId(const QList<quint32> &itemIdList);

    ///
    /// \brief list是否存在重复id
    /// \param itemIdList
    /// \return
    ///
    bool isListAllIdCurrent(const QList<quint32> &itemIdList);

    ///
    /// \brief removeTaskbarItemFromPage 为了防止page页里有重复的id，需要将任务栏的应用从page页移除
    /// \return
    ///
    void removeTaskbarItemFromPage();

    ///
    /// \brief fillUpIdlePosition 为了防止page页里有重复的id，需要将任务栏的应用从page页移除
    /// \return
    ///
    void fillUpIdlePosition();

    ///
    /// \brief getPkgNameByDesktop 根据desktopname获取包名
    /// \param desktopFile 应用的desktopFile
    /// \return
    ///
    QString getPkgNameByDesktop(const QString &desktopFile);


    ///
    /// \brief doUninstallApp 卸载应用
    /// \param desktopFile 应用的desktopFile
    /// \return
    ///
    void doUninstallApp(const QString &desktopFile);

    ///
    /// \brief createWidgetData 获取并设置小插件数据
    /// \return
    ///
    void createWidgetData();

    ///
    /// \brief initItemGridStatus 向itemGridStatus中添加一页
    /// \return
    ///
    void initItemGridStatus();

    ///
    /// \brief calculateWidgetPosition 计算小插件的位置
    /// \param item 小插件的行和列
    /// \param itemId 小插件的id
    /// \return
    ///
    void calculateWidgetPosition(const QPoint &item, const int &itemId);

    ///
    /// \brief calculateItemPosition 计算应用或分组的位置
    /// \param itemId
    /// \param page 有空余位置的page页
    /// \return
    ///
    void calculateItemPosition(const int &itemId, const int &page);

    ///
    /// \brief itemGridDataToPage 小插件数据都获取到之后，将小插件id放到对应的page中
    /// \return
    ///
    void itemGridDataToPage();

    ///
    /// \brief gridPageData 将itemGridStatus的页数据转化成qlist
    /// \param page
    /// \return itemGridStatus的page页数据
    ///
    QList<quint32> gridPageData(const int &page);

    ///
    /// \brief createDefaultGroup 获取并创建默认分组
    /// \return
    ///
    void createDefaultGroup();

    ///
    /// \brief removeGroupItemFromPage 将应用从桌面或任务栏删除
    /// \param itemId 应用的id
    /// \return
    ///
    void removeGroupItemFromPage(int itemId);

    ///
    /// \brief updatePageData 更新包含小插件的页的数据（初始化任务栏数据，创建默认分组，会从桌面删除一些应用，删除再补齐会导致页数据的顺序不正确）
    /// \return
    ///
    void updatePageData();

    ///
    /// \brief findDesktopPathFromList 从m_baseItemList查找应用desktop的实际路径
    /// \param desktopName 应用的desktop
    /// \return desktop的实际路径
    ///
    QString findDesktopPathFromList(QString desktopName);

private:
    ///
    /// \brief m_idlePosPage：存在空余位置的 page 页 id
    ///
    qint32 m_idlePosPage; //存在空余位置的 page 页 id
    QMap<quint32, BaseItem *> m_baseItemList;
    QMap<quint32, QList<quint32>> allPage; //所有page页数据
    QMap<quint32, QList<QList<quint32>>> allFlipSets;   // 所有的可翻页的应用分组
    QMap<quint32, QList<quint32>> allScrollSets;        // 所有可滚动的应用分组，任务栏数据存储到可滚动应用分组里
    QList<quint32> m_shoudBeRemovedApp;                 // 存储平板桌面后台没有监测到删除或者没有删除成功的应用 ID，在启动初期将这些应用删除
    QList<quint32> m_allItemList;
    KDirLister *m_appListener;
    QFileSystemWatcher *m_Filewatcher;                   // 监听panel.conf变化
    DataBase* m_database;
    quint32 m_line; //Launcher界面的行数
    quint32 m_column; //Launcher界面的列数
    quint32 m_maxPage;
    IconTheme* m_icon_theme;
    quint32 m_loadCount;
    quint32 m_notEmptyDir;
    QGSettings *m_tabletLauncher;
    Settings *m_panelSettings;
    bool isFirstStart;
    QList<QString> m_uninstallAppList;
    bool m_uninstallFirstApp = true;
    QString m_uninstallAppDesktopname;
    QSettings *m_widgetSettings;
    AppWidget::KAppWidgetManager *m_appWidgetManager = nullptr;
    int m_desktopRow = 0;
    int m_desktopColumn = 0;
    int m_pageCount = 0;
    QMap<int, QList<QList<int>>> m_itemGridStatus;
};

}

#endif // TABLETDESKTOPBACKEND_H
