#include "gtest/gtest.h"
#include "GapBuffer.h"
#include "System.h"
#include <string>

TEST(GapBufferTest, creates_an_empty_buffer)
{
    GapBuffer gp;
    EXPECT_EQ(0u, gp.size());
}

TEST(GapBufferTest, creates_a_buffer_from_preallocated_memory)
{
    uint8_t * b = (uint8_t *)System::alloc_pages(2u);
    strcpy((char *)b, "hi\n");
    GapBuffer gp(b, System::page_size * 2u, 3u);

    EXPECT_EQ(3u, gp.size());
}

TEST(GapBufferTest, allows_inserting)
{
    GapBuffer gp;

    gp.insert(0u, (const uint8_t *)"test", 4u);
    EXPECT_EQ('s', *gp.address(2u));
    EXPECT_EQ(4u, gp.size());
    EXPECT_EQ("test", gp.get_string());

    gp.insert(2u, (const uint8_t *)"12", 2u);
    /* The gap should be between the '2' and 's' now */
    EXPECT_GT(gp.address(4u) - gp.address(3u), 100u);
    EXPECT_EQ('s', *gp.address(4u));
    EXPECT_EQ(6u, gp.size());
    EXPECT_EQ("te12st", gp.get_string());

    gp.insert(0u, (const uint8_t *)"-", 1u);
    EXPECT_EQ("-te12st", gp.get_string());
    EXPECT_EQ(7u, gp.size());

    gp.insert(7u, (const uint8_t *)"!", 1u);
    EXPECT_EQ("-te12st!", gp.get_string());
    EXPECT_EQ(8u, gp.size());
}

TEST(GapBufferTest, allows_deleting)
{
    GapBuffer gp;

    gp.insert(0u, (const uint8_t *)"test_123", 8u);
    EXPECT_EQ(8u, gp.size());
    EXPECT_EQ("test_123", gp.get_string());

    gp.erase(6u, 2u);
    EXPECT_EQ(6u, gp.size());
    EXPECT_EQ("test_1", gp.get_string());

    gp.insert(4u, (const uint8_t *)"!@#$", 4u);
    EXPECT_EQ(10u, gp.size());
    EXPECT_EQ("test!@#$_1", gp.get_string());

    gp.erase(4u, 4u);
    EXPECT_EQ(6u, gp.size());
    EXPECT_EQ("test_1", gp.get_string());

    gp.insert(4u, (const uint8_t *)"^&", 2u);
    EXPECT_EQ(8u, gp.size());
    EXPECT_EQ("test^&_1", gp.get_string());

    gp.erase(2u, 5u);
    EXPECT_EQ(3u, gp.size());
    EXPECT_EQ("te1", gp.get_string());
}

TEST(GapBufferTest, allocates_more_memory_and_copies_buffer_contents_when_needed)
{
    GapBuffer gp;
    std::string s("ABCD");

    gp.insert(0u, (const uint8_t *)"ABCDEFGH", 8u);
    for (size_t i = 0u; i < (80u * 1024u); i++)
    {
        gp.insert(4u, (const uint8_t *)"-", 1u);
        s += "-";
    }
    s += "EFGH";
    EXPECT_EQ(s, gp.get_string());
}

TEST(GapBufferTest, allows_copying_data_range_from_one_gap_buffer_to_another)
{
    GapBuffer gp1, gp2;
    gp1.insert(0u, (const uint8_t *)"ABCDEFGH", 8u);
    gp1.copy_to(0u, 4u, gp2, 0u);
    EXPECT_EQ("ABCD", gp2.get_string());
    gp1.insert(0u, (const uint8_t *)"IJKL", 4u);
    gp1.copy_to(8u, 2u, gp2, 0u);
    EXPECT_EQ("EFABCD", gp2.get_string());
    gp1.copy_to(2u, 4u, gp2, 3u);
    EXPECT_EQ("EFAKLABBCD", gp2.get_string());
}
