#include "gtest/gtest.h"
#include "CommandMap.h"
#include <cstring>
#include <vector>

std::shared_ptr<CommandMap> build()
{
    auto dcm = std::make_shared<CommandMap>();
    dcm->add("iw", 1, nullptr, false);
    dcm->add("f", 2, nullptr, true);
    auto cm = std::make_shared<CommandMap>();
    cm->add("dd", 1, nullptr, false);
    cm->add("f", 2, nullptr, true);
    cm->add("d", 3, dcm, false);
    return cm;
}

uint8_t lookup_command(std::shared_ptr<CommandMap> cm, const char * s, Command & command)
{
    std::vector<uint32_t> v;
    while (*s)
    {
        v.push_back(*s);
        s++;
    }
    return cm->lookup_command(&v[0], v.size(), command);
}

TEST(CommandMapTest, add_does_nothing_for_empty_sequence)
{
    auto cm = build();
    cm->add("", 0, nullptr, false);
}

TEST(CommandMapTest, returns_invalid_for_invalid_first_level_sequences)
{
    auto cm = build();
    Command command;
    EXPECT_EQ(CommandMap::COMMAND_INVALID, lookup_command(cm, "Q", command));
}

TEST(CommandMapTest, returns_invalid_for_invalid_second_level_sequences)
{
    auto cm = build();
    Command command;
    EXPECT_EQ(CommandMap::COMMAND_INVALID, lookup_command(cm, "dx", command));
}

TEST(CommandMapTest, returns_incomplete_when_only_count_given)
{
    auto cm = build();
    Command command;
    EXPECT_EQ(CommandMap::COMMAND_INCOMPLETE, lookup_command(cm, "42", command));
}

TEST(CommandMapTest, returns_incomplete_for_incomplete_sequence)
{
    auto cm = build();
    Command command;
    EXPECT_EQ(CommandMap::COMMAND_INCOMPLETE, lookup_command(cm, "d", command));
}

TEST(CommandMapTest, returns_incomplete_when_waiting_for_following_character)
{
    auto cm = build();
    Command command;
    EXPECT_EQ(CommandMap::COMMAND_INCOMPLETE, lookup_command(cm, "f", command));
}

TEST(CommandMapTest, returns_incomplete_when_only_count_given_for_second_unit)
{
    auto cm = build();
    Command command;
    EXPECT_EQ(CommandMap::COMMAND_INCOMPLETE, lookup_command(cm, "d23", command));
}

TEST(CommandMapTest, returns_incomplete_for_empty_sequence)
{
    auto cm = build();
    Command command;
    EXPECT_EQ(CommandMap::COMMAND_INCOMPLETE, lookup_command(cm, "", command));
}

TEST(CommandMapTest, finds_command_with_no_motion_or_following_character)
{
    auto cm = build();
    Command command;
    EXPECT_EQ(CommandMap::COMMAND_COMPLETE, lookup_command(cm, "dd", command));
    Command expected;
    expected.main.id = 1;
    EXPECT_EQ(expected, command);
}

TEST(CommandMapTest, includes_command_count_with_no_following_character)
{
    auto cm = build();
    Command command;
    EXPECT_EQ(CommandMap::COMMAND_COMPLETE, lookup_command(cm, "4dd", command));
    Command expected;
    expected.main.count = 4;
    expected.main.id = 1;
    EXPECT_EQ(expected, command);
}

TEST(CommandMapTest, finds_command_with_following_character)
{
    auto cm = build();
    Command command;
    EXPECT_EQ(CommandMap::COMMAND_COMPLETE, lookup_command(cm, "fx", command));
    Command expected;
    expected.main.id = 2;
    expected.main.following_char = 'x';
    EXPECT_EQ(expected, command);
}

TEST(CommandMapTest, includes_command_count_with_following_character)
{
    auto cm = build();
    Command command;
    EXPECT_EQ(CommandMap::COMMAND_COMPLETE, lookup_command(cm, "30fx", command));
    Command expected;
    expected.main.count = 30;
    expected.main.id = 2;
    expected.main.following_char = 'x';
    EXPECT_EQ(expected, command);
}

TEST(CommandMapTest, finds_command_with_motion)
{
    auto cm = build();
    Command command;
    EXPECT_EQ(CommandMap::COMMAND_COMPLETE, lookup_command(cm, "diw", command));
    Command expected;
    expected.main.id = 3;
    expected.motion.id = 1;
    EXPECT_EQ(expected, command);
}

TEST(CommandMapTest, finds_command_with_motion_with_following_char)
{
    auto cm = build();
    Command command;
    EXPECT_EQ(CommandMap::COMMAND_COMPLETE, lookup_command(cm, "dfx", command));
    Command expected;
    expected.main.id = 3;
    expected.motion.id = 2;
    expected.motion.following_char = 'x';
    EXPECT_EQ(expected, command);
}

TEST(CommandMapTest, includes_motion_unit_count)
{
    auto cm = build();
    Command command;
    EXPECT_EQ(CommandMap::COMMAND_COMPLETE, lookup_command(cm, "d5fx", command));
    Command expected;
    expected.main.id = 3;
    expected.motion.count = 5;
    expected.motion.id = 2;
    expected.motion.following_char = 'x';
    EXPECT_EQ(expected, command);
}
