#include "Buffer.h"
#include <stdio.h>
#include "System.h"
#include "File.h"
#include "TextLoader.h"
#include <cassert>

Buffer::Buffer(const char * filename)
{
    if (filename != nullptr)
    {
        if (!load_from_file(filename))
        {
            load_empty_buffer();
        }
    }
    else
    {
        load_empty_buffer();
    }
}

void Buffer::load_empty_buffer()
{
    m_gap_buffer = std::make_shared<GapBuffer>(Encoding::UTF_8);
    m_eol_at_eof = true;
    m_line_endings = LineEndings::LF;
}

bool Buffer::load_from_file(const char * filename)
{
    File file;
    if (!file.open(filename))
    {
        return false;
    }

    size_t file_size = file.get_size();
    if (file_size < 0)
    {
        return false;
    }

    size_t buffer_size = ((unsigned long)file_size + 2u * System::page_size) & System::page_base_mask;
    uint8_t * buffer = (uint8_t *)System::alloc_pages(buffer_size >> System::page_size_log);
    if (buffer == NULL)
    {
        return false;
    }

    if (!file.read(buffer, file_size))
    {
        System::free_pages(buffer, buffer_size >> System::page_size_log);
        return false;
    }

    TextLoader text_loader;
    size_t loaded_size;
    text_loader.load_buffer(buffer, file_size, &loaded_size);

    m_gap_buffer = std::make_shared<GapBuffer>(buffer, buffer_size, loaded_size, text_loader.get_encoding());
    m_eol_at_eof = text_loader.get_eol_at_eof();
    m_line_endings = text_loader.get_line_endings();

    return true;
}

bool Buffer::write_to_file(const char * filename)
{
    File file;
    if (!file.open(filename, true))
    {
        return false;
    }

    m_gap_buffer->compact();

    GapBuffer::Cursor start_of_line(&*m_gap_buffer);
    size_t bytes_written = 0u;

    while (start_of_line.valid())
    {
        GapBuffer::Cursor cursor = start_of_line;
        cursor.go_end_of_line(true);
        size_t len = cursor.address() - start_of_line.address();
        if (len > 0u)
        {
            if (!file.write(start_of_line.address(), len))
            {
                return false;
            }
            bytes_written += len;
        }
        bool moved_down = cursor.go_down(0u);
        if (moved_down || m_eol_at_eof)
        {
            if (!file.write(LineEndings::spans[m_line_endings]))
            {
                return false;
            }
            bytes_written += LineEndings::spans[m_line_endings].length;
        }
        if (!moved_down)
        {
            break;
        }
        start_of_line = cursor;
    }

    return true;
}
