# opencoreC - Root Unified Makefile
# Target Architecture: x86_64 Long Mode (Microkernel OS)

SHELL := /usr/bin/env bash

# Toolchain definitions
CC      := gcc
CXX     := g++
LD      := ld
CARGO   := cargo
QEMU    := qemu-system-x86_64

# Directory layout
BUILD_DIR   := build
CONFIG_DIR  := config
INCLUDE_DIR := include
LIB_DIR     := lib
HAL_DIR     := hal
SERVERS_DIR := servers
KERNEL_DIR  := kernel
LIMINE_DIR  := boot/limine

# Common Freestanding Flags
COMMON_FLAGS := -Wall -Wextra -O2 -ffreestanding -fno-stack-protector \
                -fno-pie -no-pie -mno-red-zone -m64 \
                -mno-sse -mno-sse2 -mno-avx \
                -I$(INCLUDE_DIR) -I$(LIB_DIR)/libc/include -I$(HAL_DIR)/include

CFLAGS   := $(COMMON_FLAGS) -std=c11
CXXFLAGS := $(COMMON_FLAGS) -std=c++20 -fno-exceptions -fno-rtti -I$(LIB_DIR)/libipc_cpp/include

# Outputs
KERNEL_BIN   := target/x86_64-unknown-none/release/opencore_kernel
KERNEL_ELF   := $(BUILD_DIR)/opencore_kernel.elf
INIT_SERVER  := $(BUILD_DIR)/init_server.elf
UART_DRIVER  := $(BUILD_DIR)/uart_driver.elf
VFS_SERVER   := $(BUILD_DIR)/vfs_server.elf
NET_SERVER   := $(BUILD_DIR)/net_server.elf
SH_SERVER    := $(BUILD_DIR)/sh.elf
PROCMGR      := $(BUILD_DIR)/procmgr.elf
HELLO_APP    := $(BUILD_DIR)/hello.opc
OGIT_APP     := $(BUILD_DIR)/ogit.opc
DISK_IMG     := $(BUILD_DIR)/opencoreC.img

LIBC_OBJS    := $(BUILD_DIR)/string.o $(BUILD_DIR)/syscalls.o $(BUILD_DIR)/stdlib.o
NET_OBJS     := $(BUILD_DIR)/net.o $(BUILD_DIR)/rtl8139.o $(BUILD_DIR)/e1000.o $(BUILD_DIR)/r8169.o $(BUILD_DIR)/pci.o
STORAGE_OBJS := $(BUILD_DIR)/ahci.o $(BUILD_DIR)/fat32.o $(BUILD_DIR)/pci.o

.PHONY: all kernel libc hal servers image run qemu clean help limine-setup

all: kernel libc hal servers
	@mkdir -p $(BUILD_DIR)
	@cp $(KERNEL_BIN) $(KERNEL_ELF)
	@echo "======================================================="
	@echo " [SUCCESS] opencoreC Build Complete!"
	@echo " Kernel:   $(KERNEL_ELF)"
	@echo " Servers:  $(INIT_SERVER), $(UART_DRIVER), $(VFS_SERVER), $(NET_SERVER), $(PROCMGR), $(SH_SERVER)"
	@echo " Run:      make run (Interactive Shell Terminal)"
	@echo "======================================================="

kernel:
	@echo "[CARGO] Compiling Rust Microkernel (x86_64-unknown-none)..."
	@RUSTFLAGS="-C link-arg=-T$(CONFIG_DIR)/linker.ld -C code-model=kernel -C relocation-model=static" \
		$(CARGO) build --package opencore_kernel --target x86_64-unknown-none --release

libc:
	@mkdir -p $(BUILD_DIR)
	@echo "[CC] Compiling bare-metal freestanding libc..."
	@$(CC) $(CFLAGS) -c $(LIB_DIR)/libc/src/string.c -o $(BUILD_DIR)/string.o
	@$(CC) $(CFLAGS) -c $(LIB_DIR)/libc/src/syscalls.c -o $(BUILD_DIR)/syscalls.o
	@$(CC) $(CFLAGS) -c $(LIB_DIR)/libc/src/stdlib.c -o $(BUILD_DIR)/stdlib.o
	@$(CC) $(CFLAGS) -I$(HAL_DIR)/include -c $(LIB_DIR)/libc/src/net.c -o $(BUILD_DIR)/net.o

