#include "gtest/gtest.h"
#include "BufferView.h"

#define C(x) ((uint32_t)(x))

static std::shared_ptr<Buffer> buffer1()
{
    static const char data[] =
        "0\n"
        "1ab\n"
        "2abc\n"
        "3\n"
        "4\n"
        "5\n"
        "6abcdefg\n"
        "7\n"
        "8\n"
        "9abcdefghijklmno\n"
        "10\n"
        "11\n"
        "12\n";
    return std::make_shared<Buffer>((const uint8_t *)data, strlen(data));
}

static std::shared_ptr<Buffer> buffer2()
{
    static const char data[] =
        "0\n"
        "\n"
        "2\n"
        "3\n"
        "4\n"
        "\n"
        "6\n"
        "7\n"
        "8\n";
    return std::make_shared<Buffer>((const uint8_t *)data, strlen(data));
}

static std::shared_ptr<Buffer> buffer_empty()
{
    return std::make_shared<Buffer>(nullptr, 0u);
}

class MyCharacterWidthDeterminer : public CharacterWidthDeterminer
{
    int operator()(uint32_t character)
    {
        if (character < 32)
        {
            return 2;
        }
        else
        {
            return 1;
        }
    }
} Cwd;

static int LineNumber(std::shared_ptr<Buffer::Iterator> iterator)
{
    int line_number = 0;
    auto i = std::make_shared<Buffer::Iterator>(*iterator);
    i->go_start_of_line();
    for (;;)
    {
        uint32_t c = **i;
        if ((c < '0') || (c > '9'))
            break;
        line_number *= 10;
        line_number += ((int)c - '0');
        i->go_forward();
    }
    return line_number;
}

TEST(BufferViewTest, does_not_iterate_for_empty_buffer)
{
    std::shared_ptr<Buffer> b = buffer_empty();
    std::shared_ptr<Buffer::Iterator> iterator = b->add_iterator();
    BufferView bv(b, iterator, Cwd);
    bv.resize(40, 8);
    int iter_count = 0;
    bv.update();
    for (auto it = bv.vert_iter(); it.is_valid(); it++)
    {
        iter_count++;
    }
    EXPECT_EQ(0, iter_count);
    EXPECT_EQ(0, bv.cursor_screen_row());
    EXPECT_EQ(0, bv.cursor_screen_column());
    EXPECT_EQ(0, bv.cursor_virtual_column());
}

TEST(BufferViewTest, iterates_through_screen_lines_with_no_wrapping)
{
    std::shared_ptr<Buffer> b = buffer1();
    std::shared_ptr<Buffer::Iterator> iterator = b->add_iterator();
    BufferView bv(b, iterator, Cwd);
    bv.resize(40, 8);
    int iter_count = 0;
    bv.update();
    for (auto it = bv.vert_iter(); it.is_valid(); it++)
    {
        EXPECT_EQ(iter_count, LineNumber(it.iterator()));
        iter_count++;
    }
    EXPECT_EQ(8, iter_count);
    EXPECT_EQ(0, bv.cursor_screen_row());
    EXPECT_EQ(0, bv.cursor_screen_column());
    EXPECT_EQ(0, bv.cursor_virtual_column());
}

TEST(BufferViewTest, iterates_through_screen_lines_with_wrapping)
{
    std::shared_ptr<Buffer> b = buffer1();
    std::shared_ptr<Buffer::Iterator> iterator = b->add_iterator();
    BufferView bv(b, iterator, Cwd);
    bv.resize(3, 10);
    int iter_count = 0;
    int expected_row_offsets[] = {0, 1, 2, 4, 5, 6, 7};
    bv.update();
    for (auto it = bv.vert_iter(); it.is_valid(); it++)
    {
        EXPECT_EQ(iter_count, LineNumber(it.iterator()));
        EXPECT_EQ(expected_row_offsets[iter_count], it.row_offset());
        iter_count++;
    }
    EXPECT_EQ(7, iter_count);
    EXPECT_EQ(0, bv.cursor_screen_row());
    EXPECT_EQ(0, bv.cursor_screen_column());
    EXPECT_EQ(0, bv.cursor_virtual_column());
}

