#include "modelmanager.h"
#include "modelmanager_p.h"
#include <QStack>
#include <QThread>
#include <QGuiApplication>
#include <QMetaEnum>
#include <QDebug>
#include <KWindowSystem>
#include "base/categoryitem.h"
#include "core/desktopdatahandler.h"
#include "core/appmanager.h"
#include "taskbarmanager.h"

ModelManagerPrivate::ModelManagerPrivate(ModelManager *parent)
    : dataHandler(new DesktopDataHandler())
    , appManager(new AppManager())
    , taskBarManager(new TaskBarManager(parent))
    , q_ptr(parent)
{
    Q_Q(ModelManager);

    QThread *backendThread = new QThread;
    dataHandler->moveToThread(backendThread);
    connect(backendThread, &QThread::started, dataHandler, &DesktopDataHandler::init);
    connect(dataHandler, &DesktopDataHandler::finished, backendThread, &QThread::quit);
    connect(backendThread, &QThread::finished, dataHandler, &DesktopDataHandler::deleteLater);
    connect(backendThread, &QThread::finished, backendThread, &QThread::deleteLater);
    connect(qApp, &QGuiApplication::aboutToQuit, dataHandler, &DesktopDataHandler::finished);

    connect(dataHandler, &DesktopDataHandler::desktopData, q, &ModelManager::initDesktopData);
    connect(dataHandler, &DesktopDataHandler::categoriesData, q, &ModelManager::categories);

    connect(dataHandler, &DesktopDataHandler::appUninstalledFromGroup, q, &ModelManager::removeItemFromGroup);
    connect(dataHandler, &DesktopDataHandler::appUninstalled, q, &ModelManager::removeItemWithoutGroup);
    connect(dataHandler, &DesktopDataHandler::appInstalled, q, &ModelManager::itemInstalled);

    connect(q, &ModelManager::groupNameChanged, dataHandler, &DesktopDataHandler::setGroupName);
    connect(q, &ModelManager::updateGroup, dataHandler, &DesktopDataHandler::updateGroup);
    connect(q, &ModelManager::createGroup, dataHandler, &DesktopDataHandler::createGroup);
    connect(q, &ModelManager::removeGroup, dataHandler, &DesktopDataHandler::removeGroup);
    connect(q, &ModelManager::uninsatllApp, dataHandler, &DesktopDataHandler::uninsatllApp);

    connect(dataHandler, &DesktopDataHandler::iconChanged, q, &ModelManager::iconThemeChanged);
    connect(dataHandler, &DesktopDataHandler::taskbarDataChange, q, &ModelManager::changeTaskbarData);

    backendThread->start();
}

void ModelManagerPrivate::initTaskBarData(const QList<quint32> &ids, const QMap<quint32, UKUI::BaseItem *> &itemData)
{
    if (ids.isEmpty()) {
        return;
    }
    PageModel *groupPageModel = new PageModel;
    for (const auto &id : ids) {
        LauncherItem *launcherItem = createLauncherItem(LauncherItem::AppIcon, itemData.value(id), 0);
        if (!launcherItem) {
            continue;
        }
        if (isAppInTheWhiteList(launcherItem->desktopFile())) {
            launcherItem->setUninstallable(false);
        }
        groupPageModel->push_back(launcherItem);
    }
    if (groupModel.contains(0)) {
        for (auto item : *groupPageModel) {
            groupModel[0]->at(0)->push_back(item);
            if (isAppInTheWhiteList(item->desktopFile())) {
                item->setUninstallable(false);
            }
        }
        groupPageModel->deleteLater();
    } else {
        MultiPageModel *taskbarGroupModel = new MultiPageModel;
        taskbarGroupModel->push_back(groupPageModel);
        groupModel.insert(0, taskbarGroupModel);
    }
}

LauncherItem* ModelManagerPrivate::createLauncherItem(LauncherItem::ItemType type,
                                                      UKUI::BaseItem *srcItem,
                                                      int page)
{
    if (!srcItem) {
        return nullptr;
    }
    LauncherItem *launcherItem = new LauncherItem;
    launcherItem->setId(srcItem->getId());
    launcherItem->setName(srcItem->getName());
    launcherItem->setPageIndex(page);
    switch (type) {
    case LauncherItem::AppIcon: {
        UKUI::IconItem *iconItem = static_cast<UKUI::IconItem *>(srcItem);
        if (!iconItem) {
            qWarning() << "Ceate app icon launcher item failed, the src icon item is empty.";
            return nullptr;
        }
        if (iconItem->getType() != UKUI::Type::Icon) {
            qWarning() << "Ceate app icon launcher item failed, the src icon item is not UKUI::Type::Icon type.";
            return nullptr;
        }
        launcherItem->setType(LauncherItem::AppIcon);
        launcherItem->setIcon(iconItem->getIconName());
        launcherItem->setDesktopFile(iconItem->getDesktopName());
        launcherItem->setRowSpan(1);
        launcherItem->setColumnSpan(1);
        launcherItem->setCategories(srcItem->getCategories());
        break;
    }
    case LauncherItem::Group: {
        UKUI::GroupItem *groupItem = static_cast<UKUI::GroupItem *>(srcItem);
        if (!groupItem) {
            return nullptr;
        }
        if (groupItem->getType() != UKUI::Type::Group) {
            qWarning() << "Ceate group launcher item failed, the src group item is not UKUI::Type::Group type.";
            return nullptr;
        }
        launcherItem->setType(LauncherItem::Group);
        launcherItem->setRowSpan(1);
        launcherItem->setColumnSpan(1);     
        break;
    }
    case LauncherItem::Widget: {
        UKUI::Widget *widgetItem = static_cast<UKUI::Widget *>(srcItem);
        if (!widgetItem) {
            return nullptr;
        }
        if (widgetItem->getType() != UKUI::Type::Widget) {
            qWarning() << "Ceate widget launcher item failed, the src widget item is not UKUI::Type::widget type.";
            return nullptr;
        }
        launcherItem->setType(LauncherItem::Widget);
        if (widgetItem->getPlaceHolder().count() != 2) {
            qWarning() << "Ceate widget launcher item failed, the row or column is incorrent.";
            return nullptr;
        }
        launcherItem->setRowSpan(widgetItem->getPlaceHolder().at(0));
        launcherItem->setColumnSpan(widgetItem->getPlaceHolder().at(1));
        launcherItem->setQmlFile(widgetItem->getQmlFilePath());
        break;
    }
    default:
        return nullptr;
    }
    return launcherItem;
}

void ModelManagerPrivate::layoutForAddPlaceholderItem(int page, int groupId)
{
    layoutChangedPages.clear();
    MultiPageModel *model = groupId > 0 ? groupModel.value(groupId) : &launcherModel;
    int maxPageCount = groupId > 0 ? maxItemCountAllowedInPageOfGroup.value(groupId) : pageItemMaxCount;

    if (page == model->count() - 1 && gridCount(groupId, model->count()-1) > maxPageCount) {
        //最后一页添加占位符后网格总数超过24，需要添加一页
        PageModel *newPage = new PageModel;
        launcherModel.push_back(newPage);
    }
    // 超出当前页的item的数量的时候，将最后一个item移动至下一页
    // 如果最后一个item的是添加按钮的话，直接删除即可
    for (int pageIndex = page; pageIndex < model->count() - 1; ++pageIndex) {
        // pageIndex页所占网格的总数
        int pageGridCount = gridCount(groupId, pageIndex);
        int lastItemIndex = model->at(pageIndex)->count() - 1;

        while (pageGridCount > maxPageCount && lastItemIndex >= 0) {
            int startPage = pageIndex;
            int itemGridCount = model->at(pageIndex)->at(lastItemIndex)->rowSpan() *
                    model->at(pageIndex)->at(lastItemIndex)->columnSpan();
            qDebug() << "start page " << startPage;
            auto moveToNextPageItem = model->at(startPage)->takeLast();
            if (moveToNextPageItem->type() == LauncherItem::AddIcon) {
                moveToNextPageItem->deleteLater();
                continue;
            }
            moveToNextPageItem->setPageIndex(moveToNextPageItem->pageIndex() + 1);
            model->at(++startPage)->prepend(moveToNextPageItem);
            layoutChangedPages.push(startPage);

            --lastItemIndex;
            pageGridCount -= itemGridCount;
        }
        if (pageIndex == model->count() - 2 && gridCount(groupId, model->count()-1) > maxPageCount) {
            //倒数第二页的网格数超过24，将超出的item移动至最后一页，最后一页放不下，需要添加一页
            PageModel *newPage = new PageModel;
            launcherModel.push_back(newPage);
        }
    }
    qDebug() << "layoutChangedPages" << layoutChangedPages;
}

void ModelManagerPrivate::layoutForRemovePlaceholderItem()
{
    LauncherItem *moveItem = nullptr;
    MultiPageModel *model = placeholderItemInfo.groupId > 0 ?
                groupModel.value(placeholderItemInfo.groupId) : &launcherModel;
    while (!layoutChangedPages.isEmpty()) {
        int page = layoutChangedPages.pop();
        moveItem = model->at(page)->takeFirst();
        moveItem->setPageIndex(moveItem->pageIndex() - 1);
        model->at(--page)->append(moveItem);
    }
    // fix: 不显示“添加“按钮的分组内的item拖动至新建页时导致数组越界
#if 0
    if (model->last()->isEmpty()) {
        model->removeLast();
    }
#endif
}

void ModelManagerPrivate::layoutForReplacePlaceholderItem()
{
    LauncherItem *moveItem = nullptr;
    MultiPageModel *model = placeholderItemInfo.groupId > 0 ?
                groupModel.value(placeholderItemInfo.groupId) : &launcherModel;
    //
    while (layoutChangedPages.count() > 1) {
        int page = layoutChangedPages.pop();
        moveItem = model->at(page)->takeFirst();
        moveItem->setPageIndex(moveItem->pageIndex() - 1);

        model->at(--page)->append(moveItem);
    }

    // fix: 不显示“添加“按钮的分组内的item拖动至新建页时导致数组越界
#if 0
    if (model->last()->isEmpty()) {
        model->removeLast();
    }
#endif
}

LauncherItem *ModelManagerPrivate::createAddIconItem(int pageIndex, int state)
{
    if (!needAddIcon) {
        return nullptr;
    }
    LauncherItem *addItem = new LauncherItem;
    addItem->setId(0);
    addItem->setPageIndex(pageIndex);
    addItem->setType(LauncherItem::AddIcon);
    addItem->setIcon("qrc:/ui/images/ukui_add_default.svg");
    addItem->setName("添加");
    addItem->setRowSpan(1);
    addItem->setColumnSpan(1);
    addItem->setState(state);
    return addItem;
}

LauncherItem *ModelManagerPrivate::createAppIcon(int itemId, int pageIndex)
{
    LauncherItem *item = new LauncherItem;
    QMap<quint32, UKUI::BaseItem *> items = dataHandler->items();
    UKUI::IconItem *iconItem = static_cast<UKUI::IconItem *>(items.value(itemId));
    if (iconItem) {
        item->setId(itemId);
        item->setPageIndex(pageIndex);
        item->setName(iconItem->getName());
        item->setIcon(iconItem->getIconName());
        item->setDesktopFile(iconItem->getDesktopName());
        item->setColumnSpan(1);
        item->setRowSpan(1);
    }

    return item;
}

PageModel *ModelManagerPrivate::createPage(int pageIndex,
                                           int groupId,
                                           int state)
{
    PageModel *page = new PageModel;
    if (editableContainerId.contains(groupId)) {
        LauncherItem *addItem = createAddIconItem(pageIndex, state);
        page->push_back(addItem);
    }
    return page;
}

void ModelManagerPrivate::tryAddAddIcon(int page, int groupId)
{
    if (!needAddIcon) {
        return;
    }
    MultiPageModel *model = groupId > 0 ? groupModel.value(groupId) : &launcherModel;
    if (model->at(page)->isEmpty()) {
        return;
    }

    int maxItemCount = groupId > 0 ? maxItemCountAllowedInPageOfGroup.value(groupId) : pageItemMaxCount;
    if (model->at(page)->count() < maxItemCount &&
            model->at(page)->last()->type() != LauncherItem::AddIcon &&
            editableContainerId.contains(groupId)) {
        model->at(page)->push_back(createAddIconItem(page));
    }
}

