/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "EventHub.h"

#include "UinputDevice.h"

#include <gtest/gtest.h>
#include <inttypes.h>
#include <linux/uinput.h>
#include <log/log.h>
#include <chrono>

#define TAG "EventHub_test"

using android::createUinputDevice;
using android::EventHub;
using android::EventHubInterface;
using android::InputDeviceIdentifier;
using android::RawEvent;
using android::sp;
using android::UinputHomeKey;
using std::chrono_literals::operator""ms;
using std::chrono_literals::operator""s;

static constexpr bool DEBUG = false;

static void dumpEvents(const std::vector<RawEvent>& events) {
    for (const RawEvent& event : events) {
        if (event.type >= EventHubInterface::FIRST_SYNTHETIC_EVENT) {
            switch (event.type) {
                case EventHubInterface::DEVICE_ADDED:
                    ALOGI("Device added: %i", event.deviceId);
                    break;
                case EventHubInterface::DEVICE_REMOVED:
                    ALOGI("Device removed: %i", event.deviceId);
                    break;
                case EventHubInterface::FINISHED_DEVICE_SCAN:
                    ALOGI("Finished device scan.");
                    break;
            }
        } else {
            ALOGI("Device %" PRId32 " : time = %" PRId64 ", type %i, code %i, value %i",
                  event.deviceId, event.when, event.type, event.code, event.value);
        }
    }
}

// --- EventHubTest ---
class EventHubTest : public testing::Test {
protected:
    std::unique_ptr<EventHubInterface> mEventHub;
    // We are only going to emulate a single input device currently.
    std::unique_ptr<UinputHomeKey> mKeyboard;
    int32_t mDeviceId;

    virtual void SetUp() override {
#if !defined(__ANDROID__)
        GTEST_SKIP() << "It's only possible to interact with uinput on device";
#endif
        mEventHub = std::make_unique<EventHub>();
        consumeInitialDeviceAddedEvents();
        mKeyboard = createUinputDevice<UinputHomeKey>();
        ASSERT_NO_FATAL_FAILURE(mDeviceId = waitForDeviceCreation());
    }
    virtual void TearDown() override {
#if !defined(__ANDROID__)
        return;
#endif
        mKeyboard.reset();
        waitForDeviceClose(mDeviceId);
        assertNoMoreEvents();
    }

    /**
     * Return the device id of the created device.
     */
    int32_t waitForDeviceCreation();
    void waitForDeviceClose(int32_t deviceId);
    void consumeInitialDeviceAddedEvents();
    void assertNoMoreEvents();
    /**
     * Read events from the EventHub.
     *
     * If expectedEvents is set, wait for a significant period of time to try and ensure that
     * the expected number of events has been read. The number of returned events
     * may be smaller (if timeout has been reached) or larger than expectedEvents.
     *
     * If expectedEvents is not set, return all of the immediately available events.
     */
    std::vector<RawEvent> getEvents(std::optional<size_t> expectedEvents = std::nullopt);
};

std::vector<RawEvent> EventHubTest::getEvents(std::optional<size_t> expectedEvents) {
    std::vector<RawEvent> events;

    while (true) {
        std::chrono::milliseconds timeout = 0s;
        if (expectedEvents) {
            timeout = 2s;
        }

        std::vector<RawEvent> newEvents = mEventHub->getEvents(timeout.count());
        if (newEvents.empty()) {
            break;
        }
        events.insert(events.end(), newEvents.begin(), newEvents.end());
        if (expectedEvents && events.size() >= *expectedEvents) {
            break;
        }
    }
    if (DEBUG) {
        dumpEvents(events);
    }
    return events;
}

/**
 * Since the test runs on a real platform, there will be existing devices
 * in addition to the test devices being added. Therefore, when EventHub is first created,
 * it will return a lot of "device added" type of events.
 */