hal: libc
	@mkdir -p $(BUILD_DIR)
	@echo "[CC] Compiling HAL and Hardware Drivers..."
	@$(CC) $(CFLAGS) -I$(HAL_DIR)/include -c $(HAL_DIR)/src/uart.c -o $(BUILD_DIR)/uart.o
	@$(CC) $(CFLAGS) -I$(HAL_DIR)/include -c $(HAL_DIR)/src/pci.c -o $(BUILD_DIR)/pci.o
	@$(CC) $(CFLAGS) -I$(HAL_DIR)/include -c $(HAL_DIR)/src/apic.c -o $(BUILD_DIR)/apic.o
	@$(CC) $(CFLAGS) -I$(HAL_DIR)/include -c $(HAL_DIR)/src/fb.c -o $(BUILD_DIR)/fb.o
	@$(CC) $(CFLAGS) -I$(HAL_DIR)/include -c $(HAL_DIR)/src/rtl8139.c -o $(BUILD_DIR)/rtl8139.o
	@$(CC) $(CFLAGS) -I$(HAL_DIR)/include -c $(HAL_DIR)/src/e1000.c -o $(BUILD_DIR)/e1000.o
	@$(CC) $(CFLAGS) -I$(HAL_DIR)/include -c $(HAL_DIR)/src/r8169.c -o $(BUILD_DIR)/r8169.o
	@$(CC) $(CFLAGS) -I$(HAL_DIR)/include -c $(HAL_DIR)/src/ahci.c -o $(BUILD_DIR)/ahci.o
	@$(CC) $(CFLAGS) -I$(HAL_DIR)/include -c $(HAL_DIR)/src/fat32.c -o $(BUILD_DIR)/fat32.o

