// Copyright (c) 2022-2024 Manuel Schneider

#pragma once
#include "fallbackhandler.h"
#include "globalqueryhandler.h"
#include "query.h"
#include "triggerqueryhandler.h"
#include <QFutureWatcher>
namespace albert { class Item; }
class QueryEngine;

class QueryExecution : public albert::Query, public albert::Item::Observer
{
    Q_OBJECT  // needed for invokable methods

public:

    // explicit QueryBase(QueryEngine *e,
    //                    std::vector<albert::FallbackHandler*> fallback_handlers,
    //                    QString string);

    QueryExecution(QueryEngine *e,
                   std::vector<albert::FallbackHandler*> &&fallback_handlers,
                   albert::TriggerQueryHandler *query_handler,
                   QString string,
                   QString trigger);
    ~QueryExecution();

    void run();
    void cancel();

    QString trigger() const override final;
    QString string() const override final;
    QString synopsis() const override final;
    const bool &isValid() const override final;
    bool isActive() const override final;
    bool isTriggered() const override final;

    const std::vector<albert::ResultItem> &matches() override final;
    const std::vector<albert::ResultItem> &fallbacks() override final;

    bool activateMatch(uint item, uint action) override final;
    bool activateFallback(uint item, uint action) override final;

    void add(const std::shared_ptr<albert::Item> &item) override;
    void add(std::shared_ptr<albert::Item> &&item) override;
    void add(const std::vector<std::shared_ptr<albert::Item>> &items) override;
    void add(std::vector<std::shared_ptr<albert::Item>> &&items) override;

    void notify(const albert::Item*) override;

protected:

    void runFallbackHandlers();
    void invokeCollectResults();
    Q_INVOKABLE void collectResults();
    bool activate(const std::vector<albert::ResultItem>&, const QString &q, uint iidx, uint aidx);

    QueryEngine *query_engine_;
    static uint query_count;
    const uint query_id;

    const QString trigger_;
    const QString string_;

    albert::TriggerQueryHandler * const query_handler_;
    const std::vector<albert::FallbackHandler*> fallback_handlers_;

    bool valid_ = true;
    bool active_ = false;

    QFutureWatcher<void> future_watcher_;

    std::vector<albert::ResultItem> results_buffer_;
    std::mutex results_buffer_mutex_;

private:

    std::vector<albert::ResultItem> matches_;
    std::vector<albert::ResultItem> fallbacks_;

};


class GlobalQuery final : public QueryExecution,
                          public albert::TriggerQueryHandler
{
    Q_OBJECT  // needed for invokable methods

public:

    GlobalQuery(QueryEngine *e,
                std::vector<albert::FallbackHandler*> &&fallback_handlers,
                std::vector<albert::GlobalQueryHandler*> &&query_handlers,
                QString string);

    QString id() const override;
    QString name() const override;
    QString description() const override;
    void handleTriggerQuery(albert::Query &) override;

private:

    void addRankItems(std::vector<std::pair<albert::Extension*,albert::RankItem>>::iterator begin,
                      std::vector<std::pair<albert::Extension*,albert::RankItem>>::iterator end);

    std::vector<albert::GlobalQueryHandler*> query_handlers_;

};
