#include "Buffer.h"

bool Buffer::Iterator::is_start_of_line()
{
    if (valid())
    {
        if (m_offset == 0u)
        {
            return true;
        }
        const uint8_t * a = m_buffer->address(m_offset - 1u);
        a = Encoding::beginning_of_code_point(m_buffer->encoding(), a);
        if (Encoding::decode(m_buffer->encoding(), a) == '\n')
        {
            return true;
        }
    }
    return false;
}

bool Buffer::Iterator::is_end_of_line(bool eol_only)
{
    if (valid())
    {
        if (**this == '\n')
        {
            return true;
        }
        if (!eol_only)
        {
            return false;
        }
        Iterator i2 = *this;
        if (!i2.go_forward())
        {
            return false;
        }
        if (*i2 == '\n')
        {
            return true;
        }
    }
    return false;
}

bool Buffer::Iterator::go_back()
{
    if (valid() || (m_offset == m_buffer->size()))
    {
        if (m_offset == 0u)
        {
            m_offset = (size_t)-1;
        }
        else
        {
            const uint8_t * a = m_buffer->address(m_offset - 1u);
            const uint8_t * beginning_of_code_point = Encoding::beginning_of_code_point(m_buffer->encoding(), a);
            m_offset -= ((a - beginning_of_code_point) + 1u);
            if (Encoding::decode(m_buffer->encoding(), address()) == '\n')
            {
                m_line--;
            }
        }
        return true;
    }
    return false;
}

bool Buffer::Iterator::go_forward()
{
    if (valid())
    {
        uint8_t size;
        uint32_t c = Encoding::decode(m_buffer->encoding(), address(), &size);
        m_offset += size;
        if (c == '\n')
        {
            m_line++;
        }
        return true;
    }
    else if (m_offset == (size_t)-1)
    {
        m_offset = 0u;
        return true;
    }
    return false;
}

bool Buffer::Iterator::go_start_of_line()
{
    bool moved = false;
    Iterator i2 = *this;
    i2.go_back();
    while (i2.valid() && (*i2 != '\n'))
    {
        m_offset = i2.m_offset;
        moved = true;
        i2.go_back();
    }
    return moved;
}

bool Buffer::Iterator::go_end_of_line(bool allow_eol)
{
    bool moved = false;
    if (go_right_in_line(allow_eol))
    {
        moved = true;
    }
    while (go_right_in_line(allow_eol))
    {
        ;
    }
    return moved;
}

bool Buffer::Iterator::go_left_in_line()
{
    bool moved = false;
    Iterator i2 = *this;
    i2.go_back();
    if (i2.valid() && (*i2 != '\n'))
    {
        m_offset = i2.m_offset;
        moved = true;
    }
    return moved;
}

bool Buffer::Iterator::go_right_in_line(bool allow_eol)
{
    if (valid() && (**this != '\n'))
    {
        if (allow_eol)
        {
            return go_forward();
        }
        else
        {
            Iterator i2 = *this;
            i2.go_forward();
            if (i2.valid() && (*i2 != '\n'))
            {
                m_offset = i2.m_offset;
                return true;
            }
        }
    }
    return false;
}

bool Buffer::Iterator::go_previous_line()
{
    Iterator i2 = *this;
    while (i2.valid())
    {
        i2.go_back();
        if (*i2 == '\n')
        {
            i2.go_start_of_line();
            m_line--;
            m_offset = i2.m_offset;
            return true;
        }
    }
    return false;
}

bool Buffer::Iterator::go_next_line()
{
    Iterator i2 = *this;
    i2.go_end_of_line(true);
    i2.go_forward();
    if (i2.valid())
    {
        m_line++;
        m_offset = i2.m_offset;
        return true;
    }
    return false;
}

void Buffer::Iterator::warp_to_offset(size_t offset)
{
    Iterator i = m_buffer->begin();
    m_offset = i.m_offset;
    m_line = i.m_line;
    for (;;)
    {
        i.go_forward();
        if (i.m_offset > offset)
        {
            break;
        }
        m_offset = i.m_offset;
        m_line = i.m_line;
        if (!i.valid())
        {
            break;
        }
    }
}

Buffer::Iterator Buffer::Iterator::prev()
{
    Iterator i = *this;
    i.go_back();
    return i;
}

Buffer::Iterator Buffer::Iterator::next()
{
    Iterator i = *this;
    i.go_forward();
    return i;
}