QPair<QString, QString> ModelManagerPrivate::categoryIcon(int category)
{
    QMetaEnum metaEnum = QMetaEnum::fromType<CategoryItem::Category>();
    const char *categoryName = metaEnum.valueToKey(category);

    return QPair<QString, QString>(QString("qrc:") + "/ui/images/categories/ukui_" + categoryName + "_default.svg",
                                   QString("qrc:") + "/ui/images/categories/ukui_" + categoryName + "_click.svg");
}

bool ModelManagerPrivate::itemExists(int groupId, int itemId)
{
    Q_ASSERT(groupModel.contains(groupId));
    int pageCount = groupModel.value(groupId)->count();
    for (int page=0; page<pageCount; ++page) {
        for (int itemIndex=0;
             itemIndex<groupModel.value(groupId)->at(page)->count();
             ++itemIndex) {
            if (groupModel.value(groupId)->at(page)->at(itemIndex)->id() == itemId) {
                return true;
            }
        }
    }
    return false;
}

int ModelManagerPrivate::itemGroupId(int itemId)
{
    auto groupModelIt = groupModel.constBegin();
    while (groupModelIt != groupModel.constEnd()) {
        auto groupModel = groupModelIt.value();
        for (int page=0; page<groupModel->count(); ++page) {
            for (int itemIndex=0; itemIndex<groupModel->at(page)->count(); ++itemIndex) {
                if (groupModel->at(page)->at(itemIndex)->id() == itemId) {
                    return groupModelIt.key();
                }
            }
        }
        ++ groupModelIt;
    }
    return -1;
}

QList<quint32> ModelManagerPrivate::groupIds(int groupId) const
{
    Q_ASSERT(groupModel.contains(groupId));
    QList<quint32> ids;
    for (int page=0; page<groupModel.value(groupId)->count(); ++page) {
        int pageItemCount = groupModel.value(groupId)->at(page)->count();
        for (int index=0; index<pageItemCount; ++index) {
            ids.push_back(groupModel.value(groupId)->at(page)->at(index)->id());
        }
    }
    return ids;
}

void ModelManagerPrivate::removeGroupItem(int itemId, int groupId)
{
    if (!groupModel.contains(groupId)) {
        return;
    }
    int state = getGroupState(groupId);

//    Q_ASSERT(groupModel.contains(groupId));
    int pageCount = groupModel.value(groupId)->count();
    for (int page=0; page<pageCount; ++page) {
        int pageItemCount = groupModel.value(groupId)->at(page)->count();
        for (int itemIndex=0; itemIndex<pageItemCount; ++itemIndex) {
            if (groupModel.value(groupId)->at(page)->at(itemIndex)->id() == itemId) {
                if (state == LauncherItem::Popup) {
                    state = groupModel.value(groupId)->at(page)->at(itemIndex)->state();
                }
                groupModel.value(groupId)->at(page)->removeAt(itemIndex);

                int groupMaxItemCount = maxItemCountAllowedInPageOfGroup[groupId];
                if (groupId > kTaskBarGroupId) {
                    if (page < pageCount - 1) {
                        fillUpGroupEmptySpace(page, groupId);
                        int curPageItemCount = groupModel.value(groupId)->at(page)->count();
                        int nextPageItemCount = groupModel.value(groupId)->at(page + 1)->count();
                        //卸载分组非最后一页的应用，后一页应用向前补齐
                        //正常状态下，当前页是满的，后一页为空，删除后一页
                        //编辑状态下，如果当前页应用数量<16,删除后一页
                        if (state == LauncherItem::Normal && curPageItemCount == groupMaxItemCount && nextPageItemCount == 0) {
                            groupModel.value(groupId)->removeAt(page + 1);
                        } else if (state == LauncherItem::Editable && curPageItemCount < groupMaxItemCount){
                            groupModel.value(groupId)->removeAt(page + 1);
                        }
                    } else {
                        //卸载分组最后一页的应用
                        //正常状态下，如果该页为空，删除该页,如果分组为空，删除分组
                        //编辑状态下，该页不是第一页，如果该页为空，前一页不满，删除该页
                        //编辑状态下，该页是第一页，如果该页为空，删除分组
                        if (state == LauncherItem::Normal && groupModel.value(groupId)->at(page)->count() == 0) {
                            groupModel.value(groupId)->removeAt(page);
                            if (groupModel.value(groupId)->count() == 0) {
                                removeGroupFromDesktop(groupId);
                            }
                        } else if (state == LauncherItem::Editable && page > 0 && groupModel.value(groupId)->at(page)->count() == 0 &&
                                   groupModel.value(groupId)->at(page-1)->count() < groupMaxItemCount) {
                            groupModel.value(groupId)->removeAt(page);
                        } else if (state == LauncherItem::Editable && page == 0 && groupModel.value(groupId)->at(page)->count() == 0) {
                            groupModel.value(groupId)->removeAt(page);
                            if (groupModel.value(groupId)->count() == 0) {
                                removeGroupFromDesktop(groupId);
                            }
                        }
                    }
                }
                return;
            }
        }
    }
}

void ModelManagerPrivate::removeItemFromCategory(int itemId)
{
    Q_Q(ModelManager);
    // 需要从全部和具体的分类中都删除
    int categoryItemPageCount = categoryItemsModel.count();
    for (int page=0; page<categoryItemPageCount; ++page) {
        int itemCountInPage = categoryItemsModel.at(page)->count();
        for (int itemIndex=0; itemIndex < itemCountInPage; ++itemIndex) {
            if (categoryItemsModel.at(page)->at(itemIndex)->id() == itemId) {
                categoryItemsModel.at(page)->removeAt(itemIndex);
                if (categoryItemsModel.at(page)->isEmpty()) {
                    int category = categoryOfAddtionPage(page);
                    updateCategoriesItemsPageIndex(page, -1);
                    categoryItemsModel.removeAt(page);
                    Q_EMIT q->categoryPageRemoved(category);
                    -- categoryItemPageCount;
                    // 如果当前分类下的应用全部被删除了，则删除该分类
                    for (int categoryIndex=0; categoryIndex<categoryModel.count(); ++categoryIndex) {
                        if (categoryModel.at(categoryIndex)->category() == category) {
                            if (categoryModel.at(categoryIndex)->pageCount() == 1) {
                                categoryModel.removeAt(categoryIndex);
                                Q_EMIT q->categoryRemoved(category);
                            } else if (categoryModel.at(categoryIndex)->pageCount() > 1) {
                                int newPageCount = categoryModel.at(categoryIndex)->pageCount() - 1;
                                categoryModel.at(categoryIndex)->setPageCount(newPageCount);
                            }
                        }
                    }
#if 0
                    int pageCount = 0;
                    for (int categoryIndex=0; categoryIndex<categoryModel.count(); ++categoryIndex) {
                        pageCount += categoryModel.at(categoryIndex)->pageCount();
                        if (pageCount > page) {
                            int newPageCount = categoryModel.at(categoryIndex)->pageCount() - 1;
                            if (newPageCount > 0) {
                                categoryModel.at(categoryIndex)->setPageCount(newPageCount);
                            } else {
                                int category = categoryModel.at(categoryIndex)->category();
                                categoryModel.removeAt(categoryIndex);
                                Q_EMIT q->categoryRemoved(category);
                            }
                            break;
                        }
                    }
#endif
                    break;
                }
                // move
                else {
                    while (page + 1 < categoryItemsModel.count() &&
                            categoryOfAddtionPage(page) == categoryOfAddtionPage(page + 1) &&
                            categoryOfAddtionPage(page) != -1 &&
                            !categoryItemsModel.at(page + 1)->isEmpty()) {
                        auto movedItem = categoryItemsModel.at(page + 1)->takeFirst();
                        movedItem->setPageIndex(movedItem->pageIndex() - 1);
                        categoryItemsModel.at(page ++)->push_back(movedItem);
                        if (categoryItemsModel.at(page)->isEmpty()) {
                            int category = categoryOfAddtionPage(page);
                            categoryItemsModel.removeAt(page);
                            for (int categoryIndex=0; categoryIndex<categoryModel.count(); ++categoryIndex) {
                                if (categoryModel.at(categoryIndex)->category() == category) {
                                    int newPageCount = categoryModel.at(categoryIndex)->pageCount() - 1;
                                    categoryModel.at(categoryIndex)->setPageCount(newPageCount);
                                }
                            }
                            Q_EMIT q->categoryPageRemoved(category);
                            -- categoryItemPageCount;
                            -- page;
                        }
                    }
                    break;
                }
            }
        }
    }
}

int ModelManagerPrivate::categoryOfAddtionPage(int page)
{
    Q_ASSERT(categoryItemsModel.count() > page && page >= 0);
    int pageStartIndex = 0, pageEndIndex = 0;
    for (int categoryIndex=0; categoryIndex<categoryModel.count(); ++categoryIndex) {
        int curCategoryCount = categoryModel.at(categoryIndex)->pageCount();
        pageStartIndex = pageEndIndex;
        pageEndIndex += curCategoryCount - 1;
        if (page >= pageStartIndex && page <= pageEndIndex) {
            return categoryModel.at(categoryIndex)->category();
        }
        ++ pageEndIndex;
    }
    return -1;
}

QList<int> ModelManagerPrivate::selectedIds() const
{
    QList<int> ids;
    for (int page=0; page<categoryItemsModel.count(); ++page) {
        int itemCount = categoryItemsModel.at(page)->count();
        for (int itemIndex=0; itemIndex<itemCount; ++itemIndex) {
            auto item = categoryItemsModel.at(page)->at(itemIndex);
            if (item->state() == LauncherItem::Selectable && item->selected()) {
                if (!ids.contains(item->id())) {
                    ids.push_back(item->id());
                }
            }
        }
    }
    return ids;
}

bool ModelManagerPrivate::categoryContainsItem(int itemId, int category)
{
    int startPage = 0, endPage = 0;
    bool foundCaegory = false;
    for (int categoryIndex=0; categoryIndex<categoryModel.count(); ++categoryIndex) {
        if (categoryModel.at(categoryIndex)->category() == category) {
            endPage = startPage + categoryModel.at(categoryIndex)->pageCount() - 1;
            foundCaegory = true;
            break;
        }
        startPage += categoryModel.at(categoryIndex)->pageCount();
    }
    if (foundCaegory) {
        for (int page=startPage; page<=endPage; ++page) {
            int itemCount = categoryItemsModel.at(page)->count();
            for (int index=0; index<itemCount; ++index) {
                if (categoryItemsModel.at(page)->at(index)->id() == itemId) {
                    return true;
                }
            }
        }
    }
    return false;
}

int ModelManagerPrivate::autoPushBackItemForGroup(int itemId,
                                                  int groupId,
                                                  int startPageIndex)
{
    Q_ASSERT(groupModel.contains(groupId));
    if (groupModel[groupId]->count() <= startPageIndex) {
        groupModel[groupId]->push_back(createPage(startPageIndex, groupId, LauncherItem::Normal));
    }

    if (groupModel[groupId]->at(startPageIndex)->count() < maxItemCountAllowedInPageOfGroup[groupId]) {
        int curItemCount = groupModel[groupId]->at(startPageIndex)->count();
        // -1,当前有添加按钮
        groupModel[groupId]->at(startPageIndex)->insert(curItemCount-1, createAppIcon(itemId, startPageIndex));
        return startPageIndex;
    } else if (groupModel[groupId]->at(startPageIndex)->count() == maxItemCountAllowedInPageOfGroup[groupId]) {
        // 替换添加按钮
        if (groupModel[groupId]->at(startPageIndex)->last()->type() == LauncherItem::AddIcon) {
            int index = groupModel[groupId]->at(startPageIndex)->count() - 1;
            groupModel[groupId]->at(startPageIndex)->replace(index, createAppIcon(itemId, startPageIndex));
            return startPageIndex;
        } else {
            autoPushBackItemForGroup(itemId, groupId, ++startPageIndex);
        }
    } else {
        autoPushBackItemForGroup(itemId, groupId, ++startPageIndex);
    }
    return startPageIndex;
}

void ModelManagerPrivate::updateCategoriesItemsPageIndex(int startPageIndex, int diff)
{
    Q_ASSERT(startPageIndex >=0 && categoryItemsModel.count() > startPageIndex &&
             diff > - categoryItemsModel.count() + 1);
    for (int page=startPageIndex; page<categoryItemsModel.count(); ++page) {
        for (int itemIndex=0; itemIndex < categoryItemsModel.at(page)->count(); ++itemIndex) {
            auto item = categoryItemsModel.at(page)->at(itemIndex);
            item->setPageIndex(item->pageIndex() + diff);
        }
    }
}