void EventHubTest::consumeInitialDeviceAddedEvents() {
    std::vector<RawEvent> events = getEvents();
    std::set<int32_t /*deviceId*/> existingDevices;
    // All of the events should be DEVICE_ADDED type, except the last one.
    for (size_t i = 0; i < events.size() - 1; i++) {
        const RawEvent& event = events[i];
        EXPECT_EQ(EventHubInterface::DEVICE_ADDED, event.type);
        existingDevices.insert(event.deviceId);
    }
    // None of the existing system devices should be changing while this test is run.
    // Check that the returned device ids are unique for all of the existing devices.
    EXPECT_EQ(existingDevices.size(), events.size() - 1);
    // The last event should be "finished device scan"
    EXPECT_EQ(EventHubInterface::FINISHED_DEVICE_SCAN, events[events.size() - 1].type);
}

int32_t EventHubTest::waitForDeviceCreation() {
    // Wait a little longer than usual, to ensure input device has time to be created
    std::vector<RawEvent> events = getEvents(2);
    if (events.size() != 2) {
        ADD_FAILURE() << "Instead of 2 events, received " << events.size();
        return 0; // this value is unused
    }
    const RawEvent& deviceAddedEvent = events[0];
    EXPECT_EQ(static_cast<int32_t>(EventHubInterface::DEVICE_ADDED), deviceAddedEvent.type);
    InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceAddedEvent.deviceId);
    const int32_t deviceId = deviceAddedEvent.deviceId;
    EXPECT_EQ(identifier.name, mKeyboard->getName());
    const RawEvent& finishedDeviceScanEvent = events[1];
    EXPECT_EQ(static_cast<int32_t>(EventHubInterface::FINISHED_DEVICE_SCAN),
              finishedDeviceScanEvent.type);
    return deviceId;
}

void EventHubTest::waitForDeviceClose(int32_t deviceId) {
    std::vector<RawEvent> events = getEvents(2);
    ASSERT_EQ(2U, events.size());
    const RawEvent& deviceRemovedEvent = events[0];
    EXPECT_EQ(static_cast<int32_t>(EventHubInterface::DEVICE_REMOVED), deviceRemovedEvent.type);
    EXPECT_EQ(deviceId, deviceRemovedEvent.deviceId);
    const RawEvent& finishedDeviceScanEvent = events[1];
    EXPECT_EQ(static_cast<int32_t>(EventHubInterface::FINISHED_DEVICE_SCAN),
              finishedDeviceScanEvent.type);
}

void EventHubTest::assertNoMoreEvents() {
    std::vector<RawEvent> events = getEvents();
    ASSERT_TRUE(events.empty());
}

/**
 * Ensure that two identical devices get assigned unique descriptors from EventHub.
 */
TEST_F(EventHubTest, DevicesWithMatchingUniqueIdsAreUnique) {
    std::unique_ptr<UinputHomeKey> keyboard2 = createUinputDevice<UinputHomeKey>();
    int32_t deviceId2;
    ASSERT_NO_FATAL_FAILURE(deviceId2 = waitForDeviceCreation());

    ASSERT_NE(mEventHub->getDeviceIdentifier(mDeviceId).descriptor,
              mEventHub->getDeviceIdentifier(deviceId2).descriptor);
    keyboard2.reset();
    waitForDeviceClose(deviceId2);
}

/**
 * Ensure that input_events are generated with monotonic clock.
 * That means input_event should receive a timestamp that is in the future of the time
 * before the event was sent.
 * Input system uses CLOCK_MONOTONIC everywhere in the code base.
 */
TEST_F(EventHubTest, InputEvent_TimestampIsMonotonic) {
    nsecs_t lastEventTime = systemTime(SYSTEM_TIME_MONOTONIC);
    ASSERT_NO_FATAL_FAILURE(mKeyboard->pressAndReleaseHomeKey());

    std::vector<RawEvent> events = getEvents(4);
    ASSERT_EQ(4U, events.size()) << "Expected to receive 2 keys and 2 syncs, total of 4 events";
    for (const RawEvent& event : events) {
        // Cannot use strict comparison because the events may happen too quickly
        ASSERT_LE(lastEventTime, event.when) << "Event must have occurred after the key was sent";
        ASSERT_LT(std::chrono::nanoseconds(event.when - lastEventTime), 100ms)
                << "Event times are too far apart";
        lastEventTime = event.when; // Ensure all returned events are monotonic
    }
}

