#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <limine.h>
#include <stdint.h>
//#include "global_vars.h"
//#include <include/font.h>
//#include <include/types.h>
#include "screen.h"
#include "sprint.h"
#include "gdt.h"
#include "clear_screen.h"
#include <interrupts/idt.h>
#include <drivers/keyboard.h>
#include <include/constants.h>
#include <c_programs/clear_and_print.h>
#include <a_tools/timer.h>
#include <c_programs/start_menu.h>
#include <memory_management/pmm.h>
#include <drivers/mouse.h>
#include <gui/mode.h>
#include <drivers/disk/ata.h>
#include <drivers/disk/fat.h>
#include <drivers/disk/mbr.h>
#include <memory_management/pmm.h>
#include <include/constants.h>

//#include "screen.c"
// Set the base revision to 2, this is recommended as this is the latest
// base revision described by the Limine boot protocol specification.
// See specification for further info.

__attribute__((used, section(".requests")))
static volatile LIMINE_BASE_REVISION(2);

// The Limine requests can be placed anywhere, but it is important that
// the compiler does not optimise them away, so, usually, they should
// be made volatile or equivalent, _and_ they should be accessed at least
// once or marked as used with the "used" attribute as done here.

__attribute__((used, section(".requests")))
static volatile struct limine_framebuffer_request framebuffer_request = {
    .id = LIMINE_FRAMEBUFFER_REQUEST,
    .revision = 0
};

// Finally, define the start and end markers for the Limine requests.
// These can also be moved anywhere, to any .c file, as seen fit.


// GCC and Clang reserve the right to generate calls to the following
// 4 functions even if they are not directly called.
// Implement them as the C specification mandates.
// DO NOT remove or rename these functions, or stuff will eventually break!
// They CAN be moved to a different .c file.

void *memcpy(void *dest, const void *src, size_t n) {
    uint8_t *pdest = (uint8_t *)dest;
    const uint8_t *psrc = (const uint8_t *)src;

    for (size_t i = 0; i < n; i++) {
        pdest[i] = psrc[i];
    }

    return dest;
}

void *memset(void *s, int c, size_t n) {
    uint8_t *p = (uint8_t *)s;

    for (size_t i = 0; i < n; i++) {
        p[i] = (uint8_t)c;
    }

    return s;
}

void *memmove(void *dest, const void *src, size_t n) {
    uint8_t *pdest = (uint8_t *)dest;
    const uint8_t *psrc = (const uint8_t *)src;

    if (src > dest) {
        for (size_t i = 0; i < n; i++) {
            pdest[i] = psrc[i];
        }
    } else if (src < dest) {
        for (size_t i = n; i > 0; i--) {
            pdest[i-1] = psrc[i-1];
        }
    }

    return dest;
}

int memcmp(const void *s1, const void *s2, size_t n) {
    const uint8_t *p1 = (const uint8_t *)s1;
    const uint8_t *p2 = (const uint8_t *)s2;

    for (size_t i = 0; i < n; i++) {
        if (p1[i] != p2[i]) {
            return p1[i] < p2[i] ? -1 : 1;
        }
    }

    return 0;
}

// Halt and catch fire function.
static void hcf(void) {
    asm ("cli");
    for (;;) {
        asm ("hlt");
    }
}

// The following will be our kernel's entry point.
// If renaming _start() to something else, make sure to change the
// linker script accordingly.
void _start(void) {
    // Ensure the bootloader actually understands our base revision (see spec).
    if (LIMINE_BASE_REVISION_SUPPORTED == false) {
        hcf();
    }

    // Ensure we got a framebuffer.
    if (framebuffer_request.response == NULL
     || framebuffer_request.response->framebuffer_count < 1) {
        hcf();
    }

    // Fetch the first framebuffer.
    struct limine_framebuffer *framebuffer = framebuffer_request.response->framebuffers[0];

    // Note: we assume the framebuffer model is RGB with 32-bit pixels.
    //for (size_t i = 0; i < 100; i++) {
    //   volatile uint32_t *fb_ptr = framebuffer->address;
  //      fb_ptr[i * (framebuffer->pitch / 4) + i] = 0xff0000;
//    }





    //////////////////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////////////////////////
    //  -------------- ACTUAL KERNEL START, (BESIDES THE LINE DRAWING SHUT UP )-----------------------
    //////////////////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////////////////////////



 //   static void hang_and_set_interrupt_flag(void) {
   // asm ("cli");
    //for (;;) {
      //  asm ("hlt");
    //}
    //}




    fb_addr = (volatile uint32_t*) framebuffer->address; // one line to make it work smh

    // print char at:

    // func ('character', horizontal(x), vertical(y), colour)

   // print_char_at('Y', 0, 0, 0xFFFFFFFF);
  //  print_char_at('E', 1, 0, 0xFFFFFFFF);
   // print_char_at('S', 2, 0, 0xFFFFFFFF);
  //  print_char_at('!', 3, 0, 0xFFFFFFFF);
  //  sprint("Hello, World!", 0xFFFFFFFF);
    sprint("booting RedRosesOS: ", white);
    sprint(os_release, nice_red);
    sprint("\n", white);
    sprint("like this \n", 0xFFFFFF);
    sprint("colour too \n", 0x123123);
    sprint("pretty pink \n\n\n", 0xe81e8d);
    __asm__ ("sti");
    
    GDT_init();
    IDT_init();
    
    keyboard_init();
    IDT_init();
    keyboard_init();
    IDT_init();
    keyboard_init();
    keyboard_init();

    ATA_ALL_INIT();
    pmm_init();
    mouse_init();
    
    //clear_screen();
    cursor_pos_y = 0;
    main_menu();
    initTimer();
    playSoundTimed(880, 2);
    extern int mode;
    mode = 1;
    //clear_and_print();
    
    //clear_screen();
   // sprint("clear screen works ! \n", 0xffffff);
    //sprint("what the fuck", 0x123123);
    // We're done, just hang...
}