void ModelManagerPrivate::initSelectedItemIds(int groupId)
{
    Q_ASSERT(groupModel.contains(groupId));
    currentSelectedIds.clear();
    for (int page=0; page<groupModel.value(groupId)->count(); ++page) {
        int itemCount = groupModel.value(groupId)->at(page)->count();
        for (int itemIndex=0; itemIndex<itemCount; ++itemIndex) {
            currentSelectedIds.push_back(groupModel.value(groupId)->at(page)->at(itemIndex)->id());
        }
    }
}

int ModelManagerPrivate::availableId()
{
    QMap<int, int> ids;

    auto getIdFunction = [&ids](const MultiPageModel &model) {
        for (const auto &pageItems : model) {
            for (const auto &items : *pageItems) {
                int id = items->id();
                ids.insert(id, id);
            }
        }
    };

    getIdFunction(launcherModel);

    auto it = groupModel.constBegin();
    while (it != groupModel.constEnd()) {
        getIdFunction(**it);
        ++ it;
    }

    if (ids.lastKey() == ids.count() - 1) {
        return ids.count() + 1;
    }

    int preId = 0;
    auto idIt = ids.constBegin();
    while (idIt != ids.constEnd()) {
        if (*idIt - preId > 1) {
            return preId + 1;
        }
        preId = *idIt;
        ++ idIt;
    }

    return ids.count() + 1;
}

LauncherItem *ModelManagerPrivate::item(int itemId)
{
    for (int page=0; page<launcherModel.count(); ++page) {
        for (int index=0; index<launcherModel.at(page)->count(); ++index) {
            if (launcherModel.at(page)->at(index)->id() == itemId) {
                return launcherModel.at(page)->at(index);
            }
        }
    }
    /// TODO: move group item
    return nullptr;
}

void ModelManagerPrivate::setItemNameVisible(MultiPageModel *model, bool visible)
{
    if (!model) {
        return;
    }
    for (const auto &pageItems : *model) {
        for (const auto &item : *pageItems) {
            item->setNameVisible(visible);
        }
    }
}

void ModelManagerPrivate::replaceGroupWithAppIcon(int groupId)
{
    Q_Q(ModelManager);
    if (!groupModel.contains(groupId)) {
        return;
    }
    auto model = groupModel.value(groupId);

    if (model->size() > 1 ||(model->size() == 1 && model->first()->size() > 1)) {
        return;
    }
    for (const auto &pageModel : launcherModel) {
        for (int index=0; index<pageModel->count(); ++index) {
            if (pageModel->at(index)->id() == groupId) {
                auto item = model->first()->takeFirst();
                *pageModel->at(index) = *item;
                item->deleteLater();
                pageModel->at(index)->setNameVisible(true);
                pageModel->at(index)->setState(globalState);
                pageModel->at(index)->setPageIndex(launcherModel.indexOf(pageModel));
                groupModel.remove(groupId);
                Q_EMIT q->removeGroup(groupId);
                return;
            }
        }
    }
}

bool ModelManagerPrivate::onlyOneItemInGroup(int groupId)
{
    if (groupId < 0 || !groupModel.contains(groupId)) {
        return false;
    }
    auto model = groupModel.value(groupId);
    int itemCount = 0;
    for (int pageIndex = 0; pageIndex < model->count(); ++pageIndex) {
        for (int itemIndex = 0; itemIndex < model->at(pageIndex)->count(); ++itemIndex) {
            if (model->at(pageIndex)->at(itemIndex)->type() == LauncherItem::AppIcon) {
                ++itemCount;
            }
        }
    }
    if (itemCount == 1) {
        return true;
    }
    return false;
}

int ModelManagerPrivate::findTheSameNumber(const QList<int> &set1, const QList<int> &set2)
{
    if (set1.isEmpty() || set2.isEmpty()) {
        return -1;
    }
    for (const auto &number1 : set1) {
        for (const auto &number2 : set2) {
            if (number1 == number2) {
                return number1;
            }
        }
    }
    return -1;
}

bool ModelManagerPrivate::placeholderAvailable()
{
    return placeholderItemInfo.itemIndex > - 1 &&
            placeholderItemInfo.pageIndex > -1 &&
            placeholderItemInfo.placeholderItem;
}

void ModelManagerPrivate::clearPlaceholderItemInfo()
{
    if (placeholderItemInfo.placeholderItem) {
        placeholderItemInfo.placeholderItem->deleteLater();
        placeholderItemInfo.placeholderItem = nullptr;
    }
    placeholderItemInfo.itemIndex = -1;
    placeholderItemInfo.pageIndex = -1;
    placeholderItemInfo.groupId = -1;
}

MultiPageModel *ModelManagerPrivate::availableModel(int groupId)
{
    if (groupId == -1) {
        return &launcherModel;
    }
    if (!groupModel.contains(groupId)) {
        qWarning() << "Can not find the group model of id: " << groupId;
        return nullptr;
    }
    return groupModel.value(groupId);
}

bool ModelManagerPrivate::itemIndexIsValid(int itemIndex, int pageIndex, const MultiPageModel *model)
{
    if (model == nullptr) {
        return false;
    }
    if (pageIndex < 0 || pageIndex >= model->count()) {
        return false;
    }
    if (itemIndex < 0 || itemIndex >= model->at(pageIndex)->count()) {
        return false;
    }
    return true;
}

void ModelManagerPrivate::groupAddPage(int groupId) {
    auto model = groupModel.value(groupId);
    if (model->last()->count() >= maxItemCountAllowedInPageOfGroup.value(groupId)) {
        PageModel *newPage = new PageModel;
        model->push_back(newPage);
    }
}

void ModelManagerPrivate::removeEmptyPage(MultiPageModel * const model)
{
    for (int i = 0; i < model->count(); i++) {
        if (model->at(i)->count() == 0) {
            model->removeAt(i);
            for (int j = i; j < model->count(); j++) {
                for (int itemIndex = 0; itemIndex < model->at(j)->count(); ++itemIndex) {
                    int groupItemPageIndex = model->at(j)->at(itemIndex)->pageIndex();
                    model->at(j)->at(itemIndex)->setPageIndex(groupItemPageIndex - 1);
                }
            }
            i--;
        }
    }
}

int ModelManagerPrivate::findGroupPageIndexFromDesktop(const int &groupId)
{
    Q_ASSERT(groupModel.contains(groupId));
    for (const auto &pageItems : launcherModel) {
        for (const auto &item : *pageItems) {
            if (item->type() == LauncherItem::Group && item->id() == groupId) {
                return item->pageIndex();
            }
        }
    }
    return -1;
}

void ModelManagerPrivate::removeGroupFromDesktop(const int &groupId)
{
    Q_Q(ModelManager);
    Q_ASSERT(groupModel.contains(groupId));
    for (int page = 0; page < launcherModel.count(); page++) {
        for (int itemIndex = 0; itemIndex < launcherModel.at(page)->count(); itemIndex++) {
            if (launcherModel.at(page)->at(itemIndex)->id() == groupId) {
                if (launcherModel.at(page)->at(itemIndex)->state() == LauncherItem::Popup) {
                    qDebug() << "分组是弹出的，要在分组缩小后将分组删除";
                    return;
                }
                groupModel.remove(groupId);
                Q_EMIT q->removeGroup(groupId);
                q->removeItem(itemIndex, page); //将分组从桌面删除
                return;
            }
        }
    }
}

void ModelManagerPrivate::fillUpGroupEmptySpace(int page, int groupId)
{
    Q_ASSERT(groupModel.contains(groupId));
    if (groupId <= 0 && page >= groupModel.value(groupId)->count()-1 && page < 0) {
        return;
    }
    MultiPageModel *model = groupModel.value(groupId);
    for (int tempPage=page + 1; tempPage<model->count(); ++tempPage) {
        if (model->at(tempPage)->isEmpty()) {
            continue;
        }
        auto moveItem = model->at(tempPage)->takeFirst();
        moveItem->setPageIndex(tempPage - 1);
        model->at(tempPage-1)->push_back(moveItem);
    }
}

int ModelManagerPrivate::getGroupState(const int &groupId)
{
    Q_ASSERT(groupModel.contains(groupId));
    for (int page = 0; page < launcherModel.count(); page++) {
        for (int itemIndex = 0; itemIndex < launcherModel.at(page)->count(); itemIndex++) {
            if (launcherModel.at(page)->at(itemIndex)->id() == groupId) {
                return launcherModel.at(page)->at(itemIndex)->state();
            }
        }
    }
    return 0;
}

int ModelManagerPrivate::gridCount(const int &groupId, const int &pageIndex)
{
    MultiPageModel *model = groupId > 0 ? groupModel.value(groupId) : &launcherModel;
    if (model->empty() || pageIndex >= model->count() || pageIndex < 0) {
        qWarning() << __func__ << "groupId or pageIndex is incorrent";
        return -1;
    }
    auto pageModel = model->at(pageIndex);
    int pageItemCount = 0;
    for (int itemIndex = 0; itemIndex < pageModel->count(); ++itemIndex) {
        int itemCount = pageModel->at(itemIndex)->rowSpan() * pageModel->at(itemIndex)->columnSpan();
        pageItemCount += itemCount;
    }
    return pageItemCount;
}

void ModelManagerPrivate::getAppWhiteList()
{
    m_appWhiteList = appManager->getAppWhiteList();
}

bool ModelManagerPrivate::isAppInTheWhiteList(const QString &desktopname)
{
    for (int i = 0; i < m_appWhiteList.count(); ++i) {
        if (m_appWhiteList[i].isEmpty()) {
            continue;
        }
        if (desktopname.contains(m_appWhiteList[i])) {
            return true;
        }
    }
    return false;
}

ModelManager::ModelManager(QObject *parent)
    : QObject(parent)
    , d_ptr(new ModelManagerPrivate(this))
{
    qRegisterMetaType<MultiPageModel>("MultiPageModel");
}

ModelManager::~ModelManager()
{
    delete d_ptr;
}

QAbstractListModel *ModelManager::launcherModel() const
{
    Q_D(const ModelManager);
    return const_cast<MultiPageModel *>(&d->launcherModel);
}

QAbstractListModel *ModelManager::groupModel(const quint32 &id) const
{
    Q_D(const ModelManager);
    if (!d->groupModel.contains(id)) {
        return nullptr;
    }
    return d->groupModel.value(id);
}

QAbstractListModel *ModelManager::categoryModel() const
{
    Q_D(const ModelManager);
    return const_cast<CategoryModel *>(&d->categoryModel);
}

QAbstractListModel *ModelManager::categoryItemsModel() const
{
    Q_D(const ModelManager);
    return const_cast<MultiPageModel *>(&d->categoryItemsModel);
}

int ModelManager::groupPageItemCount(int groupId, int pageIndex)
{
    Q_D(ModelManager);

    MultiPageModel *model = groupId >= 0 ? d->groupModel.value(groupId) : &d->launcherModel;
    return model->isEmpty() ? 0 : model->at(pageIndex)->count();
}

int ModelManager::groupPageCount(int groupId)
{
    Q_D(ModelManager);
    if (!d->groupModel.contains(groupId)) {
        return 0;
    }
    return d->groupModel.value(groupId)->count();
}

void ModelManager::moveItem(int from,
                            int to,
                            int page,
                            int groupId)
{
    Q_D(ModelManager);
    if (from == to) {
        return;
    }

    MultiPageModel *model = groupId >= 0 ? d->groupModel.value(groupId) : &d->launcherModel;
    model->at(page)->move(from, to);
}

