#include "kernel.h"
#include "gdt.h"
#include "idt.h"
#include "paging.h"
#include "lapic.h"
#include "ioapic.h"
#include "printf.h"
#include "fb.h"
#include "arch.h"
#include "util.h"
#include "usbkbd.h"
#include "mouse.h"
#include "dbg.h"
#include "shell.h"
#include "fs.h"
#include "net.h"
#include "malloc.h"
#include "ecc.h"
#include "tls.h"

/* Структуры ACPI для разбора MADT */
struct acpi_sdt {
    char     signature[4];
    uint32_t length;
    uint8_t  revision;
    uint8_t  checksum;
    char     oem_id[6];
    char     oem_table_id[8];
    uint32_t oem_revision;
    uint32_t creator_id;
    uint32_t creator_revision;
} __attribute__((packed));

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));

struct madt_entry_header {
    uint8_t type;
    uint8_t length;
} __attribute__((packed));

struct madt {
    struct acpi_sdt header;
    uint32_t lapic_address;
    uint32_t flags;
    uint8_t  entries[];
} __attribute__((packed));

struct madt_lapic {
    struct madt_entry_header header;
    uint8_t  acpi_processor_id;
    uint8_t  apic_id;
    uint32_t flags;
} __attribute__((packed));

struct madt_ioapic {
    struct madt_entry_header header;
    uint8_t  ioapic_id;
    uint8_t  reserved;
    uint32_t ioapic_address;
    uint32_t gsi_base;
} __attribute__((packed));

struct madt_override {
    struct madt_entry_header header;
    uint8_t  bus;
    uint8_t  source;
    uint32_t gsi;
    uint16_t flags;
} __attribute__((packed));

/* Generic Address Structure ACPI (GAS) */
struct acpi_gas {
    uint8_t  address_space_id; /* 0=память, 1=ввод-вывод, 2=PCI */
    uint8_t  register_bit_width;
    uint8_t  register_bit_offset;
    uint8_t  reserved;
    uint64_t address;
} __attribute__((packed));

struct acpi_fadt {
    struct acpi_sdt header;
    uint32_t firmware_ctrl;
    uint32_t dsdt;
    uint8_t  reserved1;
    uint8_t  preferred_pm_profile;
    uint16_t sci_int;
    uint32_t smi_cmd;
    uint8_t  acpi_enable;
    uint8_t  acpi_disable;
    uint8_t  s4bios_req;
    uint8_t  pstate_cnt;
    uint32_t pm1a_evt_blk;
    uint32_t pm1b_evt_blk;
    uint32_t pm1a_cnt_blk;
    uint32_t pm1b_cnt_blk;
    uint32_t pm2_cnt_blk;
    uint32_t pm_tmr_blk;
    uint32_t gpe0_blk;
    uint32_t gpe1_blk;
    uint8_t  pm1_evt_len;
    uint8_t  pm1_cnt_len;
    uint8_t  pm2_cnt_len;
    uint8_t  pm_tmr_len;
    uint8_t  gpe0_blk_len;
    uint8_t  gpe1_blk_len;
    uint8_t  gpe1_base;
    uint8_t  cst_cnt;
    uint16_t p_lvl2_lat;
    uint16_t p_lvl3_lat;
    uint16_t flush_size;
    uint16_t flush_stride;
    uint8_t  duty_offset;
    uint8_t  duty_width;
    uint8_t  day_alrm;
    uint8_t  mon_alrm;
    uint8_t  century;
    uint16_t iapc_boot_arch;
    uint8_t  reserved2;
    uint32_t flags;
    struct acpi_gas reset_reg;
    uint8_t  reset_value;
    uint8_t  reserved3[3];
} __attribute__((packed));

/* Предварительные объявления */
static void parse_acpi(uint64_t rsdp_addr);
static void print_memmap(void *mmap, uint64_t mmap_size, uint64_t desc_size);

uint64_t total_mem;
static uint64_t lapic_addr = 0xFEE00000;
static uint64_t ioapic_addr = 0xFEC00000;
static uint32_t ioapic_gsi_base = 0;