TEST(BufferViewTest, fills_view_with_last_lines_of_buffer_when_cursor_warps_to_last_line)
{
    std::shared_ptr<Buffer> b = buffer1();
    std::shared_ptr<Buffer::Iterator> iterator = b->add_iterator();
    BufferView bv(b, iterator, Cwd);
    while (iterator->go_next_line())
    {
    }
    bv.resize(40, 3);
    int iter_count = 0;
    int expected_row_offsets[] = {0, 1, 2};
    bv.update();
    for (auto it = bv.vert_iter(); it.is_valid(); it++)
    {
        EXPECT_EQ(10 + iter_count, LineNumber(it.iterator()));
        EXPECT_EQ(expected_row_offsets[iter_count], it.row_offset());
        iter_count++;
    }
    EXPECT_EQ(3, iter_count);
    EXPECT_EQ(2, bv.cursor_screen_row());
    EXPECT_EQ(0, bv.cursor_screen_column());
    EXPECT_EQ(0, bv.cursor_virtual_column());
}

TEST(BufferViewTest, adjusts_cursor_row_as_cursor_moves)
{
    auto b = buffer1();
    auto iterator = b->add_iterator();
    BufferView bv(b, iterator, Cwd);
    bv.resize(5, 5);
    bv.update();
    EXPECT_EQ(0, bv.cursor_screen_row());
    bv.cursor_move(BufferView::CursorMovement::DOWN, 0u, false); // to 1
    bv.update();
    EXPECT_EQ(1, bv.cursor_screen_row());
    bv.cursor_move(BufferView::CursorMovement::DOWN, 0u, false); // to 2
    bv.update();
    EXPECT_EQ(2, bv.cursor_screen_row());
    bv.cursor_move(BufferView::CursorMovement::DOWN, 0u, false); // to 3
    bv.update();
    EXPECT_EQ(3, bv.cursor_screen_row());
    bv.cursor_move(BufferView::CursorMovement::DOWN, 0u, false); // to 4
    bv.update();
    EXPECT_EQ(4, bv.cursor_screen_row());
    bv.cursor_move(BufferView::CursorMovement::DOWN, 0u, false); // to 5
    bv.update();
    EXPECT_EQ(4, bv.cursor_screen_row());
    bv.cursor_move(BufferView::CursorMovement::DOWN, 0u, false); // to 6
    bv.update();
    EXPECT_EQ(4, bv.cursor_screen_row());
    bv.cursor_move(BufferView::CursorMovement::DOWN, 0u, false); // to 7
    bv.update();
    EXPECT_EQ(4, bv.cursor_screen_row());
    bv.cursor_move(BufferView::CursorMovement::UP, 0u, false); // to 6
    bv.update();
    EXPECT_EQ(2, bv.cursor_screen_row());
    bv.cursor_move(BufferView::CursorMovement::UP, 0u, false); // to 5
    bv.update();
    EXPECT_EQ(1, bv.cursor_screen_row());
    bv.cursor_move(BufferView::CursorMovement::UP, 0u, false); // to 4
    bv.update();
    EXPECT_EQ(0, bv.cursor_screen_row());
    bv.cursor_move(BufferView::CursorMovement::UP, 0u, false); // to 3
    bv.update();
    EXPECT_EQ(0, bv.cursor_screen_row());
    bv.cursor_move(BufferView::CursorMovement::UP, 0u, false); // to 2
    bv.update();
    EXPECT_EQ(0, bv.cursor_screen_row());
    bv.cursor_move(BufferView::CursorMovement::UP, 0u, false); // to 1
    bv.update();
    EXPECT_EQ(0, bv.cursor_screen_row());
    bv.cursor_move(BufferView::CursorMovement::UP, 0u, false); // to 0
    bv.update();
    EXPECT_EQ(0, bv.cursor_screen_row());
}