void ModelManager::moveItemToGroup(int fromIndex,
                                   int fromPage,
                                   int groupId)
{
    Q_D(ModelManager);
    Q_ASSERT(groupId >= 0);
    if (!d->groupModel.contains(groupId) ||
            !d->itemIndexIsValid(fromIndex, fromPage, &d->launcherModel)) {
        qWarning() << "Move item to group failed, from index: "
                   << fromIndex << ","
                   << "from page: " << fromPage << ","
                   << "group id: " << groupId << ".";

        return;
    }

    int groupPageIndex = d->findGroupPageIndexFromDesktop(groupId);
    if (groupPageIndex < 0) {
        qWarning() << "groupPageIndex is not valid.";
        return;
    }

    MultiPageModel *model = d->groupModel.value(groupId);
    auto moveItem = d->launcherModel.at(fromPage)->takeAt(fromIndex);

    if (d->placeholderAvailable() && d->placeholderItemInfo.groupId == -1 && fromPage != groupPageIndex) {
        for (int pageIndex = 0; pageIndex < model->count(); ++pageIndex) {
            if (model->at(pageIndex)->count() < d->maxItemCountAllowedInPageOfGroup[groupId]) {
                addPlaceholderItem(model->at(pageIndex)->count(), pageIndex, 1, 1, groupId);
                break;
            }
        }
    }

    if (d->placeholderAvailable()) {
        if (!d->itemIndexIsValid(d->placeholderItemInfo.itemIndex, d->placeholderItemInfo.pageIndex, model)) {
            qWarning() << "Move item to group failed, placeholder item index is not valid.";
            return;
        }
        moveItem->setPageIndex(d->placeholderItemInfo.pageIndex);
        model->at(d->placeholderItemInfo.pageIndex)->replace(
                    d->placeholderItemInfo.itemIndex, moveItem);

        d->layoutForReplacePlaceholderItem();
        d->layoutChangedPages.clear();
        d->clearPlaceholderItemInfo();
        updateCachedData(groupId);
        d->groupAddPage(groupId);
        return;
    }

    for (int pageIndex = 0; pageIndex < model->count(); ++pageIndex) {
        if (model->at(pageIndex)->count() < d->maxItemCountAllowedInPageOfGroup[groupId]) {
            moveItem->setPageIndex(pageIndex);
            model->at(pageIndex)->push_back(moveItem);
            break;
        }
    }

    //分组最后一页是满的，会再添加一页
    d->groupAddPage(groupId);
    updateCachedData(groupId);
}

void ModelManager::removeItem(int index,
                              int page,
                              int groupId)
{
    Q_D(ModelManager);
    MultiPageModel *model = groupId >= 0 ? d->groupModel.value(groupId) : &d->launcherModel;
    int pageItemMaxCount = groupId >= 0 ? d->maxItemCountAllowedInPageOfGroup.value(groupId) : d->pageItemMaxCount;
    bool editable = false;
    if (model->at(page)->at(index)->state() == LauncherItem::Editable) {
        editable = true;
    }
    model->at(page)->removeAt(index);

    if (groupId == d->kTaskBarGroupId) {
        updateCachedData(groupId);
        return;
    }
    // 当前页为空时删除该页
    if ((model->at(page)->isEmpty()) ||
            (d->needAddIcon
             && model->at(page)->count() == 1
             && model->at(page)->last()->type() == LauncherItem::AddIcon)) {
        if (editable) {
            if (groupId == -1 && page == model->count() - 1) {
                qDebug() << "编辑状态下,桌面的最后一个空页不删除";
            } else if(page > 0 && page == model->count()-1 && model->at(page-1)->count() == pageItemMaxCount) {
                qDebug() << "分组超过两页，最后一页为空，前一页是满的，编辑状态下不能删除最后一个空页";
            }
        } else {
            model->removeAt(page);
            for (int pageIndex=page; pageIndex<model->count(); ++pageIndex) {
                PageModel *pageModel = model->at(pageIndex);
                for (int itemIndex=0; itemIndex<pageModel->count(); ++itemIndex) {
                    pageModel->at(itemIndex)->setPageIndex(pageModel->at(itemIndex)->pageIndex() - 1);
                }
            }
        }
    } else if (d->needAddIcon) {
        if (model->at(page)->count() < pageItemMaxCount &&
                model->at(page)->last()->type() != LauncherItem::AddIcon &&
                d->editableContainerId.contains(groupId)) {
            LauncherItem *addItem = d->createAddIconItem(page, LauncherItem::Editable);
            model->at(page)->push_back(addItem);
        }
    } else {
        // 必须保证分组的非最后一页中item是满的
        if (groupId > 0 && page < model->count() - 1) {
            d->fillUpGroupEmptySpace(page, groupId);
            if (model->at(page)->count() < pageItemMaxCount) {
                model->removeAt(model->count()-1);
            }
        }
    }
    updateCachedData(groupId);
}


void ModelManager::setItemState(int state, int itemId)
{
    Q_D(ModelManager);
    if (itemId < 0) {
        return;
    }
    for (const auto &pageItems : d->launcherModel) {
        for (const auto &item : *pageItems) {
            if (item->id() == itemId) {
                item->setState(state);
                if (state == LauncherItem::Editable) {
                    item->setNameVisible(true);
                }
                return;
            }
        }
    }

    for (const auto &groupModel : d->groupModel) {
        for (const auto &pageItems : *groupModel) {
            for (const auto &item : *pageItems) {
                if (item->id() == itemId) {
                    item->setState(state);
                    if (state == LauncherItem::Editable) {
                        item->setNameVisible(true);
                    }
                    return;
                }
            }
        }
    }
}

void ModelManager::setItemsInGroupState(int state, int groupId)
{
    Q_D(ModelManager);
    Q_ASSERT(d->groupModel.contains(groupId));
    auto groupModel = d->groupModel.value(groupId);
    for (const auto &pageItems : *groupModel) {
        for (const auto &item : *pageItems) {
            item->setState(state);
        }
    }
}

void ModelManager::setAllItemsState(int state)
{
    Q_D(ModelManager);
    if (d->globalState == state) {
        return;
    }
    d->globalState = state;
    for (const auto &launcherItems : d->launcherModel) {
        for (const auto &item : *launcherItems) {
            if (item->type() == LauncherItem::Group && item->state() == LauncherItem::Popup) {
                int groupId = item->id();
                if (!d->groupModel.contains(groupId)) {
                    qWarning() << "The page model contains the group item, "
                                  "but not found in group model. " << groupId;
                    continue;
                }
                auto groupModel = d->groupModel.value(groupId);
                for (const auto &groupItems : *groupModel) {
                    for (const auto &groupItem : *groupItems) {
                        if (groupItem->type() != LauncherItem::Placeholder) {
                            groupItem->setState(state);
                        }
                    }
                }
                continue;
            }
            if (item->type() != LauncherItem::Placeholder) {
                item->setState(state);
            }
        }
    }
    if (d->groupModel.contains(d->kTaskBarGroupId)) {
        auto taskBarModel = d->groupModel.value(d->kTaskBarGroupId);
        for (const auto &taskBarItems : *taskBarModel) {
            for (const auto &item : *taskBarItems) {
                if (item->type() != LauncherItem::Placeholder) {
                    item->setState(state);
                }
            }
        }
    }
    // 删除和添加空白页
    if (state == LauncherItem::Editable) {
        d->launcherModel.push_back(new PageModel);
        auto it = d->groupModel.constBegin();
        while (it != d->groupModel.constEnd()) {
            if (it.key() == d->kTaskBarGroupId) {
                ++ it;
                continue;
            }
            d->groupModel[it.key()]->push_back(new PageModel);
            ++ it;
        }
    } else if (state == LauncherItem::Normal) {
        d->removeEmptyPage(&d->launcherModel);

        auto it = d->groupModel.constBegin();
        while (it != d->groupModel.constEnd()) {
            if (it.key() == d->kTaskBarGroupId) {
                ++ it;
                continue;
            }
            auto groupModel = it.value();            
            d->removeEmptyPage(groupModel);

            ++ it;
        }
    }
}

void ModelManager::popupGroup(int groupId)
{
    Q_D(ModelManager);
    Q_ASSERT(d->groupModel.contains(groupId));
    LauncherItem *groupItem = nullptr;
    for (const auto &pageItems : d->launcherModel) {
        for (const auto &item : *pageItems) {
            if (item->id() == groupId) {
                groupItem = item;
                break;
            }
        }
        if (groupItem) {
            break;
        }
    }
    if (groupItem) {
        groupItem->setState(LauncherItem::Popup);
    }

    d->setItemNameVisible(d->groupModel.value(groupId), true);
    setItemsInGroupState(d->globalState, groupId);
}

void ModelManager::minimizeGroup(int groupId)
{
    Q_D(ModelManager);
#ifdef QT_DEBUG
    qDebug() << __FUNCTION__ << groupId;
#endif
    if (!d->groupModel.contains(groupId)) {
        return;
    }

    auto model = d->groupModel.value(groupId);
    setItemsInGroupState(LauncherItem::Normal, groupId);
    d->setItemNameVisible(d->groupModel.value(groupId), false);
    // 恢复分组内的item的状态
    for (const auto &pageItems : d->launcherModel) {
        for (const auto &item : *pageItems) {
            if (item->id() == groupId) {
                item->setState(d->globalState);
                break;
            }
        }
    }

    // 新建分组之后又取消了，比如拖动图标至另一个图标之上，弹出新建分组窗口
    // 之后未释放图标，一直拖拽出分组边界
    if (d->placeholderItemInfo.parentItemType == ModelManagerPrivate::Group &&
            d->placeholderItemInfo.groupId == groupId) {
        removePlaceholderItem();
    }

    if (d->onlyOneItemInGroup(groupId)) {
        for (const auto &pageModel : d->launcherModel) {
            for (int index=0; index<pageModel->count(); ++index) {
                if (pageModel->at(index)->id() == groupId) {
                    auto item = model->first()->first();
                    *(pageModel->at(index)) = *item;
                    pageModel->at(index)->setNameVisible(true);
                    pageModel->at(index)->setState(d->globalState);
                    pageModel->at(index)->setPageIndex(d->launcherModel.indexOf(pageModel));
                    d->groupModel.remove(groupId);
                    Q_EMIT removeGroup(groupId);
                    return;
                }
            }
        }
    }
    if (model->count() == 0 || (model->count() == 1 && model->at(0)->count() == 0)) {
        d->removeGroupFromDesktop(groupId);
    }
}

int ModelManager::createPopupGroupModel(int underItemId,
                                        int aboveItemId,
                                        bool popup)
{
    Q_D(ModelManager);

    int newId = d->availableId();
#ifdef QT_DEBUG
    qDebug() << __FUNCTION__ << "new group id" << newId;
#endif

    LauncherItem *underItem = d->item(underItemId);
    LauncherItem *aboveItem = d->item(aboveItemId);
    if (!underItem || !aboveItem) {
        qWarning() << "create popup group failed, under item id: " << underItemId
                   << "above item id: " << aboveItemId;
        return - 1;
    }
    int theSameCategory = d->findTheSameNumber(underItem->categories(), aboveItem->categories());
    QString groupName;
    if (theSameCategory != -1) {
        groupName = d->dataHandler->categories().value(static_cast<UKUI::Categories>(theSameCategory));
    } else {
        groupName = "应用组" + QString::number(d->groupModel.count());
    }

    MultiPageModel *groupModel = new MultiPageModel;
    PageModel *pageModel = new PageModel;
    LauncherItem *itemToGroup = new LauncherItem(*underItem);
    itemToGroup->setPageIndex(0);
    itemToGroup->setState(d->globalState);
    if (d->isAppInTheWhiteList(itemToGroup->desktopFile())) {
        itemToGroup->setUninstallable(false);
    }
    pageModel->push_back(itemToGroup);
    if (underItem->type() != LauncherItem::Group) {
        underItem->setName(groupName);
    }
    d->groupModel.insert(newId, groupModel);
    d->maxItemCountAllowedInPageOfGroup[newId] = d->kMaxItemCountInGroupPage;
    groupModel->push_back(pageModel);
    groupModel->push_back(new PageModel);

    underItem->setId(newId);
    underItem->setType(LauncherItem::Group);
    underItem->setModel(groupModel);

    // 弹出分组窗口，新建分组
    if (popup) {
        addPlaceholderItem(1, 0, 1, 1, newId);
        underItem->setState(LauncherItem::Popup);
    }
    // 直接新建分组，适用于拖动图标至另一个图标之上，然后
    else {
        LauncherItem *aboveItem = d->item(aboveItemId);
        if (!aboveItem) {
            qWarning() << "create popup group failed, above item id: " << aboveItemId;
            return - 1;
        }
        pageModel->push_back(aboveItem);
        d->launcherModel.at(aboveItem->pageIndex())->removeOne(aboveItem);
        pageModel->last()->setPageIndex(0);
    }
    Q_EMIT createGroup(newId, QList<quint32>() << underItemId << aboveItemId, groupName);
    removePlaceholderItem();
    return newId;
}