/* Регистр сброса ACPI */
struct { uint8_t space_id; uint64_t address; uint8_t value; int present; } acpi_reset;

static void find_fadt(struct acpi_sdt *table) {
    if(!memcmp(table->signature, "FACP", 4)) {
        struct acpi_fadt *fadt = (struct acpi_fadt*)table;
        /* Проверяем, корректен ли регистр сброса (register_bit_width > 0) */
        if(fadt->reset_reg.register_bit_width > 0) {
            acpi_reset.space_id = fadt->reset_reg.address_space_id;
            acpi_reset.address  = fadt->reset_reg.address;
            acpi_reset.value    = fadt->reset_value;
            acpi_reset.present  = 1;
            fb_printf("acpi: reset reg space=%d addr=%p val=0x%x\n",
                acpi_reset.space_id, acpi_reset.address, acpi_reset.value);
        }
    }
}

static void parse_acpi(uint64_t rsdp_addr) {
    if(!rsdp_addr) {
        fb_printf("acpi: no RSDP\n");
        return;
    }
    acpi_reset.present = 0;

    struct acpi_rsdp *rsdp = (struct acpi_rsdp*)rsdp_addr;

    struct acpi_sdt *rsdt, *xsdt;
    uint32_t entry_count;

    if(rsdp->revision >= 2 && rsdp->xsdt_address) {
        /* Используем XSDT */
        xsdt = (struct acpi_sdt*)(uintptr_t)rsdp->xsdt_address;
        entry_count = (xsdt->length - sizeof(struct acpi_sdt)) / 8;
        fb_printf("acpi: XSDT at %p, %u entries\n", xsdt, entry_count);

        for(uint32_t i = 0; i < entry_count; i++) {
            uint64_t *entries = (uint64_t*)((uintptr_t)xsdt + sizeof(struct acpi_sdt));
            struct acpi_sdt *table = (struct acpi_sdt*)(uintptr_t)entries[i];

            find_fadt(table);

            if(!memcmp(table->signature, "APIC", 4)) {
                struct madt *madt = (struct madt*)table;
                lapic_addr = madt->lapic_address;
                fb_printf("acpi: MADT at %p, LAPIC addr=%p\n", madt, lapic_addr);

                /* Разбираем записи MADT */
                uint8_t *pos = madt->entries;
                uint8_t *end = (uint8_t*)madt + madt->header.length;
                while(pos < end) {
                    struct madt_entry_header *hdr = (struct madt_entry_header*)pos;
                    if(hdr->length == 0) break;

                    if(hdr->type == 1) {
                        struct madt_ioapic *ioapic = (struct madt_ioapic*)pos;
                        ioapic_addr = ioapic->ioapic_address;
                        ioapic_gsi_base = ioapic->gsi_base;
                    }
                    pos += hdr->length;
                }
            }
        }
    } else {
        /* Используем RSDT */
        rsdt = (struct acpi_sdt*)(uintptr_t)rsdp->rsdt_address;
        entry_count = (rsdt->length - sizeof(struct acpi_sdt)) / 4;
        fb_printf("acpi: RSDT at %p, %u entries\n", rsdt, entry_count);

        for(uint32_t i = 0; i < entry_count; i++) {
            uint32_t *entries = (uint32_t*)((uintptr_t)rsdt + sizeof(struct acpi_sdt));
            struct acpi_sdt *table = (struct acpi_sdt*)(uintptr_t)entries[i];

            find_fadt(table);

            if(!memcmp(table->signature, "APIC", 4)) {
                struct madt *madt = (struct madt*)table;
                lapic_addr = madt->lapic_address;
                fb_printf("acpi: MADT at %p, LAPIC addr=%p\n", madt, lapic_addr);

                uint8_t *pos = madt->entries;
                uint8_t *end = (uint8_t*)madt + madt->header.length;
                while(pos < end) {
                    struct madt_entry_header *hdr = (struct madt_entry_header*)pos;
                    if(hdr->length == 0) break;

                    if(hdr->type == 1) {
                        struct madt_ioapic *ioapic = (struct madt_ioapic*)pos;
                        ioapic_addr = ioapic->ioapic_address;
                        ioapic_gsi_base = ioapic->gsi_base;
                    }
                    pos += hdr->length;
                }
            }
        }
    }
}