TEST(BufferViewTest, adjusts_cursor_row_as_cursor_moves_with_scroll_offset_1)
{
    auto b = buffer1();
    auto iterator = b->add_iterator();
    BufferView bv(b, iterator, Cwd);
    bv.resize(5, 5);
    bv.set_scroll_offset(1);
    bv.update();
    EXPECT_EQ(0, bv.cursor_screen_row());
    bv.cursor_move(BufferView::CursorMovement::DOWN, 0u, false); // to 1
    bv.update();
    EXPECT_EQ(1, bv.cursor_screen_row());
    bv.cursor_move(BufferView::CursorMovement::DOWN, 0u, false); // to 2
    bv.update();
    EXPECT_EQ(2, bv.cursor_screen_row());
    bv.cursor_move(BufferView::CursorMovement::DOWN, 0u, false); // to 3
    bv.update();
    EXPECT_EQ(3, bv.cursor_screen_row());
    bv.cursor_move(BufferView::CursorMovement::DOWN, 0u, false); // to 4
    bv.update();
    EXPECT_EQ(3, bv.cursor_screen_row());
    bv.cursor_move(BufferView::CursorMovement::DOWN, 0u, false); // to 5
    bv.update();
    EXPECT_EQ(3, bv.cursor_screen_row());
    bv.cursor_move(BufferView::CursorMovement::DOWN, 0u, false); // to 6
    bv.update();
    EXPECT_EQ(3, bv.cursor_screen_row());
    bv.cursor_move(BufferView::CursorMovement::DOWN, 0u, false); // to 7
    bv.update();
    EXPECT_EQ(3, bv.cursor_screen_row());
    bv.cursor_move(BufferView::CursorMovement::UP, 0u, false); // to 6
    bv.update();
    EXPECT_EQ(1, bv.cursor_screen_row());
    bv.cursor_move(BufferView::CursorMovement::UP, 0u, false); // to 5
    bv.update();
    EXPECT_EQ(1, bv.cursor_screen_row());
    bv.cursor_move(BufferView::CursorMovement::UP, 0u, false); // to 4
    bv.update();
    EXPECT_EQ(1, bv.cursor_screen_row());
    bv.cursor_move(BufferView::CursorMovement::UP, 0u, false); // to 3
    bv.update();
    EXPECT_EQ(1, bv.cursor_screen_row());
    bv.cursor_move(BufferView::CursorMovement::UP, 0u, false); // to 2
    bv.update();
    EXPECT_EQ(1, bv.cursor_screen_row());
    bv.cursor_move(BufferView::CursorMovement::UP, 0u, false); // to 1
    bv.update();
    EXPECT_EQ(1, bv.cursor_screen_row());
    bv.cursor_move(BufferView::CursorMovement::UP, 0u, false); // to 0
    bv.update();
    EXPECT_EQ(0, bv.cursor_screen_row());
}

TEST(BufferViewTest, moves_view_down_while_traversing_a_wrapped_line_right)
{
    auto b = buffer1();
    auto iterator = b->add_iterator();
    BufferView bv(b, iterator, Cwd);
    bv.resize(4, 10);
    for (int i = 0; i < 9; i++)
    {
        EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::DOWN, 0u, false));
        bv.update();
    }
    EXPECT_EQ(9, bv.cursor_screen_row());
    EXPECT_EQ(0, bv.cursor_screen_column());
    EXPECT_EQ(0, bv.cursor_virtual_column());
    EXPECT_EQ(C('9'), **iterator);
    for (int i = 0; i < 3; i++)
    {
        EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::RIGHT, 0u, false));
        bv.update();
        EXPECT_EQ(9, bv.cursor_screen_row());
        EXPECT_EQ(1 + i, bv.cursor_screen_column());
        EXPECT_EQ(1 + i, bv.cursor_virtual_column());
    }
    for (int i = 0; i < 4; i++)
    {
        EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::RIGHT, 0u, false));
        bv.update();
        EXPECT_EQ(9, bv.cursor_screen_row());
        EXPECT_EQ(i, bv.cursor_screen_column());
        EXPECT_EQ(4 + i, bv.cursor_virtual_column());
    }
    for (int i = 0; i < 4; i++)
    {
        EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::RIGHT, 0u, false));
        bv.update();
        EXPECT_EQ(9, bv.cursor_screen_row());
        EXPECT_EQ(i, bv.cursor_screen_column());
        EXPECT_EQ(8 + i, bv.cursor_virtual_column());
    }
    EXPECT_EQ(C('k'), **iterator);
}