servers: libc hal
	@mkdir -p $(BUILD_DIR)
	@echo "[CXX/CC] Linking Ring 3 User-space Servers..."
	@# C++ LibIPC
	@$(CXX) $(CXXFLAGS) -c $(LIB_DIR)/libipc_cpp/src/ipc.cpp -o $(BUILD_DIR)/ipc.o
	@# Init Root Server
	@$(CXX) $(CXXFLAGS) -c $(SERVERS_DIR)/init/main.cpp -o $(BUILD_DIR)/init_main.o
	@$(LD) -T $(CONFIG_DIR)/user.ld -nostdlib -static -o $(INIT_SERVER) \
		$(BUILD_DIR)/init_main.o $(BUILD_DIR)/ipc.o $(LIBC_OBJS)
	@# Standalone UART Driver Server
	@$(CC) $(CFLAGS) -I$(HAL_DIR)/include -c $(SERVERS_DIR)/uart_driver/main.c -o $(BUILD_DIR)/uart_driver_main.o
	@$(LD) -T $(CONFIG_DIR)/user.ld -nostdlib -static -o $(UART_DRIVER) \
		$(BUILD_DIR)/uart_driver_main.o $(BUILD_DIR)/uart.o $(LIBC_OBJS)
	@# Standalone Virtual File System Server (with AHCI SATA & FAT32)
	@$(CXX) $(CXXFLAGS) -I$(HAL_DIR)/include -c $(SERVERS_DIR)/vfs_server/main.cpp -o $(BUILD_DIR)/vfs_server_main.o
	@$(LD) -T $(CONFIG_DIR)/user.ld -nostdlib -static -o $(VFS_SERVER) \
		$(BUILD_DIR)/vfs_server_main.o $(BUILD_DIR)/ipc.o $(STORAGE_OBJS) $(LIBC_OBJS)
	@# Standalone Network Stack Server (with Gigabit e1000 & r8169)
	@$(CXX) $(CXXFLAGS) -I$(HAL_DIR)/include -c $(SERVERS_DIR)/net_server/main.cpp -o $(BUILD_DIR)/net_server_main.o
	@$(LD) -T $(CONFIG_DIR)/user.ld -nostdlib -static -o $(NET_SERVER) \
		$(BUILD_DIR)/net_server_main.o $(BUILD_DIR)/ipc.o $(NET_OBJS) $(LIBC_OBJS)
	@# Interactive Shell with Networking & Framebuffer
	@$(CXX) $(CXXFLAGS) -I$(HAL_DIR)/include -c $(SERVERS_DIR)/sh/main.cpp -o $(BUILD_DIR)/sh_main.o
	@$(LD) -T $(CONFIG_DIR)/user.ld -nostdlib -static -o $(SH_SERVER) \
		$(BUILD_DIR)/sh_main.o $(BUILD_DIR)/ipc.o $(BUILD_DIR)/fb.o $(NET_OBJS) $(LIBC_OBJS)
	@# Process Manager Daemon
	@$(CXX) $(CXXFLAGS) -c $(SERVERS_DIR)/procmgr/main.cpp -o $(BUILD_DIR)/procmgr_main.o
	@$(LD) -T $(CONFIG_DIR)/user.ld -nostdlib -static -o $(PROCMGR) \
		$(BUILD_DIR)/procmgr_main.o $(BUILD_DIR)/ipc.o $(LIBC_OBJS)
	@# Native .opc Executable Application (hello.opc)
	@$(CC) $(CFLAGS) -c apps/hello.c -o $(BUILD_DIR)/hello.o
	@$(LD) -T $(CONFIG_DIR)/user.ld -nostdlib -static -o $(BUILD_DIR)/hello.elf \
		$(BUILD_DIR)/hello.o $(LIBC_OBJS)
	@python3 tools/elf2opc.py $(BUILD_DIR)/hello.elf $(HELLO_APP)
	@# Native .opc OpenCore Git Application (ogit.opc)
	@$(CC) $(CFLAGS) -c apps/ogit.c -o $(BUILD_DIR)/ogit.o
	@$(LD) -T $(CONFIG_DIR)/user.ld -nostdlib -static -o $(BUILD_DIR)/ogit.elf \
		$(BUILD_DIR)/ogit.o $(LIBC_OBJS)
	@python3 tools/elf2opc.py $(BUILD_DIR)/ogit.elf $(OGIT_APP)

# Download and build the Limine bootloader host tool
limine-setup:
	@if [ ! -d "$(LIMINE_DIR)" ]; then \
		echo "[LIMINE] Cloning Limine v8 binary branch..."; \
		git clone https://github.com/limine-bootloader/limine.git --branch=v8.x-binary --depth=1 $(LIMINE_DIR); \
	fi
	@echo "[LIMINE] Building host installer tool..."
	@$(MAKE) -C $(LIMINE_DIR) limine

# Build a 64 MiB bootable FAT32 disk image with Limine BIOS/UEFI
image: all limine-setup
	@echo "[IMAGE] Creating bootable disk image (64 MiB FAT32)..."
	@dd if=/dev/zero of=$(DISK_IMG) bs=1M count=64 2>/dev/null
	@parted -s $(DISK_IMG) mklabel msdos mkpart primary fat32 2048s 100% set 1 boot on 2>/dev/null
	@mformat -F -i $(DISK_IMG)@@1M ::
	@mmd -i $(DISK_IMG)@@1M ::/boot ::/EFI ::/EFI/BOOT
	@# Kernel and userspace servers
	@mcopy -i $(DISK_IMG)@@1M $(KERNEL_ELF)   ::/boot/
	@mcopy -i $(DISK_IMG)@@1M $(INIT_SERVER)  ::/boot/
	@mcopy -i $(DISK_IMG)@@1M $(UART_DRIVER)  ::/boot/
	@mcopy -i $(DISK_IMG)@@1M $(VFS_SERVER)   ::/boot/
	@mcopy -i $(DISK_IMG)@@1M $(NET_SERVER)   ::/boot/
	@mcopy -i $(DISK_IMG)@@1M $(PROCMGR)      ::/boot/
	@mcopy -i $(DISK_IMG)@@1M $(SH_SERVER)    ::/boot/
	@mcopy -i $(DISK_IMG)@@1M $(HELLO_APP)    ::/boot/
	@mcopy -i $(DISK_IMG)@@1M $(OGIT_APP)     ::/boot/
	@# Bootloader config
	@mcopy -i $(DISK_IMG)@@1M limine.conf ::/
	@mcopy -i $(DISK_IMG)@@1M limine.cfg  ::/
	@mcopy -i $(DISK_IMG)@@1M limine.conf ::/boot/
	@mcopy -i $(DISK_IMG)@@1M limine.cfg  ::/boot/
	@# Limine bootloader files
	@mcopy -i $(DISK_IMG)@@1M $(LIMINE_DIR)/limine-bios.sys ::/
	@mcopy -i $(DISK_IMG)@@1M $(LIMINE_DIR)/limine-bios.sys ::/boot/
	@mcopy -i $(DISK_IMG)@@1M $(LIMINE_DIR)/BOOTX64.EFI    ::/EFI/BOOT/
	@# Install Limine Stage 1 into MBR
	@$(LIMINE_DIR)/limine bios-install $(DISK_IMG) 2>&1 | grep -v "^Physical\|^Installing\|^Stage\|^Reminder"
	@echo "[IMAGE] Done: $(DISK_IMG) (BIOS + UEFI bootable)"

