#ifndef BUFFERPANE_H
#define BUFFERPANE_H

#include "Buffer.h"
#include "Pane.h"
#include "Font.h"
#include "GL.h"
#include <memory>
#include "Window.h"

class BufferPane : public Pane
{
public:
    BufferPane(Window * window, std::shared_ptr<Buffer> buffer);
    void resize(int width, int height) override;
    void draw();
    void cursor_move(Window::CursorMovement which);
    void enter_insert_mode(Window::EnterInsertModeMode which);
    void insert_code_point(uint32_t code_point);
    void exit_insert_mode();
    void kill_character_at_cursor();
    void write_file();
    bool insert_mode() const { return m_buffer->insert_mode(); }
    void scroll_window_up(Window::ScrollMode scroll_mode);
    void scroll_window_down(Window::ScrollMode scroll_mode);
    void undo();
    void redo();

protected:
    int effective_scroll_offset()
    {
        return std::min(m_scroll_offset, (m_rows - 1) / 2);
    }
    int draw_buffer_line(int screen_row, const Buffer::Iterator & start_of_line);
    void draw_character(uint32_t character, int screen_row, int screen_column);
    int character_width(uint32_t character);
    void draw_buffer_character(int screen_column, int screen_row, uint32_t character);
    void draw_cursor(int x, int y);
    int calculate_rows_in_line(const Buffer::Iterator & start_of_line);
    int calculate_rows_in_cursor_line(const Buffer::Iterator & start_of_line);
    int calculate_rows_in_line_with_iterator_offset(const Buffer::Iterator & start_of_line, const Buffer::Iterator & reference, int * iterator_row_offset);
    int screen_rows_below_line(const Buffer::Iterator & line);
    int screen_rows_above_line(const Buffer::Iterator & line, std::list<std::pair<int, Buffer::Iterator>> & backward_lines);
    void update_cursor_row(std::list<std::pair<int, Buffer::Iterator>> & backward_lines);
    bool move_cursor_screen_row_up();
    bool move_cursor_screen_row_down();
    void walk_line(const Buffer::Iterator & start_of_line, std::function<void(int, int, int, int, const Buffer::Iterator &)> callback);
    int determine_new_cursor_screen_row();
    int col_x(int col)
    {
        return col * m_window->font()->get_advance();
    }
    int row_y(int row)
    {
        return m_height - (row + 1) * m_window->font()->get_line_height();
    }
    void forward_to_column(int column, bool allow_eol);
    size_t display_line() const { return m_iterator->line() + 1u; }
    size_t display_column() const;
    void draw_status_bar();
    int calculate_lines_to_scroll(Window::ScrollMode scroll_mode);

    Window * m_window;
    std::shared_ptr<Buffer> m_buffer;
    int m_rows;
    int m_columns;
    int m_scroll_offset;
    int m_cursor_screen_row;
    int m_cursor_screen_column;
    int m_cursor_virtual_column;
    int m_cursor_row_offset;
    std::shared_ptr<Buffer::Iterator> m_iterator;
    std::list<std::pair<int, Buffer::Iterator>> m_screen_lines;
    int m_target_column;
};

#endif