TEST(BufferViewTest, moves_view_down_while_jumping_to_the_end_of_a_wrapped_line)
{
    auto b = buffer1();
    auto iterator = b->add_iterator();
    BufferView bv(b, iterator, Cwd);
    bv.resize(4, 10);
    for (int i = 0; i < 9; i++)
    {
        bv.cursor_move(BufferView::CursorMovement::DOWN, 0u, false);
        bv.update();
    }
    EXPECT_EQ(9, bv.cursor_screen_row());
    EXPECT_EQ(0, bv.cursor_screen_column());
    EXPECT_EQ(0, bv.cursor_virtual_column());
    EXPECT_EQ(C('9'), **iterator);
    EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::EOL, 0u, false));
    bv.update();
    EXPECT_EQ(9, bv.cursor_screen_row());
    EXPECT_EQ(3, bv.cursor_screen_column());
    EXPECT_EQ(15, bv.cursor_virtual_column());
    EXPECT_EQ(C('o'), **iterator);
}

TEST(BufferViewTest, moves_view_up_while_traversing_a_wrapped_line_left)
{
    auto b = buffer1();
    auto iterator = b->add_iterator();
    BufferView bv(b, iterator, Cwd);
    bv.resize(2, 3);
    for (int i = 0; i < 4; i++)
    {
        EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::DOWN, 0u, false));
        bv.update();
    }
    EXPECT_EQ(2, bv.cursor_screen_row());
    EXPECT_EQ(0, bv.cursor_screen_column());
    EXPECT_EQ(0, bv.cursor_virtual_column());
    for (int i = 0; i < 4; i++)
    {
        iterator->go_back();
    }
    /* Cursor at end of line 2 */
    bv.set_cursor_screen_row(0);
    bv.update();
    EXPECT_EQ(0, bv.cursor_screen_row());
    EXPECT_EQ(1, bv.cursor_screen_column());
    EXPECT_EQ(3, bv.cursor_virtual_column());
    EXPECT_EQ(C('c'), **iterator);
    EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::LEFT, 0u, false));
    bv.update();
    EXPECT_EQ(0, bv.cursor_screen_row());
    EXPECT_EQ(0, bv.cursor_screen_column());
    EXPECT_EQ(2, bv.cursor_virtual_column());
    EXPECT_EQ(C('b'), **iterator);
    EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::LEFT, 0u, false));
    bv.update();
    EXPECT_EQ(0, bv.cursor_screen_row());
    EXPECT_EQ(1, bv.cursor_screen_column());
    EXPECT_EQ(1, bv.cursor_virtual_column());
    EXPECT_EQ(C('a'), **iterator);
    EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::LEFT, 0u, false));
    bv.update();
    EXPECT_EQ(0, bv.cursor_screen_row());
    EXPECT_EQ(0, bv.cursor_screen_column());
    EXPECT_EQ(0, bv.cursor_virtual_column());
    EXPECT_EQ(C('2'), **iterator);
    EXPECT_FALSE(bv.cursor_move(BufferView::CursorMovement::LEFT, 0u, false));
    bv.update();
    EXPECT_EQ(0, bv.cursor_screen_row());
    EXPECT_EQ(0, bv.cursor_screen_column());
    EXPECT_EQ(0, bv.cursor_virtual_column());
    EXPECT_EQ(C('2'), **iterator);
}

TEST(BufferViewTest, moves_view_up_while_jumping_to_start_of_a_wrapped_line)
{
    auto b = buffer1();
    auto iterator = b->add_iterator();
    BufferView bv(b, iterator, Cwd);
    bv.resize(2, 3);
    for (int i = 0; i < 4; i++)
    {
        bv.cursor_move(BufferView::CursorMovement::DOWN, 0u, false);
        bv.update();
    }
    EXPECT_EQ(2, bv.cursor_screen_row());
    EXPECT_EQ(0, bv.cursor_screen_column());
    EXPECT_EQ(0, bv.cursor_virtual_column());
    for (int i = 0; i < 4; i++)
    {
        iterator->go_back();
    }
    /* Cursor at end of line 2 */
    bv.set_cursor_screen_row(0);
    bv.update();
    EXPECT_EQ(0, bv.cursor_screen_row());
    EXPECT_EQ(1, bv.cursor_screen_column());
    EXPECT_EQ(3, bv.cursor_virtual_column());
    EXPECT_EQ(C('c'), **iterator);
    bv.cursor_move(BufferView::CursorMovement::SOL, 0u, false);
    bv.update();
    EXPECT_EQ(0, bv.cursor_screen_row());
    EXPECT_EQ(0, bv.cursor_screen_column());
    EXPECT_EQ(0, bv.cursor_virtual_column());
    EXPECT_EQ(C('2'), **iterator);
}