// --- BitArrayTest ---
class BitArrayTest : public testing::Test {
protected:
    static constexpr size_t SINGLE_ELE_BITS = 32UL;
    static constexpr size_t MULTI_ELE_BITS = 256UL;

    virtual void SetUp() override {
        mBitmaskSingle.loadFromBuffer(mBufferSingle);
        mBitmaskMulti.loadFromBuffer(mBufferMulti);
    }

    android::BitArray<SINGLE_ELE_BITS> mBitmaskSingle;
    android::BitArray<MULTI_ELE_BITS> mBitmaskMulti;

private:
    const typename android::BitArray<SINGLE_ELE_BITS>::Buffer mBufferSingle = {
            0x800F0F0FUL // bit 0 - 31
    };
    const typename android::BitArray<MULTI_ELE_BITS>::Buffer mBufferMulti = {
            0xFFFFFFFFUL, // bit 0 - 31
            0x01000001UL, // bit 32 - 63
            0x00000000UL, // bit 64 - 95
            0x80000000UL, // bit 96 - 127
            0x00000000UL, // bit 128 - 159
            0x00000000UL, // bit 160 - 191
            0x80000008UL, // bit 192 - 223
            0x00000000UL, // bit 224 - 255
    };
};

TEST_F(BitArrayTest, SetBit) {
    ASSERT_TRUE(mBitmaskSingle.test(0));
    ASSERT_TRUE(mBitmaskSingle.test(31));
    ASSERT_FALSE(mBitmaskSingle.test(7));

    ASSERT_TRUE(mBitmaskMulti.test(32));
    ASSERT_TRUE(mBitmaskMulti.test(56));
    ASSERT_FALSE(mBitmaskMulti.test(192));
    ASSERT_TRUE(mBitmaskMulti.test(223));
    ASSERT_FALSE(mBitmaskMulti.test(255));
}

TEST_F(BitArrayTest, AnyBit) {
    ASSERT_TRUE(mBitmaskSingle.any(31, 32));
    ASSERT_FALSE(mBitmaskSingle.any(12, 16));

    ASSERT_TRUE(mBitmaskMulti.any(31, 32));
    ASSERT_FALSE(mBitmaskMulti.any(33, 33));
    ASSERT_TRUE(mBitmaskMulti.any(32, 55));
    ASSERT_TRUE(mBitmaskMulti.any(33, 57));
    ASSERT_FALSE(mBitmaskMulti.any(33, 55));
    ASSERT_FALSE(mBitmaskMulti.any(130, 190));

    ASSERT_FALSE(mBitmaskMulti.any(128, 195));
    ASSERT_TRUE(mBitmaskMulti.any(128, 196));
    ASSERT_TRUE(mBitmaskMulti.any(128, 224));
    ASSERT_FALSE(mBitmaskMulti.any(255, 256));
}

TEST_F(BitArrayTest, SetBit_InvalidBitIndex) {
    ASSERT_FALSE(mBitmaskSingle.test(32));
    ASSERT_FALSE(mBitmaskMulti.test(256));
}

TEST_F(BitArrayTest, AnyBit_InvalidBitIndex) {
    ASSERT_FALSE(mBitmaskSingle.any(32, 32));
    ASSERT_FALSE(mBitmaskSingle.any(33, 34));

    ASSERT_FALSE(mBitmaskMulti.any(256, 256));
    ASSERT_FALSE(mBitmaskMulti.any(257, 258));
    ASSERT_FALSE(mBitmaskMulti.any(0, 0));
}
