#include "uefi.h"
#include "arch.h"
#include "fb.h"
#include "kernel.h"
#include "util.h"
#include "dbg.h"

/* GUID графического протокола GOP */
static const struct EFI_GUID gop_guid =
    {0x9042A9DE, 0x23DC, 0x4A38, {0x96,0xFB,0x7A,0xDE,0xD0,0x80,0x51,0x6A}};

/* GUID таблиц ACPI */
static const struct EFI_GUID acpi2_guid =
    {0x8868E871, 0xE4F1, 0x11D3, {0xBC,0x22,0x00,0x80,0xC7,0x3C,0x88,0x81}};

static const struct EFI_GUID acpi1_guid =
    {0xEB9D2D30, 0x2D88, 0x11D3, {0x9A,0x16,0x00,0x90,0x27,0x3F,0xC1,0x4D}};

struct acpi_rsdp {
    char     signature[8];
    uint8_t  checksum;
    char     oem_id[6];
    uint8_t  revision;
    uint32_t rsdt_address;
    uint32_t length;
    uint64_t xsdt_address;
    uint8_t  extended_checksum;
    uint8_t  reserved[3];
} __attribute__((packed));

/* Предварительное объявление */
void kernel_main(struct fb_info *fb_info, void *mmap, uint64_t mmap_size,
                 uint64_t desc_size, uint64_t rsdp_addr);

static void putchar_efi(EFI_SYSTEM_TABLE *systab, char c) {
    uint16_t ustr[2] = {(uint16_t)c, 0};
    void *con_out = systab->ConOut;
    typedef void (__attribute__((ms_abi)) *output_fn)(void*, uint16_t*);
    ((output_fn)(*(void**)((char*)con_out + 8)))(con_out, ustr);
}

static void puts_efi(EFI_SYSTEM_TABLE *systab, const char *s) {
    while(*s) putchar_efi(systab, *s++);
}