TEST(BufferViewTest, warps_view_when_jumping_to_beginning_and_end_of_buffer)
{
    auto b = buffer1();
    auto iterator = b->add_iterator();
    BufferView bv(b, iterator, Cwd);
    bv.resize(4, 4);
    EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::LAST_LINE, 0u, false));
    bv.update();
    EXPECT_EQ(12, LineNumber(iterator));
    EXPECT_EQ(3, bv.cursor_screen_row());
    EXPECT_EQ(0, bv.cursor_screen_column());
    EXPECT_EQ(0, bv.cursor_virtual_column());
    EXPECT_FALSE(bv.cursor_move(BufferView::CursorMovement::LAST_LINE, 0u, false));
    EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::FIRST_LINE, 0u, false));
    bv.update();
    EXPECT_EQ(0, LineNumber(iterator));
    EXPECT_EQ(0, bv.cursor_screen_row());
    EXPECT_EQ(0, bv.cursor_screen_column());
    EXPECT_EQ(0, bv.cursor_virtual_column());
    EXPECT_FALSE(bv.cursor_move(BufferView::CursorMovement::FIRST_LINE, 0u, false));
}

TEST(BufferViewTest, sets_target_column_to_eol_when_jumping_to_SOL_and_EOL)
{
    auto b = buffer1();
    auto iterator = b->add_iterator();
    BufferView bv(b, iterator, Cwd);
    bv.resize(4, 4);
    bv.cursor_move(BufferView::CursorMovement::EOL, 0u, false);
    bv.update();
    EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::DOWN, 0u, false));
    bv.update();
    EXPECT_EQ(1, bv.cursor_screen_row());
    EXPECT_EQ(C('b'), **iterator);
    EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::UP, 0u, false));
    bv.update();
    EXPECT_EQ(0, bv.cursor_screen_row());
    bv.cursor_move(BufferView::CursorMovement::SOL, 0u, false);
    bv.update();
    EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::DOWN, 0u, false));
    EXPECT_EQ(C('1'), **iterator);
    EXPECT_EQ(1, bv.cursor_screen_row());
}

TEST(BufferViewTest, sets_target_column_when_traversing_left_and_right)
{
    auto b = buffer1();
    auto iterator = b->add_iterator();
    BufferView bv(b, iterator, Cwd);
    bv.resize(4, 4);
    EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::DOWN, 0u, false));
    bv.update();
    EXPECT_EQ(1, LineNumber(iterator));
    EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::RIGHT, 0u, false));
    bv.update();
    EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::RIGHT, 0u, false));
    bv.update();
    EXPECT_EQ(1, LineNumber(iterator));
    EXPECT_EQ(C('b'), **iterator);
    EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::DOWN, 0u, false));
    bv.update();
    EXPECT_EQ(2, LineNumber(iterator));
    EXPECT_EQ(C('b'), **iterator);
    EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::LEFT, 0u, false));
    bv.update();
    EXPECT_EQ(2, LineNumber(iterator));
    EXPECT_EQ(C('a'), **iterator);
    EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::UP, 0u, false));
    bv.update();
    EXPECT_EQ(1, LineNumber(iterator));
    EXPECT_EQ(C('a'), **iterator);
}

