#include "BufferLineWalker.h"

/**
 * Construct a BufferLineWalker.
 *
 * @param buffer The Buffer we're iterating.
 * @param start_of_line Buffer iterator pointing to the beginning of the buffer line.
 * @param view_width View width, beyond which characters will wrap to the next line.
 * @param character_width_determiner Functor to return the width (in columns) of a character.
 */
BufferLineWalker::BufferLineWalker(
        std::shared_ptr<Buffer> buffer,
        std::shared_ptr<Buffer::Iterator> start_of_line,
        int view_width,
        CharacterWidthDeterminer & character_width_determiner)
    : m_buffer(buffer),
      m_character_width_determiner(character_width_determiner)
{
    m_view_width = view_width;
    m_row_offset = 0;
    m_screen_column = 0;
    m_virtual_column = 0;
    m_iterator = std::make_shared<Buffer::Iterator>(*start_of_line);
    process_new_character();
}

void BufferLineWalker::process_new_character()
{
    if ((!m_iterator) || (!m_iterator->valid()))
    {
        m_code_point = '\n';
        m_character_width = 0;
        return;
    }

    m_code_point = **m_iterator;

    if (m_code_point == '\n')
    {
        m_character_width = 0;
        return;
    }

    if (m_code_point == '\t')
    {
        uint8_t tabstop = m_buffer->tabstop();
        m_character_width = tabstop - m_virtual_column % tabstop;
        return;
    }

    m_character_width = m_character_width_determiner(m_code_point);

    if (((m_screen_column + m_character_width) > m_view_width) &&
            (m_screen_column > 0))
    {
        m_row_offset++;
        m_screen_column = 0;
    }
}

void BufferLineWalker::operator++(int unused)
{
    if (!m_iterator)
        return;

    if ((m_code_point == '\n') || (!m_iterator->valid()))
    {
        m_code_point = '\n';
        m_iterator.reset();
        return;
    }

    /* Adjust virtual column. */
    m_virtual_column += m_character_width;

    /* Adjust screen position. */
    if (m_code_point == '\t')
    {
        m_screen_column += m_character_width;
        while (m_screen_column >= m_view_width)
        {
            m_screen_column -= m_view_width;
            m_row_offset++;
        }
    }
    else
    {
        if ((m_screen_column + m_character_width) >= m_view_width)
        {
            m_row_offset++;
            m_screen_column = 0;
        }
        else
        {
            m_screen_column += m_character_width;
        }
    }

    m_iterator->go_forward();
    process_new_character();
}