void ModelManager::setGroupName(const QString &name, int groupId)
{
    Q_D(ModelManager);
    if (!d->groupModel.contains(groupId)) {
        return;
    }
    for (const auto &pageItems : d->launcherModel) {
        for (const auto &item : *pageItems) {
            if ((item->type() == LauncherItem::Container
                 || item->type() == LauncherItem::Group) &&
                    item->id() == groupId) {
                item->setName(name);
                Q_EMIT groupNameChanged(name, groupId);
                return;
            }
        }
    }
}

void ModelManager::setAddAppCategory(int category, int groupId)
{
    Q_D(ModelManager);
    Q_ASSERT(d->groupModel.contains(groupId));
    d->currentCategoryOfAddion = category;
    d->initSelectedItemIds(groupId);

    int startPage = 0, endPage = 0;
    if (category == CategoryItem::All) {
        startPage = 0;
        endPage = d->categoryItemsModel.count() - 1;//d->categoryModel.first()->pageCount() - 1;
    } else {
        for (int index=0; index<d->categoryModel.count(); ++index) {
            if (d->categoryModel.at(index)->category() == category) {
                -- startPage;
                endPage = startPage + d->categoryModel.at(index)->pageCount();
                break;
            }
            startPage += d->categoryModel.at(index)->pageCount();
        }
    }
    for (int page=0; page<d->categoryItemsModel.count(); ++page) {
        int pageItemCount = d->categoryItemsModel.at(page)->count();
        for (int itemIndex=0; itemIndex<pageItemCount; ++itemIndex) {
            auto item = d->categoryItemsModel.at(page)->at(itemIndex);
            if (((category == CategoryItem::All
                  && !d->categoryContainsItem(item->id(), CategoryItem::Education)
                  && d->categoryOfAddtionPage(page) != CategoryItem::Education)
                 || (category != CategoryItem::All && d->dataHandler->categoriesItems()
                     .value(UKUI::Categories::Education).contains(item->id()))) &&
                    page >= startPage && page <= endPage) {
                item->setState(LauncherItem::Selectable);
                int itemGroupId = d->itemGroupId(item->id());
                if (itemGroupId == -1) {
                    item->setSelected(false);
                } else {
                    if (itemGroupId == groupId) {
                        item->setSelected(true);
                    } else {
                        item->setState(LauncherItem::Normal);
                    }
                }
            } else {
                item->setState(LauncherItem::Normal);
            }
        }
    }
}

void ModelManager::updateCachedData(int groupId)
{
    Q_D(ModelManager);
    Q_ASSERT(groupId >= -1);
    if (groupId >= 0 && !d->groupModel.contains(groupId)) {
        return;
    }
    MultiPageModel *model = groupId >= 0 ? d->groupModel.value(groupId) : &d->launcherModel;
    QList<QList<quint32>> groupItemIds;
    for (int page=0; page<model->count(); ++page) {
        QList<quint32> itemIds;
        for (int itemIndex=0;
             itemIndex < model->at(page)->count();
             ++ itemIndex) {
            int id = model->at(page)->at(itemIndex)->id();
            if (id == 0) {
                qDebug() << id;
            }
#ifdef EDU_SCREEN
            // add app icon
            if (id == 0) {
                continue;
            }
#endif
            if (model->at(page)->at(itemIndex)->type() != LauncherItem::WindowIcon) {
                itemIds.push_back(id);
            }
        }
        if (!itemIds.isEmpty()) {
            groupItemIds.push_back(itemIds);
        }
    }
    Q_EMIT updateGroup(groupItemIds, groupId);
}

bool ModelManager::groupEditable(int groupId)
{
    Q_D(ModelManager);
    return d->editableContainerId.contains(groupId);
}

void ModelManager::updateGroupItems(int groupId, int pageIndex)
{
    Q_D(ModelManager);
    Q_ASSERT(d->groupModel.contains(groupId));

    auto selectedIds = d->selectedIds();

    // add
    for (const auto &id : selectedIds) {
        if (!d->itemExists(groupId, id)) {
            pageIndex = d->autoPushBackItemForGroup(id, groupId, pageIndex);
            qDebug() << "insert page index: " << pageIndex;
        }
    }
    // remove
    auto groupIds = d->groupIds(groupId);
    for (const auto &id : groupIds) {
        // 添加按钮的id是0
        if (!selectedIds.contains(id) && id != 0) {
            d->removeGroupItem(id, groupId);
        }
    }

#if 0
    if (d->groupModel.value(groupId)->last()->count() == d->maxItemCountInPage.value(groupId)) {
        d->groupModel[groupId]->push_back(
                    d->createPage(d->groupModel.value(groupId)->count(), groupId, LauncherItem::Normal));
    }
#endif

    updateCachedData(groupId);
}

void ModelManager::setItemSelectState(int itemId,
                                      int pageIndex,
                                      bool selected)
{
    Q_D(ModelManager);
    if (d->categoryItemsModel.isEmpty()) {
        return;
    }
    if (d->categoryItemsModel.count() <= pageIndex || pageIndex < 0) {
        return;
    }

    if (selected) {
        d->currentSelectedIds.push_front(itemId);
    } else {
        d->currentSelectedIds.removeOne(itemId);
    }

    for (int page=0; page<d->categoryItemsModel.count(); ++page) {
        int itemCount = d->categoryItemsModel.at(page)->count();
        for (int itemIndex=0; itemIndex<itemCount; ++itemIndex) {
            auto item = d->categoryItemsModel.at(page)->at(itemIndex);
            if (item->id() == itemId && item->state() == LauncherItem::Selectable) {
                item->setSelected(selected);
            }
        }
    }
}

void ModelManager::setGroupState(int state, int groupId)
{
    Q_ASSERT(groupId > 0);
    Q_D(ModelManager);
    if (!d->groupModel.contains(groupId)) {
        qWarning() << "The group " << groupId << " dose not exist!";
        return;
    }
#ifdef QT_DEBUG
    qDebug() << __FUNCTION__ << state << groupId;
#endif

    auto groupModel = d->groupModel.value(groupId);

    auto setNameVisibleFunction = [](MultiPageModel *groupModel, bool visible) {
        for (const auto &pageItems : *groupModel) {
            for (const auto &item : *pageItems) {
                item->setNameVisible(visible);
            }
        }
    };

    auto setGroupItemState = [](MultiPageModel *groupModel, int state) {
        for (const auto &pageItems : *groupModel) {
            for (const auto &item : *pageItems) {
                item->setState(state);
            }
        }
    };

    for (const auto &pageModel : d->launcherModel) {
        for (const auto &item : *pageModel) {
            if (item->id() == groupId) {
                item->setState(state);
                switch (state) {
                case LauncherItem::Popup: {
                    setNameVisibleFunction(groupModel, true);
                    setGroupItemState(groupModel, state);
                    break;
                }
                case LauncherItem::Editable: {
                    setNameVisibleFunction(groupModel, false);
                    setGroupItemState(groupModel, LauncherItem::Normal);
                    break;
                }
                case LauncherItem::Normal: {
                    removePlaceholderItem();
                    setNameVisibleFunction(groupModel, false);
                    // 如果分组最小化恢复分组时，分组内只有一个app，则需要删除分组恢复为AppIcon类型
                    if (groupModel->count() == 1 &&
                            groupModel->first()->count() == 1) {
                        auto takeGroupModel = d->groupModel.take(groupId);
                        *item = *takeGroupModel->first()->first();
                        takeGroupModel->deleteLater();
                    }
                    break;
                }
                default: {
                    removePlaceholderItem();
                    setNameVisibleFunction(groupModel, false);
                    break;
                }
                }
                return;
            }
        }
    }
}

void ModelManager::addPlaceholderItem(int index,
                                      int pageIndex,
                                      int rowSpan,
                                      int columnSpan,
                                      int groupId)
{
    Q_D(ModelManager);

    qDebug() << "Add placeholder item " << index << pageIndex << groupId;
    if (index < 0 || pageIndex < 0) {
        return;
    }
    MultiPageModel *model = d->availableModel(groupId);    
    if (model == nullptr) {
        return;
    }
    // main page
    if (groupId == -1 && !d->placeholderAvailable()) {
        //将应用移动到pageIndex页之后，pageIndex页所占网格总数
        int pageGridCount = d->gridCount(groupId, pageIndex) + rowSpan*columnSpan;
        //pageIndex页最后一个item索引
        int lastItemIndex = model->at(pageIndex)->count() - 1;
        while (pageGridCount > d->pageItemMaxCount && lastItemIndex >= 0) {
            --index;
            //item所占网格总数
            int itemGridCount = model->at(pageIndex)->at(lastItemIndex)->rowSpan() *
                    model->at(pageIndex)->at(lastItemIndex)->columnSpan();
            pageGridCount -= itemGridCount;
            --lastItemIndex;
        }
        if (index < 0) {
            qWarning() << __func__ << "Add placeholder failed, the item index is not avalid.";
            return;
        }
    }
    if (groupId > 0 && !d->placeholderAvailable()) {
        if (index >= d->kMaxItemCountInGroupPage) {
            --index;
        }
    }


    if (groupId > 0 && pageIndex > model->count() - 1) {
        return;
    }

    if (d->placeholderAvailable()) {
        // 如果是在同一页的占位 item,则直接移动占位 item 即可
        if (d->placeholderItemInfo.pageIndex == pageIndex &&
                d->placeholderItemInfo.groupId == groupId) {
            moveItem(d->placeholderItemInfo.itemIndex, index, pageIndex, groupId);
            d->placeholderItemInfo.itemIndex = index;
            return;
        }
        // in the main page
        if (d->placeholderItemInfo.groupId == -1) {
            d->launcherModel.at(d->placeholderItemInfo.pageIndex)->removeAt(d->placeholderItemInfo.itemIndex);
        } else {
            model->at(d->placeholderItemInfo.pageIndex)->removeAt(d->placeholderItemInfo.itemIndex);
        }
    }
    LauncherItem *placeholderItem = new LauncherItem;
    d->placeholderItemInfo.placeholderItem = placeholderItem;
    d->placeholderItemInfo.parentItemType = groupId >= 0 ?
                ModelManagerPrivate::Group : ModelManagerPrivate::Page;
    d->placeholderItemInfo.itemIndex = index;
    d->placeholderItemInfo.pageIndex = pageIndex;
    d->placeholderItemInfo.groupId = groupId;

    placeholderItem->setRowSpan(rowSpan);
    placeholderItem->setColumnSpan(columnSpan);
    placeholderItem->setPageIndex(pageIndex);
    placeholderItem->setType(LauncherItem::Placeholder);

#ifdef EDU_SCREEN
    // 不可编辑的分组不存在添加按钮，所有需要push_back
    if (d->editableContainerId.contains(groupId)) {
        model->at(pageIndex)->insert(index, placeholderItem);
    } else {
        model->at(pageIndex)->push_back(placeholderItem);
        d->placeholderItemInfo.itemIndex = model->at(pageIndex)->count() - 1;
        qDebug() << "d->placeholderItemInfo.itemIndex2: " << d->placeholderItemInfo.itemIndex;
    }
#else
    model->at(pageIndex)->insert(index, placeholderItem);
#endif
    d->layoutForAddPlaceholderItem(pageIndex, groupId);
}

void ModelManager::removePlaceholderItem()
{
    Q_D(ModelManager);
    qDebug() << "Remove placeholder item";
    if (!d->placeholderAvailable()) {
        return;
    }
    if (d->placeholderItemInfo.parentItemType == ModelManagerPrivate::Page) {
        if (!d->itemIndexIsValid(d->placeholderItemInfo.itemIndex,
                                 d->placeholderItemInfo.pageIndex,
                                 &d->launcherModel)) {
            return;
        }
        d->launcherModel[d->placeholderItemInfo.pageIndex]->removeAt(d->placeholderItemInfo.itemIndex);
    } else if (d->placeholderItemInfo.parentItemType == ModelManagerPrivate::Group) {
        if (!d->groupModel.contains(d->placeholderItemInfo.groupId) ||
                !d->itemIndexIsValid(d->placeholderItemInfo.itemIndex,
                                     d->placeholderItemInfo.pageIndex,
                                     d->groupModel.value(d->placeholderItemInfo.groupId))) {
            return;
        }
        d->groupModel[d->placeholderItemInfo.groupId]->
                at(d->placeholderItemInfo.pageIndex)->removeAt(d->placeholderItemInfo.itemIndex);
    }
    d->layoutForRemovePlaceholderItem();
    d->clearPlaceholderItemInfo();
}