TEST(BufferViewTest, moves_cursor_by_screen_row)
{
    auto b = buffer1();
    auto iterator = b->add_iterator();
    BufferView bv(b, iterator, Cwd);
    bv.resize(4, 4);
    for (int i = 0; i < 6; i++)
    {
        EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::SCREEN_ROW_DOWN, 0u, false));
        bv.update();
        EXPECT_EQ(1 + i, LineNumber(iterator));
    }
    EXPECT_EQ(C('6'), **iterator);
    EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::SCREEN_ROW_DOWN, 0u, false));
    bv.update();
    EXPECT_EQ(6, LineNumber(iterator));
    EXPECT_EQ(C('d'), **iterator);
    EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::RIGHT, 0u, false));
    bv.update();
    EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::RIGHT, 0u, false));
    bv.update();
    EXPECT_EQ(C('f'), **iterator);
    EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::SCREEN_ROW_DOWN, 0u, false));
    bv.update();
    EXPECT_EQ(C('7'), **iterator);
    EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::SCREEN_ROW_UP, 0u, false));
    bv.update();
    EXPECT_EQ(C('f'), **iterator);
    EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::SCREEN_ROW_UP, 0u, false));
    bv.update();
    EXPECT_EQ(C('b'), **iterator);
    EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::SCREEN_ROW_UP, 0u, false));
    bv.update();
    EXPECT_EQ(C('5'), **iterator);
}

TEST(BufferViewTest, cursor_move_screen_row_up_and_down_returns_false_at_top_and_bottom)
{
    auto b = buffer1();
    auto iterator = b->add_iterator();
    BufferView bv(b, iterator, Cwd);
    bv.resize(4, 4);
    EXPECT_FALSE(bv.cursor_move(BufferView::CursorMovement::SCREEN_ROW_UP, 0u, false));
    bv.update();
    EXPECT_EQ(0, LineNumber(iterator));
    for (int i = 0; i < 16; i++)
    {
        EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::SCREEN_ROW_DOWN, 0u, false));
        bv.update();
    }
    EXPECT_FALSE(bv.cursor_move(BufferView::CursorMovement::SCREEN_ROW_DOWN, 0u, false));
}

TEST(BufferViewTest, scrolls_the_view_up_and_down)
{
    auto b = buffer1();
    auto iterator = b->add_iterator();
    BufferView bv(b, iterator, Cwd);
    bv.resize(40, 6);
    bv.update();
    EXPECT_EQ(0, LineNumber(iterator));
    EXPECT_EQ(0, bv.cursor_screen_row());
    bv.scroll_view_down(1, false);
    bv.update();
    EXPECT_EQ(1, LineNumber(iterator));
    EXPECT_EQ(0, bv.cursor_screen_row());
    bv.scroll_view_down(5, false);
    bv.update();
    EXPECT_EQ(6, LineNumber(iterator));
    EXPECT_EQ(0, bv.cursor_screen_row());
    bv.scroll_view_down(1, false);
    bv.update();
    EXPECT_EQ(7, LineNumber(iterator));
    EXPECT_EQ(0, bv.cursor_screen_row());
    bv.scroll_view_down(1, false);
    bv.update();
    EXPECT_EQ(7, LineNumber(iterator));
    EXPECT_EQ(0, bv.cursor_screen_row());
    bv.scroll_view_down(3, false);
    bv.update();
    EXPECT_EQ(7, LineNumber(iterator));
    EXPECT_EQ(0, bv.cursor_screen_row());

    bv.scroll_view_up(1, false);
    bv.update();
    EXPECT_EQ(7, LineNumber(iterator));
    EXPECT_EQ(1, bv.cursor_screen_row());
    bv.scroll_view_up(4, false);
    bv.update();
    EXPECT_EQ(7, LineNumber(iterator));
    EXPECT_EQ(5, bv.cursor_screen_row());
    bv.scroll_view_up(1, false);
    bv.update();
    EXPECT_EQ(6, LineNumber(iterator));
    EXPECT_EQ(5, bv.cursor_screen_row());
    bv.scroll_view_up(1, false);
    bv.update();
    EXPECT_EQ(5, LineNumber(iterator));
    EXPECT_EQ(5, bv.cursor_screen_row());
    bv.scroll_view_up(1, false);
    bv.update();
    EXPECT_EQ(5, LineNumber(iterator));
    EXPECT_EQ(5, bv.cursor_screen_row());
    bv.scroll_view_up(3, false);
    bv.update();
    EXPECT_EQ(5, LineNumber(iterator));
    EXPECT_EQ(5, bv.cursor_screen_row());
}

