/*
 * Copyright © 2016 Canonical Ltd.
 *
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 3,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com>
 */

#include "dbus_bus.h"
#include "dbus_client.h"
#include "src/adapters/lsc_user_activity.h"

#include "wait_condition.h"

#include <gtest/gtest.h>
#include <gmock/gmock.h>

#include <chrono>

namespace rt = repowerd::test;
using namespace std::chrono_literals;

namespace
{

struct LscUserActivityDBusClient : rt::DBusClient
{
    LscUserActivityDBusClient(std::string const& dbus_address)
        : rt::DBusClient{
            dbus_address,
            "com.lomiri.SystemCompositor.UserActivity",
            "/com/lomiri/SystemCompositor/UserActivity"}
    {
        connection.request_name("com.lomiri.SystemCompositor.UserActivity");
    }

    void emit_user_activity_changing_power_state()
    {
        emit_signal("com.lomiri.SystemCompositor.UserActivity", "Activity", g_variant_new("(i)", 0));
    }

    void emit_user_activity_extending_power_state()
    {
        emit_signal("com.lomiri.SystemCompositor.UserActivity", "Activity", g_variant_new("(i)", 1));
    }
};

struct AnLscUserActivity : testing::Test
{
    AnLscUserActivity()
    {
        registrations.push_back(
            lsc_user_activity.register_user_activity_handler(
                [this] (repowerd::UserActivityType type)
                {
                    mock_handlers.user_activity(type);
                }));

        lsc_user_activity.start_processing();
    }

    struct MockHandlers
    {
        MOCK_METHOD1(user_activity, void(repowerd::UserActivityType));
    };
    testing::NiceMock<MockHandlers> mock_handlers;

    rt::DBusBus bus;
    repowerd::LscUserActivity lsc_user_activity{bus.address()};
    LscUserActivityDBusClient client{bus.address()};
    std::vector<repowerd::HandlerRegistration> registrations;

    std::chrono::seconds const default_timeout{3};
};

}

TEST_F(AnLscUserActivity, calls_handler_for_user_activity_changing_power_state)
{
    rt::WaitCondition request_processed;

    EXPECT_CALL(mock_handlers, user_activity(repowerd::UserActivityType::change_power_state))
        .WillOnce(WakeUp(&request_processed));

    client.emit_user_activity_changing_power_state();

    request_processed.wait_for(default_timeout);
    EXPECT_TRUE(request_processed.woken());
}

TEST_F(AnLscUserActivity, calls_handler_for_user_activity_extending_power_state)
{
    rt::WaitCondition request_processed;

    EXPECT_CALL(mock_handlers, user_activity(repowerd::UserActivityType::extend_power_state))
        .WillOnce(WakeUp(&request_processed));

    client.emit_user_activity_extending_power_state();

    request_processed.wait_for(default_timeout);
    EXPECT_TRUE(request_processed.woken());
}

TEST_F(AnLscUserActivity, does_not_calls_unregistered_handlers)
{
    using namespace testing;

    registrations.clear();

    EXPECT_CALL(mock_handlers, user_activity(_)).Times(0);

    client.emit_user_activity_changing_power_state();
    client.emit_user_activity_extending_power_state();

    // Give some time for dbus signals to be delivered
    std::this_thread::sleep_for(100ms);
}
