/*
 * Copyright (c) 2024, Sönke Holz <soenke.holz@serenityos.org>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#include <Kernel/EFIPrekernel/GOP.h>
#include <Kernel/EFIPrekernel/Globals.h>

namespace Kernel {

void init_gop_and_populate_framebuffer_boot_info(BootInfo& boot_info)
{
    auto gop_guid = EFI::GraphicsOutputProtocol::guid;
    EFI::GraphicsOutputProtocol* gop;
    if (g_efi_system_table->boot_services->locate_protocol(&gop_guid, nullptr, reinterpret_cast<void**>(&gop)) != EFI::Status::Success) {
        dbgln("GOP not available");
        return;
    }

    // Choose the mode with the highest pixel count.
    EFI::GraphicsOutputModeInformation chosen_mode_info {};
    ssize_t chosen_mode_number = -1;

    // NOTE: MaxMode is the number of supported modes, not the highest mode number.
    for (u32 mode_number = 0; mode_number < gop->mode->max_mode; mode_number++) {
        FlatPtr size_of_mode_info;
        EFI::GraphicsOutputModeInformation* mode_info;

        if (auto status = gop->query_mode(gop, mode_number, &size_of_mode_info, &mode_info); status != EFI::Status::Success) {
            dbgln("Failed to query GOP mode {}: {}", mode_number, status);
            continue;
        }

        if (mode_info->pixel_format == EFI::GraphicsPixelFormat::BlueGreenRedReserved8BitPerColor
            && mode_info->vertical_resolution * mode_info->horizontal_resolution > chosen_mode_info.vertical_resolution * chosen_mode_info.horizontal_resolution) {
            chosen_mode_info = *mode_info;
            chosen_mode_number = mode_number;
        }
    }

    if (chosen_mode_number == -1) {
        dbgln("No usable GOP mode found");
        return;
    }

    if (auto status = gop->set_mode(gop, chosen_mode_number); status != EFI::Status::Success) {
        dbgln("Failed to set GOP mode {}: {}", chosen_mode_number, status);
        return;
    }

    dbgln("Chosen GOP mode: {}x{} (mode {})", chosen_mode_info.horizontal_resolution, chosen_mode_info.vertical_resolution, chosen_mode_number);

    boot_info.boot_framebuffer = {
        .paddr = PhysicalAddress { gop->mode->frame_buffer_base },
        .pitch = gop->mode->info->pixels_per_scan_line * sizeof(u32),
        .width = gop->mode->info->horizontal_resolution,
        .height = gop->mode->info->vertical_resolution,
        .bpp = 32,
        .type = BootFramebufferType::BGRx8888,
    };
}

}
