#ifndef BUFFERPANE_H
#define BUFFERPANE_H

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

class BufferPane : public Pane
{
public:
    class Cwd : public CharacterWidthDeterminer
    {
    public:
        Cwd(Window * window)
        {
            m_window = window;
        }
        int operator()(uint32_t character);

    protected:
        Window * m_window;
    };

    typedef BufferView::CursorMovement CursorMovement;

    BufferPane(Window * window, std::shared_ptr<Buffer> buffer);
    void resize(int width, int height) override;
    void draw();
    void cursor_move(CursorMovement which, uint32_t c);
    void enter_insert_mode(Window::EnterInsertModeMode which);
    void insert_code_point(uint32_t code_point);
    void exit_insert_mode();
    void kill_character_forward();
    void kill_character_backward();
    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();
    void set_show_status_bar(bool show_status_bar)
    {
        m_show_status_bar = show_status_bar;
    }
    void set_show_line_number_gutter(bool show_line_number_gutter)
    {
        m_show_line_number_gutter = show_line_number_gutter;
    }
    void set_command_mode();
    void set_focused(bool focused)
    {
        m_focused = focused;
    }
    void clear();
    std::shared_ptr<Buffer> buffer() const { return m_buffer; }

protected:
    void draw_buffer_line(int screen_row, std::shared_ptr<Buffer::Iterator> start_of_line);
    void draw_buffer_character(uint32_t character, int screen_row, int screen_column);
    void draw_cursor(int x, int y, int i, int columns);
    void draw_crosshair(int x, int y, int width = -1, int height = -1);
    void draw_line_number(int screen_row, size_t line_number);
    int col_x(int col)
    {
        return (m_line_number_gutter_width + col) * m_window->font()->get_advance() + (m_show_line_number_gutter ? 3 : 0);
    }
    int row_y(int row)
    {
        return m_height - (row + 1) * m_window->font()->get_line_height();
    }
    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);
    void resize_buffer_view();

    Window * m_window;
    std::shared_ptr<Buffer> m_buffer;
    Cwd m_cwd;
    std::shared_ptr<BufferView> m_buffer_view;
    int m_rows;
    int m_columns;
    int m_line_number_gutter_width;
    std::shared_ptr<Buffer::Iterator> m_iterator;
    std::list<std::pair<int, Buffer::Iterator>> m_screen_lines;
    bool m_show_status_bar;
    bool m_show_line_number_gutter;
    bool m_command_mode;
    bool m_focused;
};

#endif
