//! The allocator to be implemented by ableOS
//!
//! NOTE: All memory regions are taken from https://wiki.osdev.org/Memory_Map_(x86)

#![allow(missing_docs)]

use alloc::alloc::{GlobalAlloc, Layout};
use core::{fmt::Display, ptr::null_mut};
use log::{debug, info};

// const HEAP_START: usize = 600_000_000;
const HEAP_START: usize = 0x00100000;
const BLOCK_SIZE: usize = 1024;
const BLOCK_COUNT: usize = 512;

#[derive(Debug, Clone, Copy)]
pub struct MemoryRegion {
    start: usize,
    end: usize,
}
impl Display for MemoryRegion {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        writeln!(
            f,
            "MemoryRegion {{ start: {}, end: {}, size: {} bytes}}",
            self.start,
            self.end,
            self.end - self.start
        )
    }
}
impl MemoryRegion {
    pub fn new(start: usize, end: usize) -> MemoryRegion {
        MemoryRegion { start, end }
    }
    pub fn test_region(&self) -> bool {
        unsafe {
            let mutptr = self.start as *mut u8;

            core::ptr::write(mutptr, 0xFF);
            // trace!("{}", core::ptr::read(mutptr));
        }

        true
    }
}

#[derive(Debug, Clone, Copy)]
pub struct AAlloc {
    current_region: usize,
    memory_regions: [Option<MemoryRegion>; 512],
}

impl AAlloc {
    fn test_regions(&self) {
        for x in 0..self.current_region {
            if let Some(region) = self.memory_regions[x] {
                debug!("Region {}: {:?}", x, region);
            }
        }
    }

    pub fn add_region(&mut self, mem: MemoryRegion) {
        self.memory_regions[self.current_region] = Some(mem);
        self.current_region += 1;
    }

    pub fn intialize() {
        info!("Heap Start: {}", HEAP_START);
        info!("Heap Size:  {}", BLOCK_SIZE * BLOCK_COUNT);
        info!("Heap End:   {}", HEAP_START + BLOCK_SIZE * BLOCK_COUNT);

        let mut aalloc = AAlloc {
            current_region: 0,
            memory_regions: [None; 512],
        };

        // BS MEMORY REGION
        aalloc.add_region(MemoryRegion::new(HEAP_START, HEAP_START + 10));

        aalloc.add_region(MemoryRegion::new(0x00007E00, 0x0007FFFF));

        aalloc.add_region(MemoryRegion::new(0x00100000, 0x00EFFFFF));

        // ISA Memory Hole
        aalloc.add_region(MemoryRegion::new(0x00F00000, 0x00FFFFFF));

        aalloc.add_region(MemoryRegion::new(0x0000000100000000, 0x0000000100000000));

        aalloc.memory_regions[0].unwrap().test_region();
        debug!("{}", aalloc);
    }
}

impl Display for AAlloc {
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
        write!(f, "AAlloc {{\n\tcurrent_region: {},\n", self.current_region)?;

        for x in 0..self.current_region {
            if let Some(region) = self.memory_regions[x] {
                write!(f, "\tRegion {}: {}", x, region)?;
            }
        }
        write!(f, "}}")?;
        Ok(())
    }
}

unsafe impl GlobalAlloc for AAlloc {
    unsafe fn alloc(&self, _layout: Layout) -> *mut u8 {
        info!("Allocating memory");

        info!("{}", _layout.size());
        info!("{}", _layout.align());

        null_mut()
    }

    unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {
        panic!("dealloc should be never called")
    }
}
