KJob *ContextRepository::dissociate(Domain::Context::Ptr parent, Domain::Task::Ptr child)
{
    Item childItem;

    childItem = m_serializer->createItemFromTask(child);
    Q_ASSERT(childItem.isValid());
    auto job = new Utils::CompositeJob();
    ItemFetchJobInterface *fetchItemJob = m_storage->fetchItem(childItem);
    job->install(fetchItemJob->kjob(), [fetchItemJob, parent, job, this] {
        if (fetchItemJob->kjob()->error() != KJob::NoError)
            return;

        Q_ASSERT(fetchItemJob->items().size() == 1);
        auto childItem = fetchItemJob->items().at(0);
        auto tag = m_serializer->createTagFromContext(parent);
        Q_ASSERT(tag.isValid());
        childItem.clearTag(tag);

        auto updateJob = m_storage->updateItem(childItem);
        job->addSubjob(updateJob);
        updateJob->start();
    });

    return job;
}
TaskQueries::TaskResult::Ptr TaskQueries::findAll() const
{
    if (!m_findAll) {
        {
            TaskQueries *self = const_cast<TaskQueries*>(this);
            self->m_findAll = self->createTaskQuery();
        }

        m_findAll->setFetchFunction([this] (const TaskQuery::AddFunction &add) {
            CollectionFetchJobInterface *job = m_storage->fetchCollections(Akonadi::Collection::root(),
                                                                           StorageInterface::Recursive,
                                                                           StorageInterface::Tasks);
            Utils::JobHandler::install(job->kjob(), [this, job, add] {
                if (job->kjob()->error() != KJob::NoError)
                    return;

                for (auto collection : job->collections()) {
                    ItemFetchJobInterface *job = m_storage->fetchItems(collection);
                    Utils::JobHandler::install(job->kjob(), [this, job, add] {
                        if (job->kjob()->error() != KJob::NoError)
                            return;

                        for (auto item : job->items()) {
                            add(item);
                        }
                    });
                }
            });
        });

        m_findAll->setConvertFunction([this] (const Akonadi::Item &item) {
            return m_serializer->createTaskFromItem(item);
        });
        m_findAll->setUpdateFunction([this] (const Akonadi::Item &item, Domain::Task::Ptr &task) {
            m_serializer->updateTaskFromItem(task, item);
        });
        m_findAll->setPredicateFunction([this] (const Akonadi::Item &item) {
            return m_serializer->isTaskItem(item);
        });
        m_findAll->setRepresentsFunction([this] (const Akonadi::Item &item, const Domain::Task::Ptr &task) {
            return m_serializer->representsItem(task, item);
        });
    }

    return m_findAll->result();
}
TaskQueries::TaskResult::Ptr TaskQueries::findChildren(Domain::Task::Ptr task) const
{
    Akonadi::Item item = m_serializer->createItemFromTask(task);   
    if (!m_findChildren.contains(item.id())) {
        TaskQuery::Ptr query;

        {
            TaskQueries *self = const_cast<TaskQueries*>(this);
            query = self->createTaskQuery();
            self->m_findChildren.insert(item.id(), query);
        }

        query->setFetchFunction([this, item] (const TaskQuery::AddFunction &add) {
            ItemFetchJobInterface *job = m_storage->fetchItem(item);
            Utils::JobHandler::install(job->kjob(), [this, job, add] {
                if (job->kjob()->error() != KJob::NoError)
                    return;

                Q_ASSERT(job->items().size() == 1);
                auto item = job->items()[0];
                Q_ASSERT(item.parentCollection().isValid());
                ItemFetchJobInterface *job = m_storage->fetchItems(item.parentCollection());
                Utils::JobHandler::install(job->kjob(), [this, job, add] {
                    if (job->kjob()->error() != KJob::NoError)
                        return;

                    for (auto item : job->items())
                        add(item);
                });
            });
        });
        query->setConvertFunction([this] (const Akonadi::Item &item) {
            return m_serializer->createTaskFromItem(item);
        });
        query->setUpdateFunction([this] (const Akonadi::Item &item, Domain::Task::Ptr &task) {
            m_serializer->updateTaskFromItem(task, item);
        });
        query->setPredicateFunction([this, task] (const Akonadi::Item &item) {
            return m_serializer->isTaskChild(task, item);
        });
        query->setRepresentsFunction([this] (const Akonadi::Item &item, const Domain::Task::Ptr &task) {
            return m_serializer->representsItem(task, item);
        });
    }

    return m_findChildren.value(item.id())->result();
}
TaskQueries::TaskResult::Ptr TaskQueries::findWorkdayTopLevel() const
{
    if (!m_findWorkdayTopLevel) {
        {
            TaskQueries *self = const_cast<TaskQueries*>(this);
            self->m_findWorkdayTopLevel = self->createTaskQuery();
        }

        m_findWorkdayTopLevel->setFetchFunction([this] (const TaskQuery::AddFunction &add) {
            CollectionFetchJobInterface *job = m_storage->fetchCollections(Akonadi::Collection::root(),
                                                                           StorageInterface::Recursive,
                                                                           StorageInterface::Tasks);
            Utils::JobHandler::install(job->kjob(), [this, job, add] {
                if (job->kjob()->error() != KJob::NoError)
                    return;

                for (auto collection : job->collections()) {
                    ItemFetchJobInterface *job = m_storage->fetchItems(collection);
                    Utils::JobHandler::install(job->kjob(), [this, job, add] {
                        if (job->kjob()->error() != KJob::NoError)
                            return;

                        for (auto item : job->items()) {
                            add(item);
                        }
                    });
                }
            });
        });

        m_findWorkdayTopLevel->setConvertFunction([this] (const Akonadi::Item &item) {
            return m_serializer->createTaskFromItem(item);
        });
        m_findWorkdayTopLevel->setUpdateFunction([this] (const Akonadi::Item &item, Domain::Task::Ptr &task) {
            m_serializer->updateTaskFromItem(task, item);
        });
        m_findWorkdayTopLevel->setPredicateFunction([this] (const Akonadi::Item &item) {
            if (!m_serializer->isTaskItem(item))
                return false;

            const Domain::Task::Ptr task = m_serializer->createTaskFromItem(item);

            const QDate doneDate = task->doneDate().date();
            const QDate startDate = task->startDate().date();
            const QDate dueDate = task->dueDate().date();
            const QDate today = Utils::DateTime::currentDateTime().date();

            const bool pastStartDate = startDate.isValid() && startDate <= today;
            const bool pastDueDate = dueDate.isValid() && dueDate <= today;
            const bool todayDoneDate = doneDate == today;

            if (task->isDone())
                return todayDoneDate;
            else
                return pastStartDate || pastDueDate;

        });
        m_findWorkdayTopLevel->setRepresentsFunction([this] (const Akonadi::Item &item, const Domain::Task::Ptr &task) {
            return m_serializer->representsItem(task, item);
        });
    }

    return m_findWorkdayTopLevel->result();
}