void __attribute__((ms_abi)) efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *systab) {
    EFI_BOOT_SERVICES *BS = systab->BootServices;
    EFI_STATUS status;
    struct fb_info fb_info;
    void *acpi_table = NULL;
    struct acpi_rsdp *rsdp = NULL;

    /* Очищаем информацию о буфере кадров */
    fb_info.base = 0;
    fb_info.width = 0;
    fb_info.height = 0;
    fb_info.pitch = 0;
    fb_info.bpp = 4;
    fb_info.rgb = 0;
    fb_info.r_mask = 0;
    fb_info.g_mask = 0;
    fb_info.b_mask = 0;

    /* Получаем GOP */
    EFI_GRAPHICS_OUTPUT_PROTOCOL *gop = NULL;
    struct EFI_GUID local_gop_guid = gop_guid;
    status = BS->LocateProtocol(&local_gop_guid, NULL, (void**)&gop);

    /* Выводим статус GOP */
    {
        char buf[20];
        puts_efi(systab, "LocateProtocol status=");
        uint64_t v = status;
        int pos = 0;
        for(int i = 15; i >= 0; i--) { buf[pos++] = "0123456789abcdef"[(v >> (i*4)) & 0xF]; }
        buf[pos] = 0;
        puts_efi(systab, buf);
        puts_efi(systab, "\r\n");
    }

    if(status == EFI_SUCCESS && gop) {
        puts_efi(systab, "GOP found, Mode=");
        {
            char buf[20];
            uint64_t v = (uint64_t)(uintptr_t)gop->Mode;
            int pos = 0;
            for(int i = 15; i >= 0; i--) { buf[pos++] = "0123456789abcdef"[(v >> (i*4)) & 0xF]; }
            buf[pos] = 0;
            puts_efi(systab, buf);
            puts_efi(systab, "\r\n");
        }
    }

    if(status == EFI_SUCCESS && gop && gop->Mode) {
        EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info = gop->Mode->Info;

        /* Проверяем, валиден ли текущий режим */
        int mode_ok = 0;
        if(info && info->HorizontalResolution > 0 && info->VerticalResolution > 0 &&
           gop->Mode->FrameBufferBase != 0) {
            mode_ok = 1;
        }

        /* Если текущий режим невалиден — перебираем все доступные */
        if(!mode_ok && gop->Mode->MaxMode > 1) {
            puts_efi(systab, "GOP mode invalid, enumerating...\r\n");
            for(uint32_t m = 0; m < gop->Mode->MaxMode; m++) {
                uint64_t info_size = 0;
                EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *mode_info = NULL;
                EFI_STATUS qstatus = gop->QueryMode(gop, m, &info_size, &mode_info);
                if(qstatus != EFI_SUCCESS || !mode_info) continue;

                if(mode_info->HorizontalResolution >= 800 &&
                   mode_info->VerticalResolution >= 600) {
                    /* Пробуем установить этот режим */
                    EFI_STATUS sstatus = gop->SetMode(gop, m);
                    if(sstatus == EFI_SUCCESS) {
                        info = gop->Mode->Info;
                        mode_ok = 1;
                        puts_efi(systab, "GOP mode set OK\r\n");
                        break;
                    }
                }
            }
        }

        if(mode_ok && info) {
            puts_efi(systab, "GOP mode OK\r\n");

            fb_info.base   = gop->Mode->FrameBufferBase;
            fb_info.width  = info->HorizontalResolution;
            fb_info.height = info->VerticalResolution;
            fb_info.pitch  = info->PixelsPerScanLine * 4;
            fb_info.bpp    = 4;

            /* Обработка формата пикселей */
            /*
             * PixelFormat:
             *   0 = PixelFormatRgbReserved  (R в старшем байте → rgb=1)
             *   1 = PixelFormatBgrReserved  (B в старшем байте → rgb=0)
             *   2 = PixelFormatBitMask      (используем маски)
             */
            if(info->PixelFormat == 2) {
                /* BitMask — вычисляем rgb из масок */
                uint32_t rm = info->PixelInformation.RedMask;
                uint32_t gm = info->PixelInformation.GreenMask;
                uint32_t bm = info->PixelInformation.BlueMask;

                fb_info.r_mask = rm;
                fb_info.g_mask = gm;
                fb_info.b_mask = bm;

                /* Определяем порядок: если R старше B → rgb=1, иначе rgb=0 */
                int r_shift = 0, b_shift = 0;
                if(rm) { uint32_t m = rm; while(!(m & 1)) { r_shift++; m >>= 1; } }
                if(bm) { uint32_t m = bm; while(!(m & 1)) { b_shift++; m >>= 1; } }
                fb_info.rgb = (r_shift > b_shift) ? 1 : 0;
            } else {
                /* PixelFormat 0 или 1 — стандартный порядок */
                fb_info.rgb    = info->PixelFormat;
                fb_info.r_mask = (info->PixelFormat == 0) ? 0x00FF0000 : 0x000000FF;
                fb_info.g_mask = 0x0000FF00;
                fb_info.b_mask = (info->PixelFormat == 0) ? 0x000000FF : 0x00FF0000;
            }
        }
    }

    if(fb_info.base && fb_info.width && fb_info.height) {
        puts_efi(systab, "PrismOS: framebuffer OK\r\n");
    } else {
        puts_efi(systab, "PrismOS: no GOP found!\r\n");
        while(1) __asm__("hlt");
    }

    dbg_printf("GOP: %dx%d fmt=%d base=%p\n", fb_info.width, fb_info.height, fb_info.rgb, fb_info.base);

    /* Ищем ACPI RSDP в таблице конфигурации */
    for(uint64_t i = 0; i < systab->NumberOfTableEntries; i++) {
        struct EFI_CONFIGURATION_TABLE *entry = &systab->ConfigurationTable[i];

        /* Проверяем ACPI 2.0+ */
        if(entry->VendorGuid.Data1 == acpi2_guid.Data1 &&
           entry->VendorGuid.Data2 == acpi2_guid.Data2 &&
           entry->VendorGuid.Data3 == acpi2_guid.Data3 &&
           !memcmp(entry->VendorGuid.Data4, acpi2_guid.Data4, 8)) {
            acpi_table = entry->VendorTable;
            puts_efi(systab, "ACPI 2.0 table found\r\n");
            break;
        }

        /* Проверяем ACPI 1.0 */
        if(entry->VendorGuid.Data1 == acpi1_guid.Data1 &&
           entry->VendorGuid.Data2 == acpi1_guid.Data2 &&
           entry->VendorGuid.Data3 == acpi1_guid.Data3 &&
           !memcmp(entry->VendorGuid.Data4, acpi1_guid.Data4, 8)) {
            acpi_table = entry->VendorTable;
            puts_efi(systab, "ACPI 1.0 table found\r\n");
            break;
        }
    }

    if(acpi_table) {
        rsdp = (struct acpi_rsdp *)acpi_table;
        puts_efi(systab, "RSDP signature valid\r\n");
        dbg_printf("ACPI: RSDP at %p\n", rsdp);
    } else {
        puts_efi(systab, "ACPI not found!\r\n");
    }

    /* Получаем карту памяти */
    uint64_t mmap_size = 0;
    uint64_t map_key = 0;
    uint64_t desc_size = 0;
    uint32_t desc_version = 0;

    /* Первый вызов для получения нужного размера */
    dbg_printf("Getting memory map...\n");
    status = BS->GetMemoryMap(&mmap_size, NULL, &map_key, &desc_size, &desc_version);
    puts_efi(systab, "MMap status=");
    {
        char buf[20];
        uint64_t v = status;
        int pos = 0;
        for(int i = 15; i >= 0; i--) { buf[pos++] = "0123456789abcdef"[(v >> (i*4)) & 0xF]; }
        buf[pos] = 0;
        puts_efi(systab, buf);
        puts_efi(systab, " size=");
        v = mmap_size; pos = 0;
        for(int i = 15; i >= 0; i--) { buf[pos++] = "0123456789abcdef"[(v >> (i*4)) & 0xF]; }
        buf[pos] = 0;
        puts_efi(systab, buf);
        puts_efi(systab, "\r\n");
    }
    if(status != EFI_BUFFER_TOO_SMALL) {
        puts_efi(systab, "Failed to get memory map size!\r\n");
        while(1) __asm__("hlt");
    }

    /* Добавляем запас места для дополнительных дескрипторов */
    mmap_size += 128 * desc_size;

    puts_efi(systab, "Alloc pool...\r\n");

    /* Выделяем память под карту памяти */
    EFI_MEMORY_DESCRIPTOR *mmap_buf = NULL;
    status = BS->AllocatePool(0, mmap_size, (void**)&mmap_buf);
    if(status != EFI_SUCCESS) {
        puts_efi(systab, "Failed to allocate pool for memory map!\r\n");
        while(1) __asm__("hlt");
    }

    puts_efi(systab, "Get mmap...\r\n");

    /* Получаем карту памяти снова — последний вызов UEFI перед ExitBootServices */
    status = BS->GetMemoryMap(&mmap_size, mmap_buf, &map_key, &desc_size, &desc_version);
    if(status != EFI_SUCCESS) {
        puts_efi(systab, "Failed to get memory map!\r\n");
        while(1) __asm__("hlt");
    }

    /* ПОСЛЕ ЭТОЙ ТОЧКИ ВЫЗОВЫ UEFI ЗАПРЕЩЕНЫ */
    /* Используем прямой вывод в последовательный порт для отладки */

    /* Выходим из загрузочных сервисов */
    dbg_printf("Exiting boot services...\n");
    status = BS->ExitBootServices(image_handle, map_key);
    if(status != EFI_SUCCESS) {
        /* Нельзя использовать UEFI — пишем статус в последовательный порт 0x3F8 */
        static const char hex[] = "0123456789abcdef";
        char dbg[32] = "EBS1:XX\r\n";
        dbg[5] = hex[(status >> 4) & 0xF];
        dbg[6] = hex[status & 0xF];
        dbg_puts(dbg);

        /* Повторная попытка */
        mmap_size += 128 * desc_size;
        status = BS->GetMemoryMap(&mmap_size, mmap_buf, &map_key, &desc_size, &desc_version);
        if(status != EFI_SUCCESS) {
            char dbg2[32] = "MM2:XX\r\n";
            dbg2[4] = hex[(status >> 4) & 0xF];
            dbg2[5] = hex[status & 0xF];
            dbg_puts(dbg2);
            while(1) __asm__("hlt");
        }
        status = BS->ExitBootServices(image_handle, map_key);
        if(status != EFI_SUCCESS) {
            char dbg3[32] = "EBS2:XX\r\n";
            dbg3[5] = hex[(status >> 4) & 0xF];
            dbg3[6] = hex[status & 0xF];
            dbg_puts(dbg3);
            while(1) __asm__("hlt");
        }
    }

    char dbg_ok[] = "EBS OK!\r\n";
    dbg_puts(dbg_ok);
    dbg_printf("Boot services exited OK\n");

    /* После ExitBootServices доступны только буфер кадров и CPU */
    dbg_printf("Initializing framebuffer...\n");
    char dbg_fb[] = "fb_init...\r\n";
    dbg_puts(dbg_fb);

    /* Инициализируем буфер кадров */
    fb_init(&fb_info);
    char dbg_fb2[] = "fb_puts...\r\n";
    dbg_puts(dbg_fb2);
    fb_puts("PrismOS: boot services exited\r\n");

    char dbg_km[] = "kmain...\r\n";
    dbg_puts(dbg_km);

    /* Вызываем ядро */
    dbg_printf("Jumping to kernel...\n");
    uint64_t rsdp_addr = rsdp ? (uint64_t)rsdp : 0;
    kernel_main(&fb_info, mmap_buf, mmap_size, desc_size, rsdp_addr);

    /* Сюда никогда не попадаем */
    while(1) __asm__("hlt");
}