TEST(BufferViewTest, scrolls_the_view_up_and_down_with_wrapped_lines)
{
    auto b = buffer1();
    auto iterator = b->add_iterator();
    BufferView bv(b, iterator, Cwd);
    bv.resize(4, 2);
    bv.update();
    EXPECT_EQ(0, LineNumber(iterator));
    EXPECT_EQ(0, bv.cursor_screen_row());
    bv.scroll_view_down(6, false);
    bv.update();
    EXPECT_EQ(6, LineNumber(iterator));
    EXPECT_EQ(C('6'), **iterator);
    EXPECT_EQ(0, bv.cursor_screen_row());
    bv.scroll_view_down(1, false);
    bv.update();
    EXPECT_EQ(6, LineNumber(iterator));
    EXPECT_EQ(C('d'), **iterator);
    bv.scroll_view_down(1, false);
    bv.update();
    EXPECT_EQ(7, LineNumber(iterator));
    EXPECT_EQ(C('7'), **iterator);
    bv.scroll_view_down(4, false);
    bv.update();
    EXPECT_EQ(9, LineNumber(iterator));
    EXPECT_EQ(C('h'), **iterator);
    bv.scroll_view_down(2, false);
    bv.update();
    EXPECT_EQ(10, LineNumber(iterator));
    EXPECT_EQ(C('1'), **iterator);

    bv.cursor_move(BufferView::CursorMovement::RIGHT, 0u, false);
    bv.update();
    bv.scroll_view_up(3, false);
    bv.update();
    EXPECT_EQ(9, LineNumber(iterator));
    EXPECT_EQ(C('i'), **iterator);
    bv.scroll_view_up(5, false);
    bv.update();
    EXPECT_EQ(6, LineNumber(iterator));
    EXPECT_EQ(C('e'), **iterator);
}

TEST(BufferViewTest, scrolls_the_view_up_and_down_with_nonzero_scroll_offset)
{
    auto b = buffer1();
    auto iterator = b->add_iterator();
    BufferView bv(b, iterator, Cwd);
    bv.resize(40, 6);
    bv.set_scroll_offset(2);
    bv.update();
    EXPECT_EQ(0, LineNumber(iterator));
    EXPECT_EQ(0, bv.cursor_screen_row());
    bv.scroll_view_down(1, false);
    bv.update();
    EXPECT_EQ(3, LineNumber(iterator));
    EXPECT_EQ(2, bv.cursor_screen_row());
    bv.scroll_view_down(5, false);
    bv.update();
    EXPECT_EQ(8, LineNumber(iterator));
    EXPECT_EQ(2, bv.cursor_screen_row());
    bv.scroll_view_down(1, false);
    bv.update();
    EXPECT_EQ(9, LineNumber(iterator));
    EXPECT_EQ(2, bv.cursor_screen_row());
    bv.scroll_view_down(1, false);
    bv.update();
    EXPECT_EQ(9, LineNumber(iterator));
    EXPECT_EQ(2, bv.cursor_screen_row());
    bv.scroll_view_down(3, false);
    bv.update();
    EXPECT_EQ(9, LineNumber(iterator));
    EXPECT_EQ(2, bv.cursor_screen_row());

    bv.scroll_view_up(1, false);
    bv.update();
    EXPECT_EQ(9, LineNumber(iterator));
    EXPECT_EQ(3, bv.cursor_screen_row());
    bv.scroll_view_up(4, false);
    bv.update();
    EXPECT_EQ(5, LineNumber(iterator));
    EXPECT_EQ(3, bv.cursor_screen_row());
    bv.scroll_view_up(1, false);
    bv.update();
    EXPECT_EQ(4, LineNumber(iterator));
    EXPECT_EQ(3, bv.cursor_screen_row());
    bv.scroll_view_up(1, false);
    bv.update();
    EXPECT_EQ(3, LineNumber(iterator));
    EXPECT_EQ(3, bv.cursor_screen_row());
    bv.scroll_view_up(1, false);
    bv.update();
    EXPECT_EQ(3, LineNumber(iterator));
    EXPECT_EQ(3, bv.cursor_screen_row());
    bv.scroll_view_up(3, false);
    bv.update();
    EXPECT_EQ(3, LineNumber(iterator));
    EXPECT_EQ(3, bv.cursor_screen_row());
}

