/* This file is part of libhud-qt
 * Copyright 2013 Canonical Ltd.
 *
 * libhud-qt is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version 3,
 * as published by the Free Software Foundation.
 *
 * libhud-qt is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranties of
 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
 * PURPOSE.  See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/* Can we have dynamic (context aware) actions as they could go away when
 * context changes (switch of tab or page or activation of different item on
 * a canvas, etc) and this could confuse the recent actions list..?
 */

/* Functionality should probably be specific to a Form Factor. how and where to
 * provide the information? Number of maximum toolbar items might be different
 * and actions probably don't have shortcuts. Also Form Factor might affect the
 * parameteres (what widgets are available, how hinting works.. still TBD)
 */

#include "context.h"

#include <QMutexLocker>
#include <QDebug>

// needed for glib includes.
#undef signals
#include <libhud-2/hud.h>


using namespace Ubuntu::HUD;

class Ubuntu::HUD::Context::Private
{
public:
    QString identifier;
    QList<Action *> actions;

    GSimpleActionGroup *group;
    HudActionPublisher *publisher;
};

Context::Context(QObject *parent)
    : QObject(parent)
{
    d = new Private;

    static QMutex mutex;
    QMutexLocker locker(&mutex);
    static int id = 0;

    QString export_path = QString("/context_%1").arg(id++);

    d->group = g_simple_action_group_new();
    d->publisher = hud_action_publisher_new(HUD_ACTION_PUBLISHER_ALL_WINDOWS,
                                            qPrintable(export_path));
    hud_action_publisher_add_action_group(d->publisher, "hud", qPrintable(export_path));

    g_dbus_connection_export_action_group(g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL),
                                          qPrintable(export_path),
                                          G_ACTION_GROUP(d->group),
                                          NULL);
}

/*!
 * \class Context
 * \brief HUD search context for an application
 * \since 1.0
 *
 * Applications wishing to integrate with HUD have to declare one or more
 * contexts to be registered to HUD. A HUD context defines a set of actions,
 * toolbar items, and application AppStack information.
 *
 * Usually applications only need one context. If application provides
 * multiple distict views (like browser tabs) which each have their own
 * set of actions relevant to each view it's possible to have each view
 * specify their own HUD contexts and make each view available on the HUD
 * with their actions at the same time.
 */

using namespace Ubuntu::HUD;
Context::~Context()
{
    g_clear_object (&d->publisher);
    g_clear_object (&d->group);
    delete d;
}

/*!
 * \brief registered actions
 *
 * \returns the list of registered actions
 */
QList<Action *>
Context::actions()
{
    return d->actions;
}

/*! unique identifier for this context */
QString
Context::identifier() const
{
    return d->identifier;
}

void
Context::setIdentifier(const QString &identifier)
{
    if (d->identifier == identifier)
        return;
    d->identifier = identifier;
    emit identifierChanged(identifier);
}

void
Context::injectRecentAction(const QString &id)
{
    Q_UNUSED(id);
}

/*!
 * \brief add action to context
 * \param action
 *
 * Action will be published in HUD.
 *
 * action has to be valid througout the lifetime of the context or be
 * removed using removeAction();
 *
 * \note ownership of the action remains on the caller.
 */
void
Context::addAction(Action *action)
{
    if (d->actions.contains(action)) {
        qWarning("Trying to add action to Context that already exists.");
        return;
    }
    d->actions.append(action);
    emit actionAdded(action);
    emit actionsChanged();

    QList<GSimpleAction *> gactions;
    HudActionDescription *desc;

    // see the documentation in action.cpp.
    action->magic((void**)&desc, (void*)&gactions);

    hud_action_publisher_add_description(d->publisher, desc);
    foreach(GSimpleAction *gaction, gactions) {
        g_simple_action_group_insert(d->group, G_ACTION(gaction));
    }
}

/*!
 * \brief remove action from context
 */
void
Context::removeAction(Action *action)
{
    if (!d->actions.contains(action)) {
        qWarning("Trying to remove action from Context that does not exist.");
        return;
    }

    d->actions.removeOne(action);
    emit actionRemoved(action);
    emit actionsChanged();
}

/*!
 * if user invokes an action directly from the application UI
 * this function can be used to add the action to the list of
 * recent actions.
 * what about actions that don't have meaningful "redo" like Copy
 * or Delete or New Document?
 */
void
Context::invokeAction(const QString &id)
{
    foreach(Action *action, d->actions) {
        if (action->identifier() == id)
            action->trigger();
    }
}

HudActionPublisher *
Context::publisher()
{
    return d->publisher;
}