void ModelManager::replacePlaceholderItem(int fromIndex, int fromPage, int groupId)
{
    Q_D(ModelManager);
    Q_ASSERT(fromIndex >=0 && fromPage >=0 && groupId >= -1);
    if (!d->placeholderAvailable()) {
        return;
    }
#ifdef QT_DEBUG
    qDebug() << __FUNCTION__ << "group id, from page, index" << groupId << fromPage << fromIndex;
#endif

    LauncherItem *moveItem = nullptr;
    MultiPageModel *fromModel = d->availableModel(groupId);
    MultiPageModel *toModel = d->availableModel(d->placeholderItemInfo.groupId);
    if (fromModel == nullptr || toModel == nullptr) {
        return;
    }
    if (!d->itemIndexIsValid(fromIndex, fromPage, fromModel) ||
            !d->itemIndexIsValid(d->placeholderItemInfo.itemIndex, d->placeholderItemInfo.pageIndex, toModel)) {
        qWarning() << "Replace placeholder failed, the item index is not avalid.";
        return;
    }

    if (fromModel->at(fromPage)->at(fromIndex)->type() != LauncherItem::AppIcon &&
            fromModel->at(fromPage)->at(fromIndex)->type() != LauncherItem::Group &&
            fromModel->at(fromPage)->at(fromIndex)->type() != LauncherItem::Widget) {
        return;
    }

    moveItem = fromModel->at(fromPage)->takeAt(fromIndex);
    moveItem->setPageIndex(d->placeholderItemInfo.pageIndex);
    toModel->at(d->placeholderItemInfo.pageIndex)->replace(
                d->placeholderItemInfo.itemIndex, moveItem);
    moveItem->setState(LauncherItem::Editable);
    moveItem->setNameVisible(true);

#ifndef EDU_SCREEN
    // 如果分组内只有一个应用，删除该分组
    if (groupId > 0) {
        if (fromModel->size() == 1 && fromModel->first()->size() == 1) {
            d->replaceGroupWithAppIcon(groupId);
        }
        /// \todo 根据操作的类型进行选择性的更新
        updateCachedData();
        updateCachedData(groupId);
    }
#endif

#ifdef EDU_SCREEN
    d->tryAddAddIcon(fromPage, groupId);
    d->layoutForReplacePlaceholderItem();
#endif
    d->layoutChangedPages.clear();
    d->clearPlaceholderItemInfo();
}
void ModelManager::replacePlaceholderItemTaskbar(int fromIndex,
                                                 int fromPage,
                                                 int fromgroupId,
                                                 int togroupId)
{
    Q_D(ModelManager);
    qDebug() << __FUNCTION__ << fromIndex << fromPage << fromgroupId << togroupId;
    if (!d->placeholderAvailable()) {
        return;
    }
    if (d->placeholderItemInfo.groupId == fromgroupId) {
        return;
    }
    LauncherItem *moveItem = nullptr;
    MultiPageModel *model = fromgroupId >= 0 ?
                d->groupModel.value(fromgroupId) : &d->launcherModel;

    if (model->at(fromPage)->at(fromIndex)->type() != LauncherItem::AppIcon) {
        return;
    }

    moveItem = model->at(fromPage)->takeAt(fromIndex);
    moveItem->setPageIndex(d->placeholderItemInfo.pageIndex);

    int windowOpenItemIndex = -1;
    if (togroupId == 0) {
        for (int itemIndex = 0; itemIndex < d->groupModel[0]->at(0)->count(); itemIndex++) {
            if (d->groupModel[0]->at(0)->at(itemIndex)->desktopFile() == moveItem->desktopFile()) {
                windowOpenItemIndex = itemIndex;
                moveItem->setAppOpen(true);
                moveItem->setWindowId(d->groupModel[0]->at(0)->at(itemIndex)->windowId());
                break;
            }
        }
    }

    MultiPageModel *desktopModel = togroupId >= 0 ?
                d->groupModel.value(togroupId) : &d->launcherModel;

    desktopModel->at(d->placeholderItemInfo.pageIndex)->replace(
                d->placeholderItemInfo.itemIndex, moveItem);

    if (windowOpenItemIndex > -1) {
        removeItem(windowOpenItemIndex, 0, 0);
    }
    d->layoutChangedPages.clear();
    d->clearPlaceholderItemInfo();
}

QAbstractListModel* ModelManager::taskbarModel() //const
{
    Q_D(ModelManager);
    if(!d->groupModel.contains(0)) {
        MultiPageModel *multiPage = new MultiPageModel;
        multiPage->push_back(new PageModel);
        d->groupModel[0] = multiPage;
    }
    return d->groupModel[0]->at(0);
}

void ModelManager::setAppOpenState(QString desktopName, WId wid)
{
    Q_D(const ModelManager);
    for(int itemIndex = 0; itemIndex < d->groupModel[0]->at(0)->count(); itemIndex++) {
        if(d->groupModel[0]->at(0)->at(itemIndex)->desktopFile().endsWith(desktopName)) {
            d->groupModel[0]->at(0)->at(itemIndex)->setAppOpen(true);
            d->groupModel[0]->at(0)->at(itemIndex)->setWindowId(wid);
            return;
        }
    }
    qDebug() << "desktopName cannot be found";
}

void ModelManager::setAppCloseState(QString desktopName)
{
    Q_D(const ModelManager);
    for (int itemIndex = 0; itemIndex < d->groupModel[0]->at(0)->count(); itemIndex++) {
        if (d->groupModel[0]->at(0)->at(itemIndex)->desktopFile().endsWith(desktopName)) {
            d->groupModel[0]->at(0)->at(itemIndex)->setAppOpen(false);
            return;
        }
    }
    qDebug() << "desktopName cannot be found";
}

void ModelManager::addWindowIconToTaskbar(const QString &iconPath,
                                          const QString &desktopName,
                                          const QString &appName,
                                          const int &wid)
{
    Q_D(ModelManager);
    LauncherItem* item = new LauncherItem();
    item->setId(d->availableId());
    item->setPageIndex(0);
    item->setColumnSpan(1);
    item->setRowSpan(1);
    item->setEditable(false);
    item->setType(LauncherItem::WindowIcon);
    item->setIcon(iconPath);
    item->setDesktopFile(desktopName);
    item->setAppOpen(true);
    item->setWindowId(wid);
    item->setName(appName);
    if (d->groupModel.contains(d->kTaskBarGroupId) && d->groupModel.value(d->kTaskBarGroupId) != nullptr && d->groupModel[0]->count() != 0) {
        d->groupModel[0]->at(0)->append(item);
    }
}

void ModelManager::removeWindowIconFromTaskbar(const QString &iconPath)
{
    Q_D(const ModelManager);
    for (int itemIndex = 0; itemIndex < d->groupModel[0]->at(0)->count(); itemIndex++) {
        if(d->groupModel[0]->at(0)->at(itemIndex)->icon() == iconPath) {
            int index = itemIndex;
            removeItem(index, 0, 0);
            return;
        }
    }
}

void ModelManager::removeAppIconFromTaskBar(const QString &desktopFileName)
{
    Q_D(const ModelManager);
    for (int itemIndex = 0; itemIndex < d->groupModel[0]->at(0)->count(); itemIndex++) {
        if(d->groupModel[0]->at(0)->at(itemIndex)->desktopFile().contains(desktopFileName)) {
            removeItem(itemIndex, 0, 0);
            return;
        }
    }
}

QString ModelManager::findIconPath(const QString &desktopName)
{
    Q_D(const ModelManager);
    for (int pageIndex=0; pageIndex < d->launcherModel.count(); ++pageIndex) {
        for (int itemIndex=0; itemIndex < d->launcherModel.at(pageIndex)->count(); ++itemIndex) {
            if (d->launcherModel.at(pageIndex)->at(itemIndex)->type() == LauncherItem::Group) {
                int groupId = d->launcherModel.at(pageIndex)->at(itemIndex)->id();
                auto groupModel = d->groupModel.value(groupId);
                for (const auto &pageItems : *groupModel) {
                    for (const auto &item : *pageItems) {
                        if (item->desktopFile().contains(desktopName)) {
                            qDebug() << "group iconPath---->" << item->icon();
                            return item->icon();
                        }
                    }
                }
            }

            if ((d->launcherModel.at(pageIndex)->at(itemIndex)->desktopFile() == desktopName)) {
                 return d->launcherModel.at(pageIndex)->at(itemIndex)->icon();
            }
        }
    }
    return QString();
}

QString ModelManager::findDesktopName(const QString &desktopName)
{
    Q_D(const ModelManager);
    for (int pageIndex=0; pageIndex < d->launcherModel.count(); ++pageIndex) {
        for (int itemIndex=0; itemIndex < d->launcherModel.at(pageIndex)->count(); ++itemIndex) {
            if (d->launcherModel.at(pageIndex)->at(itemIndex)->type() == LauncherItem::Group) {
                int groupId = d->launcherModel.at(pageIndex)->at(itemIndex)->id();
                auto groupModel = d->groupModel.value(groupId);
                for (const auto &pageItems : *groupModel) {
                    for (const auto &item : *pageItems) {
                        if (item->desktopFile().contains(desktopName)) {
                            qDebug() << "group desktopfile---->" << item->desktopFile();
                            return item->desktopFile();
                        }
                    }
                }
            }

            if ((d->launcherModel.at(pageIndex)->at(itemIndex)->desktopFile().endsWith(desktopName))) {
                return d->launcherModel.at(pageIndex)->at(itemIndex)->desktopFile();
            }
        }
    }

    return QString();
}

QString ModelManager::findAppName(const QString &desktopName)
{
    Q_D(const ModelManager);
    for (int pageIndex=0; pageIndex < d->launcherModel.count(); ++pageIndex) {
        for (int itemIndex=0; itemIndex < d->launcherModel.at(pageIndex)->count(); ++itemIndex) {
            if (d->launcherModel.at(pageIndex)->at(itemIndex)->type() == LauncherItem::Group) {
                int groupId = d->launcherModel.at(pageIndex)->at(itemIndex)->id();
                auto groupModel = d->groupModel.value(groupId);
                for (const auto &pageItems : *groupModel) {
                    for (const auto &item : *pageItems) {
                        if (item->desktopFile().contains(desktopName)) {
                            return item->name();
                        }
                    }
                }
            }

            if ((d->launcherModel.at(pageIndex)->at(itemIndex)->desktopFile().endsWith(desktopName))) {
                return d->launcherModel.at(pageIndex)->at(itemIndex)->name();
            }
        }
    }
    return QString();
}


QString ModelManager::findDesktopNameFromTaskbar(const QString &desktopName)
{
    Q_D(ModelManager);
    if (!d->groupModel.contains(d->kTaskBarGroupId) ||
            !d->itemIndexIsValid(0, 0, d->groupModel.value(d->kTaskBarGroupId))) {
        return QString();
    }
    for (int itemIndex = 0; itemIndex < d->groupModel[0]->at(0)->count(); itemIndex++) {
        if (d->groupModel[0]->at(0)->at(itemIndex)->desktopFile().contains(desktopName)) {
            return d->groupModel[0]->at(0)->at(itemIndex)->desktopFile();
        }
    }
    return QString();
}

void ModelManager::activeWindow(const quint32 &wid)
{
    Q_D(const ModelManager);
    d->taskBarManager->appWindowOpen(wid);
}

void ModelManager::changeAppWindowWid(const QString &desktopName,
                                      const int &wid,
                                      const int &newWid)
{
    Q_D(const ModelManager);
    if (!findDesktopName(desktopName).isEmpty()) {
        for (int itemIndex = 0; itemIndex < d->groupModel[0]->at(0)->count(); itemIndex++) {
            if (d->groupModel[0]->at(0)->at(itemIndex)->desktopFile().contains(desktopName)) {
               if (d->groupModel[0]->at(0)->at(itemIndex)->windowId() == wid) {
                   d->groupModel[0]->at(0)->at(itemIndex)->setWindowId(newWid);
               }
            }
        }
    }
    else {
        for (int itemIndex = 0; itemIndex < d->groupModel[0]->at(0)->count(); itemIndex++) {
            if (d->groupModel[0]->at(0)->at(itemIndex)->desktopFile().contains(desktopName)) {
                if (d->groupModel[0]->at(0)->at(itemIndex)->windowId() == wid) {
                    d->groupModel[0]->at(0)->at(itemIndex)->setWindowId(newWid);
                }
            }
        }
    }
}

