#include <luna/String.h>
#include <os/File.h>
#include <ui/Font.h>

SharedPtr<ui::Font> s_default_font = {};
SharedPtr<ui::Font> s_default_bold_font = {};

constexpr static int BYTES_PER_PIXEL = (int)sizeof(ui::Color);

namespace ui
{
    Result<SharedPtr<Font>> Font::load(const os::Path& path)
    {
        auto font = TRY(make_shared<Font>());

        auto file = TRY(os::File::open(path, os::File::ReadOnly));

        TRY(file->read_typed(font->m_psf_header));

        if (font->m_psf_header.magic != PSF_FONT_MAGIC)
        {
            os::eprintln("ui::Font::load(%s) failed: font magic does not match PSF2 magic", path.name().chars());
            return err(ENOTSUP);
        }

        if (font->m_psf_header.version != 0)
        {
            os::eprintln("ui::Font::load(%s) failed: font version is unsupported", path.name().chars());
            return err(ENOTSUP);
        }

        if (font->m_psf_header.flags)
        {
            os::eprintln("ui::Font::load(%s) warning: font has a unicode table, which we're ignoring",
                         path.name().chars());
            // todo(); // Font has a unicode table, oh no!
        }

        font->m_font_data = TRY(file->read_all()); // Read the rest of the file into the font data buffer.

        return font;
    }

    Result<SharedPtr<Font>> Font::load_builtin(StringView name, FontWeight weight)
    {
        auto path = TRY(String::format("/usr/share/fonts/%s-%s.psf"_sv, name.chars(),
                                       weight == FontWeight::Bold ? "Bold" : "Regular"));

        return load(path.view());
    }

    SharedPtr<Font> Font::default_font()
    {
        if (s_default_font) return s_default_font;
        s_default_font = load("/usr/share/fonts/Tamsyn-Regular.psf").release_value();
        return s_default_font;
    }

    SharedPtr<Font> Font::default_bold_font()
    {
        if (s_default_bold_font) return s_default_bold_font;
        s_default_bold_font = load("/usr/share/fonts/Tamsyn-Bold.psf").release_value();
        return s_default_bold_font;
    }

    void Font::render(wchar_t codepoint, ui::Color color, ui::Canvas& canvas)
    {
        const wchar_t str[] = { codepoint, 0 };
        render(str, color, canvas);
    }

    void Font::render(const wchar_t* text, ui::Color color, ui::Canvas& canvas)
    {
        usize len = wcslen(text);

        int height = m_psf_header.height;
        int width = m_psf_header.width;
        int last_char_width = width;

        if (canvas.width < (m_psf_header.width * static_cast<int>(len)))
        {
            len = (canvas.width / width) + 1;
            last_char_width = canvas.width % width;
        }

        if (canvas.height < height) height = canvas.height;

        const int bytes_per_line = (m_psf_header.width + 7) / 8;

        for (usize i = 0; i < len; i++)
        {
            if (i + 1 == len) width = last_char_width;
            wchar_t codepoint = text[i];

            u8* glyph =
                m_font_data.data() + (codepoint > 0 && codepoint < (wchar_t)m_psf_header.numglyph ? codepoint : 0) *
                                         m_psf_header.bytesperglyph;

            u32 offset = (u32)i * m_psf_header.width * BYTES_PER_PIXEL;
            for (int y = 0; y < height; y++)
            {
                u32 line = offset;
                int mask = 1 << (m_psf_header.width - 1);
                for (int x = 0; x < width; x++)
                {
                    if (*((u32*)glyph) & mask) *(u32*)(canvas.ptr + line) = color.raw;
                    mask >>= 1;
                    line += BYTES_PER_PIXEL;
                }
                glyph += bytes_per_line;
                offset += canvas.stride * BYTES_PER_PIXEL;
            }
        }
    }
}