static void print_memmap(void *mmap, uint64_t mmap_size, uint64_t desc_size) {
    uint64_t count = mmap_size / desc_size;

    fb_printf("Memory Map: %llu entries\n", count);
    total_mem = 0;

    for(uint64_t i = 0; i < count; i++) {
        EFI_MEMORY_DESCRIPTOR *d = (EFI_MEMORY_DESCRIPTOR*)((uint8_t*)mmap + i * desc_size);
        uint64_t pages = d->NumberOfPages;

        if(d->Type == EFI_MEMORY_TYPE_CONVENTIONAL) {
            total_mem += pages * 4096;
        }
    }

    fb_printf("Total conventional memory: %llu MB\n", total_mem / (1024 * 1024));
}













void kernel_main(struct fb_info *fb_info, void *mmap, uint64_t mmap_size,
                 uint64_t desc_size, uint64_t rsdp_addr) {
    /* Шаг 1: инициализируем консоль буфера кадров */
    fb_init(fb_info);
    dbg_init();
    fb_printf("%s %s - booting...\n", PRISMOS_NAME, PRISMOS_VERSION);
    dbg_printf("=== PrismOS kernel start ===\n");

    /* Шаг 2: инициализируем GDT */
    fb_printf("[0/6] GDT init... ");
    dbg_printf("GDT: init...\n");
    gdt_init();
    fb_printf("OK\n");

    /* Шаг 3: инициализируем страничную адресацию */
    fb_printf("[1/6] Paging init... ");
    dbg_printf("Paging: init...\n");
    paging_init();
    fb_printf("OK\n");

    /* Включаем страничную адресацию */
    fb_printf("[2/6] Enabling paging... ");
    dbg_printf("Paging: enabling...\n");
    paging_enable();
    fb_printf("OK\n");

    /* Шаг 4: инициализируем IDT */
    fb_printf("[3/6] IDT init... ");
    dbg_printf("IDT: init...\n");
    idt_init();
    fb_printf("OK\n");
    dbg_printf("IDT: OK\n");

    /* Шаг 5: разбираем таблицы ACPI */
    fb_printf("[4/6] ACPI init... ");
    dbg_printf("ACPI: parsing tables...\n");
    parse_acpi(rsdp_addr);
    fb_printf("OK\n");

    /* Выводим карту памяти */
    print_memmap(mmap, mmap_size, desc_size);

    /* Шаг 6: инициализируем APIC */
    fb_printf("[5/6] APIC init...\n");
    dbg_printf("APIC: init...\n");

    /* Инициализируем LAPIC */
    lapic_init(lapic_addr);

    /* Инициализируем IOAPIC */
    ioapic_init(ioapic_addr, ioapic_gsi_base);

    fb_printf("APIC init OK\n");

    /* Включаем прерывания */
    sti();

    /* Инициализируем таймер LAPIC */
    lapic_timer_init(0x20, 1000);

    /* Инициализируем USB-клавиатуру */
    fb_printf("[6/6] USB keyboard init... ");
    dbg_printf("USB: init...\n");
    int kbd_ok = usb_kbd_init();
    fb_printf("%s\n", kbd_ok ? "OK" : "NOT FOUND");
    dbg_printf("USB: %s\n", kbd_ok ? "READY" : "NOT FOUND");

    /* Инициализируем PS/2 мышь (ноутбуки/тачпад) */
    ps2_mouse_init();

    /* Инициализируем USB-мышь */
    mouse_cursor_init();
    usb_mouse_init();

    fb_printf("\n=== %s %s booted at %d x %d ===\n", PRISMOS_NAME, PRISMOS_VERSION, fb.width, fb.height);

    fs_init();
    net_init();
    kfree_init(0, 0);

    /* Auto-test: ECC self-test */
    ecc_self_test();

    shell_init(total_mem);
    shell_run();
}
