#ifndef GAPBUFFER_H
#define GAPBUFFER_H

#include <stdint.h>
#include <stdlib.h>
#include "Encoding.h"
#include <memory>
#include <list>

class GapBuffer
{
public:
    class Iterator
    {
    public:
        Iterator(GapBuffer * gap_buffer)
        {
            m_gap_buffer = gap_buffer;
            m_offset = 0u;
        }
        bool valid() const
        {
            return m_offset < m_gap_buffer->size();
        }
        void go_forward();
        bool check_go_forward();
        void go_back();
        bool check_go_back();
        uint8_t * address() const
        {
            return m_gap_buffer->address(m_offset);
        }
        uint32_t operator*() const
        {
            if (valid())
            {
                return Encoding::decode(m_gap_buffer->m_encoding, address());
            }
            else
            {
                return 0xFFFFFFFFu;
            }
        }
        GapBuffer * gap_buffer() const { return m_gap_buffer; }
        bool operator<(const Iterator & other) const
        {
            return m_offset < other.m_offset;
        }

    protected:
        GapBuffer * m_gap_buffer;
        size_t m_offset;
    };

    class Cursor
    {
    public:
        Cursor(GapBuffer * gap_buffer)
            : m_iterator(gap_buffer)
        {
            m_line = 0u;
            init_column();
        }
        bool is_start_of_line();
        bool is_end_of_line(bool allow_eol);
        bool go_start_of_line();
        bool go_end_of_line(bool allow_eol);
        bool go_left();
        bool go_right(bool allow_eol);
        bool go_up(size_t target_column);
        bool go_down(size_t target_column);
        bool operator<(const Cursor & other) const
        {
            return m_iterator < other.m_iterator;
        }
        uint32_t operator*() const { return *m_iterator; }
        uint8_t * address() const { return m_iterator.address(); }
        bool valid() const { return m_iterator.valid(); }
        size_t line() const { return m_line; }
        size_t column() const { return m_column; }

    protected:
        Iterator m_iterator;
        size_t m_line;
        size_t m_column;

        void init_column();
        void calculate_column();
        void forward_to_column(size_t target_column);
    };

    uint8_t tabstop;

    GapBuffer(Encoding::Type encoding);
    GapBuffer(uint8_t * buffer, size_t buffer_size, size_t size, Encoding::Type encoding);
    ~GapBuffer();
    size_t buffer_size() const { return m_buffer_size; }
    size_t size() const { return m_size; }
    std::shared_ptr<Cursor> add_cursor();
    uint8_t * address(size_t offset) const
    {
        if (offset < m_gap_position)
        {
            return &m_buffer[offset];
        }
        else
        {
            return &m_buffer[offset + gap_size()];
        }
    }
    size_t gap_size() const { return m_buffer_size - m_size; }
    void compact();

protected:
    uint8_t * m_buffer;
    size_t m_buffer_size;
    size_t m_size;
    size_t m_gap_position;
    Encoding::Type m_encoding;
    std::list<std::shared_ptr<Cursor>> m_cursors;
};

#endif