# Launch QEMU with serial terminal (Interactive CLI mode)
run: image
	@echo "[QEMU] Launching opencoreC (SMP 4 cores, Serial terminal, Framebuffer, RTL8139 Net)..."
	@echo "[QEMU] Type 'help' for commands. Ctrl+A X to exit QEMU."
	@echo "-------------------------------------------------------"
	@$(QEMU) \
		-M q35 \
		-smp 4 \
		-m 512M \
		-vga std \
		-netdev user,id=net0 \
		-device rtl8139,netdev=net0 \
		-serial mon:stdio \
		-no-reboot \
		-display none \
		-drive file=$(DISK_IMG),format=raw

# Launch QEMU with Native Host CPU via KVM Hardware Acceleration
run-kvm: image
	@echo "[QEMU] Launching with Native Host CPU & KVM Hardware Acceleration..."
	@echo "[QEMU] Type 'help' for commands. Ctrl+A X to exit QEMU."
	@echo "-------------------------------------------------------"
	@$(QEMU) \
		-enable-kvm \
		-cpu host \
		-M q35 \
		-smp 4 \
		-m 512M \
		-vga std \
		-netdev user,id=net0 \
		-device e1000,netdev=net0 \
		-serial mon:stdio \
		-no-reboot \
		-display none \
		-drive file=$(DISK_IMG),format=raw

# Run without rebuilding image (faster iteration)
qemu: $(DISK_IMG)
	@echo "[QEMU] Quick launch from existing image..."
	@echo "[QEMU] Type 'help'. Ctrl+A X to exit."
	@$(QEMU) -M q35 -smp 4 -m 512M -vga std -netdev user,id=net0 -device rtl8139,netdev=net0 \
		-serial mon:stdio -no-reboot -display none \
		-drive file=$(DISK_IMG),format=raw

clean:
	@echo "[CLEAN] Removing build directory and cargo target..."
	@rm -rf $(BUILD_DIR) target

help:
	@echo "opencoreC Microkernel Build System"
	@echo ""
	@echo "Targets:"
	@echo "  make all          - Build kernel, HAL, libc, and all user servers"
	@echo "  make image        - Create bootable disk image (build/opencoreC.img)"
	@echo "  make gui          - Launch QEMU with Desktop GUI Window (LVGL, Framebuffer, Mouse)"
	@echo "  make run          - Build image and launch in QEMU (interactive serial CLI)"
	@echo "  make qemu         - Quick relaunch without rebuild (CLI)"
	@echo "  make limine-setup - Download/build Limine bootloader"
	@echo "  make clean        - Remove all build artifacts"
	@echo ""
	@echo "QEMU Controls:"
	@echo "  Type commands in the terminal (serial I/O)"
	@echo "  Ctrl+A then X  -> exit QEMU"
	@echo "  Ctrl+A then C  -> QEMU monitor"