bool ModelManager::itemExistsOnDesktop(const int &itemId, const int &pageIndex)
{
    Q_D(ModelManager);
    if (pageIndex >= d->launcherModel.count() || pageIndex < 0) {
        return false;
    }
    for (int itemIndex = 0; itemIndex < d->launcherModel.at(pageIndex)->count(); itemIndex++) {
        if (d->launcherModel.at(pageIndex)->at(itemIndex)->id() == itemId) {
            return true;
        }
    }
    return false;
}

void ModelManager::initDesktopData(const QMap<quint32, QList<quint32> > &pageData,
                                   const QMap<quint32, QList<QList<quint32>>> &setData,
                                   const QMap<quint32, UKUI::BaseItem *> &itemData)
{
    Q_D(ModelManager);

    d->getAppWhiteList();
    // taskbar
    if (setData.contains(d->kTaskBarGroupId) &&
            !setData.value(d->kTaskBarGroupId).isEmpty()) {
        d->initTaskBarData(setData.value(d->kTaskBarGroupId).at(0), itemData);
    }

    QMap<quint32, QList<quint32>>::const_iterator i = pageData.constBegin();
    while (i != pageData.constEnd()) {
        PageModel *pageModel = new PageModel;
        PageModel groupPageModel, containerPageModel;
        for (const auto &itemId : i.value()) {
            UKUI::BaseItem *item = itemData.value(itemId);
            LauncherItem *launcherItem = new LauncherItem;
            if (!launcherItem || !item) {
                continue;
            }
            switch (item->getType()) {
            case UKUI::Type::Icon: {
                launcherItem = d->createLauncherItem(LauncherItem::AppIcon, item, i.key());
                if (d->isAppInTheWhiteList(launcherItem->desktopFile())) {
                    launcherItem->setUninstallable(false);
                }
                break;
            }
            case UKUI::Type::Group: {
                launcherItem = d->createLauncherItem(LauncherItem::Group, item, i.key());
                launcherItem->setUninstallable(false);

                MultiPageModel *groupModel = new MultiPageModel;
                QList<QList<quint32>> childItemsId = setData.value(item->getId());
                int page = 0;
                for (auto const &pageItemIds : childItemsId) {
                    PageModel *groupPageModel = new PageModel;
                    for (auto const &itemId : pageItemIds) {
                        LauncherItem *item = d->createLauncherItem(LauncherItem::AppIcon, itemData.value(itemId), page);
                        if (item) {
                            item->setNameVisible(false);
                            groupPageModel->push_back(item);
                            if (d->isAppInTheWhiteList(item->desktopFile())) {
                                item->setUninstallable(false);
                            }
                        }
                    }
                    ++ page;
                    groupModel->push_back(groupPageModel);
                }
                d->groupModel.insert(item->getId(), groupModel);
                launcherItem->setModel(groupModel);
                d->maxItemCountAllowedInPageOfGroup[item->getId()] = d->kMaxItemCountInGroupPage;
                break;
            }

            case UKUI::Type::Container: {
                UKUI::ContainerItem *containerItem = static_cast<UKUI::ContainerItem *>(item);
                if (!containerItem) {
                    break;
                }
                launcherItem->setId(containerItem->getId());
                launcherItem->setName(containerItem->getName());
                launcherItem->setType(LauncherItem::Container);
                launcherItem->setPageIndex(i.key());
                launcherItem->setRowSpan(containerItem->getPlaceHolder().at(0));
                launcherItem->setColumnSpan(containerItem->getPlaceHolder().at(1));
                launcherItem->setUninstallable(false);

                /// \todo 根据配置设置哪些可以修改标题
                if (d->editableContainerId.contains(launcherItem->id())) {
                    launcherItem->setEditable(true);
                }

                d->maxItemCountAllowedInPageOfGroup[launcherItem->id()] =
                        (launcherItem->rowSpan() - 1) * launcherItem->columnSpan();
                d->maxItemCountAllowedInPageOfGroup.insert(containerItem->getId(),
                                            (launcherItem->rowSpan()-1) * launcherItem->columnSpan());

                MultiPageModel *groupModel = new MultiPageModel;
                QList<QList<quint32>> childItemsId = setData.value(containerItem->getId());
                for (int page=0; page<childItemsId.count(); ++page) {
                    PageModel *groupPageModel = new PageModel;
                    int itemCount = childItemsId.at(page).count();
                    for (int itemIndex=0; itemIndex<itemCount; ++itemIndex) {
                        LauncherItem *groupItem = new LauncherItem;
                        UKUI::IconItem *appItem =
                                static_cast<UKUI::IconItem *>(itemData[childItemsId.at(page).at(itemIndex)]);
                        if (appItem) {
                            groupItem->setId(appItem->getId());
                            groupItem->setName(appItem->getName());
                            groupItem->setType(LauncherItem::AppIcon);
                            groupItem->setPageIndex(page);
                            groupItem->setIcon(appItem->getIconName());
                            groupItem->setDesktopFile(appItem->getDesktopName());
                            groupItem->setRowSpan(1);
                            groupItem->setColumnSpan(1);
                            groupPageModel->push_back(groupItem);
                            if (d->isAppInTheWhiteList(groupItem->desktopFile())) {
                                groupItem->setUninstallable(false);
                            }
                        }
                    }
                    if (d->needAddIcon &&
                            groupPageModel->count() < d->maxItemCountAllowedInPageOfGroup[containerItem->getId()] &&
                            d->editableContainerId.contains(containerItem->getId())) {
                        LauncherItem *addItem = d->createAddIconItem(page);
                        groupPageModel->push_back(addItem);
                    }
                    groupModel->push_back(groupPageModel);
                }
                d->groupModel.insert(containerItem->getId(), groupModel);
                break;
            }

            case UKUI::Type::Widget: {
                launcherItem = d->createLauncherItem(LauncherItem::Widget, item, i.key());
                break;
            }

            case UKUI::Type::Unknown:
                break;
            default:
                break;
            }
            pageModel->push_back(launcherItem);
        }
        d->launcherModel.push_back(pageModel);
        ++i;
    }
    // init task bar
    QList<WId> runningAppWindows = KWindowSystem::windows();
    for (auto &wid : runningAppWindows) {
        KWindowInfo winInfo(wid, 0, NET::WM2DesktopFileName);
        QString desktopName = d->appManager->AppDesktopFileNameByPid(winInfo.pid());
        if(desktopName.isEmpty()) {
            continue;
        }
        QString desktopname = findDesktopNameFromTaskbar(desktopName);
        QString appName = findAppName(desktopName);
        if (desktopname.isEmpty()) {
            desktopname = findDesktopName(desktopName);
            if(!desktopname.isEmpty()) {
                QString filePathName = findIconPath(desktopname);
                d->taskBarManager->setWindowDesktopFileName(wid, desktopname);
                addWindowIconToTaskbar(filePathName, desktopname, appName, wid);
            }
        } else {
            //该应用在任务栏，设置为打开状态
            setAppOpenState(desktopname, wid);
            d->taskBarManager->setWindowDesktopFileName(wid, desktopname);
        }
    }
}

void ModelManager::categories(const QMap<UKUI::Categories, QList<quint32>> categories)
{
    Q_D(ModelManager);

    d->categoryModel.clear();
    d->categoryItemsModel.clear();

    CategoryItem *allCategoryItem = new CategoryItem;
    int pageCount = d->dataHandler->apps().count() % d->pageItemMaxOfAddition ?
                d->dataHandler->apps().count() / d->pageItemMaxOfAddition + 1 :
                d->dataHandler->apps().count() / d->pageItemMaxOfAddition;
    allCategoryItem->setCategory(CategoryItem::All);
    allCategoryItem->setName("全部");
    allCategoryItem->setNormalIcon(d->categoryIcon(CategoryItem::All).first);
    allCategoryItem->setClickIcon(d->categoryIcon(CategoryItem::All).second);
    allCategoryItem->setPageCount(pageCount);
    d->categoryModel.push_back(allCategoryItem);

    // All
    auto apps = d->dataHandler->apps();
    auto appIt = apps.constBegin();
    PageModel *pageModel = new PageModel;
    while (appIt != apps.constEnd()) {
        UKUI::IconItem *iconItem =
                static_cast<UKUI::IconItem *>(d->dataHandler->items().value(*appIt));
        LauncherItem *launcherItem = new LauncherItem;
        launcherItem->setId(*appIt);
        launcherItem->setName(iconItem->getName());
        launcherItem->setIcon(iconItem->getIconName());
        launcherItem->setPageIndex(d->categoryItemsModel.isEmpty() ? 0 : d->categoryItemsModel.count());
        pageModel->push_back(launcherItem);
        if (pageModel->count() == d->pageItemMaxOfAddition) {
            d->categoryItemsModel.push_back(pageModel);
            pageModel = new PageModel;
        }
        ++ appIt;
    }
    if (!pageModel->isEmpty()) {
        d->categoryItemsModel.push_back(pageModel);
    } else {
        pageModel->deleteLater();
    }
    //

    auto it = categories.constBegin();
    while (it != categories.constEnd()) {
        if (it.value().isEmpty()) {
            ++ it;
            continue;
        }

        int category = static_cast<int>(it.key());
        CategoryItem *categoryItem = new CategoryItem;
        int pageCount = it.value().count() % d->pageItemMaxOfAddition ?
                    it.value().count() / d->pageItemMaxOfAddition + 1 :
                    it.value().count() / d->pageItemMaxOfAddition;
        categoryItem->setCategory(category + 1);
        categoryItem->setName(d->dataHandler->categoryName(static_cast<int>( it.key())));
        categoryItem->setPageCount(pageCount);
        categoryItem->setNormalIcon(d->categoryIcon(categoryItem->category()).first);
        categoryItem->setClickIcon(d->categoryIcon(categoryItem->category()).second);

        auto itemIt = it.value().constBegin();
        PageModel *pageModel = new PageModel;
        while (itemIt != it.value().constEnd()) {
            UKUI::IconItem *iconItem =
                    static_cast<UKUI::IconItem *>(d->dataHandler->items().value(*itemIt));
            LauncherItem *launcherItem = new LauncherItem;
            launcherItem->setName(iconItem->getName());
            launcherItem->setIcon(iconItem->getIconName());
            launcherItem->setId(*itemIt);
            launcherItem->setPageIndex(d->categoryItemsModel.isEmpty() ? 0 : d->categoryItemsModel.count());
            pageModel->push_back(launcherItem);
            if (pageModel->count() == d->pageItemMaxOfAddition) {
                d->categoryItemsModel.push_back(pageModel);
                pageModel = new PageModel;
            }
            ++ itemIt;
        }
        if (!pageModel->isEmpty()) {
            d->categoryItemsModel.push_back(pageModel);
        } else {
            pageModel->deleteLater();
        }

        d->categoryModel.push_back(categoryItem);
        ++ it;
    }
}

void ModelManager::removeItemFromGroup(int itemId, int groupId)
{
    Q_D(ModelManager);
    d->removeGroupItem(itemId, groupId);
    d->removeItemFromCategory(itemId);
}

void ModelManager::removeItemWithoutGroup(int itemId)
{
    Q_D(ModelManager);
    d->removeItemFromCategory(itemId);
    if (!d->needAddIcon) {
        for (int page=0; page<d->launcherModel.count(); ++page) {
            for (int itemIndex=0; itemIndex<d->launcherModel.at(page)->count(); ++itemIndex) {
                if (d->launcherModel.at(page)->at(itemIndex)->id() == itemId) {
                    removeItem(itemIndex, page);
                    return;
                }
            }
        }
    }
}

