/*
 * Copyright (C) 2015 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 "InputDevice.h"

#include <memory>

#include <linux/input.h>

#include <gtest/gtest.h>

#include <utils/Timers.h>

#include "InputHub.h"
#include "InputMocks.h"
#include "MockInputHost.h"

// # of milliseconds to allow for timing measurements
#define TIMING_TOLERANCE_MS 25

#define MSC_ANDROID_TIME_SEC  0x6
#define MSC_ANDROID_TIME_USEC 0x7

using ::testing::_;
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::ReturnNull;

namespace android {
namespace tests {

class EvdevDeviceTest : public ::testing::Test {
protected:
    virtual void SetUp() {
        // Creating device identifiers and definitions should always happen.
        EXPECT_CALL(mHost, createDeviceIdentifier(_, _, _, _, _))
            .WillOnce(ReturnNull());
        EXPECT_CALL(mHost, createDeviceDefinition())
            .WillOnce(Return(&mDeviceDef));
        // InputMappers may cause any of these to be called, but we are not
        // testing these here.
        ON_CALL(mHost, createInputReportDefinition())
            .WillByDefault(Return(&mReportDef));
        ON_CALL(mHost, createOutputReportDefinition())
            .WillByDefault(Return(&mReportDef));
        ON_CALL(mHost, registerDevice(_, _))
            .WillByDefault(ReturnNull());
    }

    MockInputHost mHost;
    // Ignore uninteresting calls on the report definitions by using NiceMocks.
    NiceMock<MockInputReportDefinition> mReportDef;
    NiceMock<MockInputDeviceDefinition> mDeviceDef;
};

TEST_F(EvdevDeviceTest, testOverrideTime) {
    auto node = std::make_shared<MockInputDeviceNode>();
    auto device = std::make_unique<EvdevDevice>(&mHost, node);
    ASSERT_TRUE(device != nullptr);

    // Send two timestamp override events before an input event.
    nsecs_t when = 2ULL;
    InputEvent msc1 = { when, EV_MSC, MSC_ANDROID_TIME_SEC, 1 };
    InputEvent msc2 = { when, EV_MSC, MSC_ANDROID_TIME_USEC, 900000 };

    // Send a key down and syn. Should get the overridden timestamp.
    InputEvent keyDown = { when, EV_KEY, KEY_HOME, 1 };
    InputEvent syn = { when, EV_SYN, SYN_REPORT, 0 };

    // Send a key up, which should be at the reported timestamp.
    InputEvent keyUp = { when, EV_KEY, KEY_HOME, 0 };

    device->processInput(msc1, when);
    device->processInput(msc2, when);
    device->processInput(keyDown, when);
    device->processInput(syn, when);
    device->processInput(keyUp, when);

    nsecs_t expectedWhen = s2ns(1) + us2ns(900000);
    EXPECT_EQ(expectedWhen, keyDown.when);
    EXPECT_EQ(expectedWhen, syn.when);
    EXPECT_EQ(when, keyUp.when);
}

TEST_F(EvdevDeviceTest, testWrongClockCorrection) {
    auto node = std::make_shared<MockInputDeviceNode>();
    auto device = std::make_unique<EvdevDevice>(&mHost, node);
    ASSERT_TRUE(device != nullptr);

    auto now = systemTime(SYSTEM_TIME_MONOTONIC);

    // Input event that supposedly comes from 1 minute in the future. In
    // reality, the timestamps would be much further off.
    InputEvent event = { now + s2ns(60), EV_KEY, KEY_HOME, 1 };

    device->processInput(event, now);

    EXPECT_NEAR(now, event.when, ms2ns(TIMING_TOLERANCE_MS));
}

TEST_F(EvdevDeviceTest, testClockCorrectionOk) {
    auto node = std::make_shared<MockInputDeviceNode>();
    auto device = std::make_unique<EvdevDevice>(&mHost, node);
    ASSERT_TRUE(device != nullptr);

    auto now = systemTime(SYSTEM_TIME_MONOTONIC);

    // Input event from now, but will be reported as if it came early.
    InputEvent event = { now, EV_KEY, KEY_HOME, 1 };

    // event_time parameter is 11 seconds in the past, so it looks like we used
    // the wrong clock.
    device->processInput(event, now - s2ns(11));

    EXPECT_NEAR(now, event.when, ms2ns(TIMING_TOLERANCE_MS));
}

TEST_F(EvdevDeviceTest, testN7v2Touchscreen) {
    auto node = std::shared_ptr<MockInputDeviceNode>(MockNexus7v2::getElanTouchscreen());
    auto device = std::make_unique<EvdevDevice>(&mHost, node);
    EXPECT_EQ(INPUT_DEVICE_CLASS_TOUCH|INPUT_DEVICE_CLASS_TOUCH_MT,
            device->getInputClasses());
}

TEST_F(EvdevDeviceTest, testN7v2ButtonJack) {
    auto node = std::shared_ptr<MockInputDeviceNode>(MockNexus7v2::getButtonJack());
    auto device = std::make_unique<EvdevDevice>(&mHost, node);
    EXPECT_EQ(INPUT_DEVICE_CLASS_KEYBOARD, device->getInputClasses());
}

TEST_F(EvdevDeviceTest, testN7v2HeadsetJack) {
    // Eventually these mock device tests will all expect these calls. For now
    // only the SwitchInputMapper has been implemented.
    // TODO: move this expectation out to a common function
    EXPECT_CALL(mHost, createInputReportDefinition());
    EXPECT_CALL(mHost, createOutputReportDefinition());
    EXPECT_CALL(mHost, freeReportDefinition(_));
    EXPECT_CALL(mHost, registerDevice(_, _));

    auto node = std::shared_ptr<MockInputDeviceNode>(MockNexus7v2::getHeadsetJack());
    auto device = std::make_unique<EvdevDevice>(&mHost, node);
    EXPECT_EQ(INPUT_DEVICE_CLASS_SWITCH, device->getInputClasses());
}

TEST_F(EvdevDeviceTest, testN7v2H2wButton) {
    auto node = std::shared_ptr<MockInputDeviceNode>(MockNexus7v2::getH2wButton());
    auto device = std::make_unique<EvdevDevice>(&mHost, node);
    EXPECT_EQ(INPUT_DEVICE_CLASS_KEYBOARD, device->getInputClasses());
}

TEST_F(EvdevDeviceTest, testN7v2GpioKeys) {
    auto node = std::shared_ptr<MockInputDeviceNode>(MockNexus7v2::getGpioKeys());
    auto device = std::make_unique<EvdevDevice>(&mHost, node);
    EXPECT_EQ(INPUT_DEVICE_CLASS_KEYBOARD, device->getInputClasses());
}

TEST_F(EvdevDeviceTest, testNexusPlayerGpioKeys) {
    auto node = std::shared_ptr<MockInputDeviceNode>(MockNexusPlayer::getGpioKeys());
    auto device = std::make_unique<EvdevDevice>(&mHost, node);
    EXPECT_EQ(INPUT_DEVICE_CLASS_KEYBOARD, device->getInputClasses());
}

TEST_F(EvdevDeviceTest, testNexusPlayerMidPowerBtn) {
    auto node = std::shared_ptr<MockInputDeviceNode>(MockNexusPlayer::getMidPowerBtn());
    auto device = std::make_unique<EvdevDevice>(&mHost, node);
    EXPECT_EQ(INPUT_DEVICE_CLASS_KEYBOARD, device->getInputClasses());
}

TEST_F(EvdevDeviceTest, testNexusRemote) {
    auto node = std::shared_ptr<MockInputDeviceNode>(MockNexusPlayer::getNexusRemote());
    auto device = std::make_unique<EvdevDevice>(&mHost, node);
    EXPECT_EQ(INPUT_DEVICE_CLASS_KEYBOARD, device->getInputClasses());
}

TEST_F(EvdevDeviceTest, testAsusGamepad) {
    auto node = std::shared_ptr<MockInputDeviceNode>(MockNexusPlayer::getAsusGamepad());
    auto device = std::make_unique<EvdevDevice>(&mHost, node);
    EXPECT_EQ(INPUT_DEVICE_CLASS_JOYSTICK|INPUT_DEVICE_CLASS_KEYBOARD, device->getInputClasses());
}

TEST_F(EvdevDeviceTest, testMocks) {
    auto node = std::make_shared<MockInputDeviceNode>();
    auto device = std::make_unique<EvdevDevice>(&mHost, node);
}

}  // namespace tests
}  // namespace android