TEST(BufferViewTest, scrolls_window_through_a_very_long_single_line_file)
{
    auto b = std::make_shared<Buffer>();
    auto iterator = b->add_cursor();
    for (int row = 0; row < 120; row++)
    {
        for (int col = 0; col < 10; col++)
        {
            b->insert_code_point(*iterator, (uint32_t)(row + 1000));
        }
    }
    iterator->go_start_of_line();
    BufferView bv(b, iterator, Cwd);
    bv.resize(10, 10);
    bv.update();
    EXPECT_EQ(0, bv.cursor_screen_row());
    EXPECT_EQ(1000u, **iterator);
    for (uint32_t i = 0u; i < 100u; i++)
    {
        bv.scroll_view_down(1, false);
        bv.update();
        EXPECT_EQ(0, bv.cursor_screen_row());
        EXPECT_EQ(i + 1001u, **iterator);
    }
    bv.scroll_view_down(20, false);
    bv.update();
    EXPECT_EQ(0, bv.cursor_screen_row());
    EXPECT_EQ(1110u, **iterator);

    for (int i = 0; i < 9; i++)
    {
        bv.scroll_view_up(1, false);
        bv.update();
        EXPECT_EQ(i + 1, bv.cursor_screen_row());
        EXPECT_EQ(1110u, **iterator);
    }
    for (uint32_t i = 0; i < 50; i++)
    {
        bv.scroll_view_up(1, false);
        bv.update();
        EXPECT_EQ(9, bv.cursor_screen_row());
        EXPECT_EQ(1109u - i, **iterator);
    }
    bv.scroll_view_up(1000u, false);
    bv.update();
    EXPECT_EQ(9, bv.cursor_screen_row());
    EXPECT_EQ(1009u, **iterator);
}

TEST(BufferViewTest, shows_only_lines_in_buffer_if_fewer_lines_in_buffer_than_view)
{
    auto b = buffer1();
    auto iterator = b->add_iterator();
    BufferView bv(b, iterator, Cwd);
    bv.resize(50, 50);
    bv.update();
    int count = 0;
    for (auto it = bv.vert_iter(); it.is_valid(); it++)
    {
        EXPECT_EQ(count, it.row_offset());
        count++;
    }
    EXPECT_EQ(13, count);
}

TEST(BufferViewTest, scrolls_past_empty_lines)
{
    auto b = buffer2();
    auto iterator = b->add_cursor();
    BufferView bv(b, iterator, Cwd);
    bv.resize(10, 1);
    bv.update();
    EXPECT_EQ(0, LineNumber(iterator));
    bv.scroll_view_down(3, false);
    bv.update();
    EXPECT_EQ(3, LineNumber(iterator));
    bv.scroll_view_up(3, false);
    bv.update();
    EXPECT_EQ(0, LineNumber(iterator));
}

TEST(BufferViewTest, moving_cursor_screen_row_down_jumps_past_wrapped_tab)
{
    static const char data[] =
        "0\n"
        "1abc\tdefghijklmnopqrstuvwxyz\n"
        "2\n"
        "3\n";
    auto b = std::make_shared<Buffer>((const uint8_t *)data, strlen(data));
    auto iterator = b->add_cursor();
    BufferView bv(b, iterator, Cwd);
    bv.resize(5, 10);
    bv.update();
    EXPECT_EQ(C('0'), **iterator);
    EXPECT_EQ(0, bv.cursor_screen_row());
    bv.cursor_move(BufferView::CursorMovement::SCREEN_ROW_DOWN, 0u, false);
    bv.update();
    EXPECT_EQ(C('1'), **iterator);
    EXPECT_EQ(1, bv.cursor_screen_row());
    bv.cursor_move(BufferView::CursorMovement::SCREEN_ROW_DOWN, 0u, false);
    bv.update();
    EXPECT_EQ(C('d'), **iterator);
    EXPECT_EQ(2, bv.cursor_screen_row());
    bv.cursor_move(BufferView::CursorMovement::SCREEN_ROW_DOWN, 0u, false);
    bv.update();
    EXPECT_EQ(C('f'), **iterator);
    EXPECT_EQ(3, bv.cursor_screen_row());
}