void ModelManager::itemInstalled(int itemId,
                                 int indexOfAllCategory,
                                 int indexOfCategory,
                                 int page,
                                 bool newCategory,
                                 int category)
{
    Q_D(ModelManager);

    UKUI::IconItem *iconItem =
            static_cast<UKUI::IconItem *>(d->dataHandler->items().value(itemId));
    LauncherItem *launcherItem = new LauncherItem;
    launcherItem->setName(iconItem->getName());
    launcherItem->setIcon(iconItem->getIconName());
    launcherItem->setId(itemId);
    launcherItem->setRowSpan(1);
    launcherItem->setColumnSpan(1);
    launcherItem->setPageIndex(page);
    launcherItem->setDesktopFile(iconItem->getDesktopName());
    launcherItem->setType(LauncherItem::AppIcon);

    // xc通用平板
    if (!d->needAddIcon && page != -1) {
        if (d->launcherModel.count() > page) {
            d->launcherModel.at(page)->push_back(launcherItem);
        } else if (d->launcherModel.count() == page) {
            PageModel *newPage = new PageModel;
            newPage->push_back(launcherItem);
            d->launcherModel.push_back(newPage);
        }
        return;
    }
    if (CategoryItem::All == d->currentCategoryOfAddion) {
        launcherItem->setState(LauncherItem::Selectable);
        launcherItem->setSelected(false);
    } else {
        launcherItem->setState(LauncherItem::Normal);
    }

    int insertAllCategoryPage = indexOfAllCategory / d->pageItemMaxOfAddition;
    int insertAllCategoryIndex = indexOfAllCategory % d->pageItemMaxOfAddition;        

    // new All category page
    if (d->categoryModel.first()->pageCount() <= insertAllCategoryPage) {
        d->categoryItemsModel.insert(insertAllCategoryPage, new PageModel);
        d->categoryItemsModel.at(insertAllCategoryPage)->push_back(launcherItem);
        launcherItem->setPageIndex(insertAllCategoryPage);
        int newPageCount = d->categoryModel.first()->pageCount() + 1;
        d->categoryModel.first()->setPageCount(newPageCount);
        d->updateCategoriesItemsPageIndex(insertAllCategoryPage + 1, 1);
        Q_EMIT categoryPageAdded(CategoryItem::All);
    } else {
        d->categoryItemsModel.at(insertAllCategoryPage)->insert(insertAllCategoryIndex, launcherItem);
        launcherItem->setPageIndex(insertAllCategoryPage);
        int page = insertAllCategoryPage;
        while (d->categoryItemsModel.at(page)->count() > d->pageItemMaxOfAddition &&
               (page + 1) < d->categoryModel.first()->pageCount()) {
            auto item = d->categoryItemsModel.at(page)->takeLast();
            item->setPageIndex(item->pageIndex() + 1);
            d->categoryItemsModel.at(++page)->push_front(item);
        }
        if (d->categoryItemsModel.at(page)->count() > d->pageItemMaxOfAddition) {
            auto item = d->categoryItemsModel.at(page)->takeLast();
            d->categoryItemsModel.insert(++page, new PageModel);
            int newPageCount = d->categoryModel.first()->pageCount() + 1;
            d->categoryModel.first()->setPageCount(newPageCount);
            item->setPageIndex(item->pageIndex() + 1);
            d->categoryItemsModel.at(page)->push_front(item);
            d->updateCategoriesItemsPageIndex(page + 1, 1);
            Q_EMIT categoryPageAdded(CategoryItem::All);
        }
    }

    LauncherItem *categoryLauncherItem = new LauncherItem;
    *categoryLauncherItem = *launcherItem;
    if (d->currentCategoryOfAddion == category) {
        categoryLauncherItem->setState(LauncherItem::Selectable);
        categoryLauncherItem->setSelected(false);
    } else {
        categoryLauncherItem->setState(LauncherItem::Normal);
    }
    // create a new category
    if (newCategory) {
        int newPageIndex = 0;
        bool foundNewCategoryPos = false;

        CategoryItem *categoryItem = new CategoryItem;
        categoryItem->setCategory(category);
        categoryItem->setName(d->dataHandler->categoryName(category - 1));
        categoryItem->setPageCount(1);
        categoryItem->setNormalIcon(d->categoryIcon(categoryItem->category()).first);
        categoryItem->setClickIcon(d->categoryIcon(categoryItem->category()).second);

        for (int categoryIndex=0; categoryIndex<d->categoryModel.count() - 1; ++categoryIndex) {
            newPageIndex += d->categoryModel.at(categoryIndex)->pageCount();
            if (category > d->categoryModel.at(categoryIndex)->category() &&
                    category < d->categoryModel.at(categoryIndex + 1)->category()) {
                d->categoryModel.insert(categoryIndex + 1, categoryItem);
                foundNewCategoryPos = true;
                break;
            }
        }
        // the last category
        if (!foundNewCategoryPos) {
            d->categoryModel.push_back(categoryItem);
            ++ newPageIndex;
        }
        PageModel *newPage = new PageModel;
        categoryLauncherItem->setPageIndex(newPageIndex);
        newPage->push_back(categoryLauncherItem);
        d->categoryItemsModel.insert(newPageIndex, newPage);      
        Q_EMIT categoryAdded(category);
        newPageIndex = foundNewCategoryPos ? newPageIndex + 1 : newPageIndex;
        d->updateCategoriesItemsPageIndex(newPageIndex, 1);
    } else {
        int insertCategoryPage = indexOfCategory / d->pageItemMaxOfAddition;
        int insertCategoryIndex = indexOfCategory % d->pageItemMaxOfAddition;
        int insertPage = insertCategoryPage;
        for (int index=0; index<d->categoryModel.count(); ++index) {
            if (d->categoryModel.at(index)->category() == category) {
                // insert to a new page
                if (d->categoryModel.at(index)->pageCount() <= insertCategoryPage) {
                    PageModel *pageModel = new PageModel;
                    pageModel->push_back(categoryLauncherItem);
                    if (d->categoryItemsModel.count() > insertPage) {
                        categoryLauncherItem->setPageIndex(insertPage);
                        d->categoryItemsModel.insert(insertPage, pageModel);
                        d->updateCategoriesItemsPageIndex(insertPage + 1, 1);
                    } else {
                        categoryLauncherItem->setPageIndex(d->categoryItemsModel.count());
                        d->categoryItemsModel.push_back(pageModel);
                    }
                    Q_EMIT categoryPageAdded(d->categoryModel.at(index)->category());
                }
                // insert to a already page
                else {
                    categoryLauncherItem->setPageIndex(insertPage);
                    d->categoryItemsModel.at(insertPage)->insert(insertCategoryIndex, categoryLauncherItem);
                    do {
                        if (d->categoryItemsModel.at(insertPage)->count() > d->pageItemMaxOfAddition) {
                            auto item = d->categoryItemsModel.at(insertPage)->takeLast();
                            item->setPageIndex(item->pageIndex() + 1);
                            // the last page of this category
                            if (d->categoryModel.at(index)->pageCount() == insertCategoryPage + 1) {
                                PageModel *pageModel = new PageModel;
                                pageModel->push_back(item);
                                d->categoryItemsModel.insert(insertPage + 1, pageModel);
                                d->updateCategoriesItemsPageIndex(insertPage + 2, 1);
                                d->categoryModel.at(index)->setPageCount(d->categoryModel.at(index)->pageCount() + 1);
                                Q_EMIT categoryPageAdded(d->categoryModel.at(index)->category());
                                break;
                            } else {
                                d->categoryItemsModel.at(insertPage ++)->push_front(item);
                            }
                        }
                    } while(insertPage < d->categoryModel.at(index)->pageCount());
                }
                break;
            }
            insertPage += d->categoryModel.at(index)->pageCount();
        }
    }
}

void ModelManager::iconThemeChanged()
{
    Q_D(ModelManager);
    auto itemsData = d->dataHandler->items();
    auto desktopData = d->launcherModel;
    auto groupData = d->groupModel;
    auto updateIconFunction = [&itemsData, &desktopData, &groupData](LauncherItem *item){
        int itemId = item->id();
        bool findItemId = false;
        bool findGroupItemId = false;

        if (item->type() == LauncherItem::WindowIcon) {
            for (int page = 0; page < desktopData.count(); ++page) {
                for (int itemIndex = 0; itemIndex < desktopData.at(page)->count(); ++itemIndex) {
                    if (desktopData.at(page)->at(itemIndex)->type() == LauncherItem::Group) {
                        int groupId = desktopData.at(page)->at(itemIndex)->id();
                        auto groupModel = groupData[groupId];
                        for (const auto &pageItems : *groupModel) {
                            for (const auto &groupItem : *pageItems) {
                                if (groupItem->desktopFile() == item->desktopFile()) {
                                    itemId = groupItem->id();
                                    findGroupItemId = true;
                                    break;
                                }
                            }
                            if (findGroupItemId) {
                                break;
                            }
                        }
                    }
                    if (findGroupItemId) {
                        findItemId = true;
                        break;
                    }
                    if (desktopData.at(page)->at(itemIndex)->desktopFile() == item->desktopFile()) {
                        itemId = desktopData.at(page)->at(itemIndex)->id();
                        findItemId = true;
                        break;
                    }
                }
                if (findItemId) {
                    break;
                }
            }
        }

        UKUI::IconItem *iconItem =
                static_cast<UKUI::IconItem *>(itemsData.value(itemId));
        if (iconItem) {
            item->setIcon(iconItem->getIconName());
        } else {
            qWarning() << "static_cast to UKUI::IconItem * failed: "
                       << item->id();
        }
    };
    auto groupIt = d->groupModel.begin();
    while (groupIt != d->groupModel.end()) {
        for (auto pageModel : *groupIt.value()) {
            for (auto item : *pageModel) {
                if (item->id() == 0) {
                    continue;
                }
                updateIconFunction(item);
            }
        }
        ++ groupIt;
    }

#ifdef EDU_SCREEN
    auto categoryIt = d->categoryItemsModel.begin();
    while (categoryIt != d->categoryItemsModel.end()) {
        for (auto item : (**categoryIt)) {
            if (item->id() == 0) {
                continue;
            }
            UKUI::IconItem *iconItem =
                    static_cast<UKUI::IconItem *>(itemsData.value(item->id()));
            if (iconItem) {
                item->setIcon(iconItem->getIconName());
            } else {
                qWarning() << "static_cast to category item UKUI::IconItem * failed: "
                           << item->id();
            }
        }
        ++ categoryIt;
    }
#else
    for (int page=0; page<d->launcherModel.count(); ++page) {
        for (int itemIndex=0; itemIndex<d->launcherModel.at(page)->count(); ++itemIndex) {
            if (d->launcherModel.at(page)->at(itemIndex)->type() == LauncherItem::AppIcon) {
                updateIconFunction(d->launcherModel.at(page)->at(itemIndex));
            }
        }
    }
#endif
}

void ModelManager::changeTaskbarData(appChangeInfo appChangeData)
{
    Q_D(ModelManager);

    //任务栏移除应用
    if (appChangeData.isTaskbarDeleteApp && !appChangeData.isDesktopDeleteApp && !appChangeData.isExchangePosition) {
        LauncherItem *launcherItem = d->groupModel.value(0)->at(0)->takeAt(appChangeData.itemFromPageIndex);
        launcherItem->setPageIndex(appChangeData.itemToPage);
        if (d->launcherModel.count() == appChangeData.itemToPage) {
            PageModel *newPage = new PageModel;
            newPage->push_back(launcherItem);
            d->launcherModel.push_back(newPage);
            qDebug() << "添加新页--launchermodel.count-->" << d->launcherModel.count();
        } else {
            d->launcherModel.at(appChangeData.itemToPage)->insert(appChangeData.itemToPageIndex, launcherItem);
        }
    }

    //任务栏添加应用
    if (!appChangeData.isTaskbarDeleteApp && appChangeData.isDesktopDeleteApp && !appChangeData.isExchangePosition) {
        LauncherItem *launcherItem;
        if (appChangeData.itemFromSet == -1) {
            launcherItem = d->launcherModel.at(appChangeData.itemFromPage)->takeAt(appChangeData.itemFromPageIndex);
            if (d->launcherModel.at(appChangeData.itemFromPage)->count() == 0) {
                d->launcherModel.removeAt(appChangeData.itemFromPage);
                qDebug() << "删除空白页 pageid-->" << appChangeData.itemFromPage;
            }
        }
        if (appChangeData.itemFromSet > 0) {
            int groupId = appChangeData.itemFromSet;
            int pageIndex = appChangeData.itemFromPage;
            launcherItem = d->groupModel[groupId]->at(pageIndex)->takeAt(appChangeData.itemFromPageIndex);
        }
        launcherItem->setPageIndex(0);
        d->groupModel.value(0)->at(0)->insert(appChangeData.itemToPageIndex, launcherItem);
    }

    //任务栏交换应用
    if (!appChangeData.isTaskbarDeleteApp && !appChangeData.isDesktopDeleteApp && appChangeData.isExchangePosition) {
        d->groupModel.value(0)->at(0)->move(appChangeData.itemFromPageIndex, appChangeData.itemToPageIndex);
    }
}
