# ==============================================================================
# 1. Variables
# ==============================================================================
KERNEL_DIR   	= linux
INIT_DIR     	= init
USERPROG_DIR 	= userprog
GRUB_DIR    	= grub
BUILD_DIR   	= build

USER_OUT_DIR 	= $(BUILD_DIR)/userprog
INIT_OUT_DIR 	= $(BUILD_DIR)/init

# [AUTOMATION]
USER_DIRS    	= $(wildcard $(USERPROG_DIR)/*)
USER_NAMES   	= $(notdir $(USER_DIRS))
USER_BINS    	= $(addprefix $(USER_OUT_DIR)/, $(USER_NAMES))

# Config & Tools
DISK_SIZE_MB 	= 512

ROOT_SIZE_MB 	= 256
ROOT_SECTORS 	= $$(($(ROOT_SIZE_MB) * 2048))
ROOT_OFFSET  	= 2048

HOME_SIZE_MB 	= 128
HOME_SECTORS 	= $$(($(HOME_SIZE_MB) * 2048))
HOME_OFFSET  	= $$(($(ROOT_OFFSET) + $(ROOT_SECTORS)))

GRUB_LIB     	= /usr/lib/grub/i386-pc
DD           	= dd status=none
DEBUGFS      	= debugfs

# Sources & Artifacts
KERNEL_SRC_BIN 	= $(KERNEL_DIR)/arch/x86/boot/bzImage
INIT_SRC       	= $(INIT_DIR)/init.c
GRUB_CFG_SRC   	= $(GRUB_DIR)/grub.cfg

INIT_BIN       	= $(INIT_OUT_DIR)/init

DISK_IMG       	= $(BUILD_DIR)/disk.img
ROOT_IMG		= $(BUILD_DIR)/rootfs.img
HOME_IMG     	= $(BUILD_DIR)/homefs.img
CORE_IMG       	= $(BUILD_DIR)/core.img

# ==============================================================================
# 2. Main Targets
# ==============================================================================
.PHONY: all clean run dirs help force_look

all: dirs $(DISK_IMG)
	@echo " [DONE]  Image ready: $(DISK_IMG)"

run: $(DISK_IMG)
	qemu-system-x86_64 -drive file=$(DISK_IMG),format=raw -nographic \
		-net nic,model=e1000 -net user

dirs:
	@mkdir -p $(BUILD_DIR) $(USER_OUT_DIR) $(INIT_OUT_DIR)
# 	@mkdir -p $(USER_OUT_DIR)
# 	@mkdir -p $(INIT_OUT_DIR)

clean:
	@echo " [CLEAN] Cleaning root build dir..."
	@rm -rf $(BUILD_DIR)
	@echo " [CLEAN] Cleaning user programs..."
	@# 각 유저 프로그램 폴더 들어가서 make clean 실행
	@for dir in $(USER_DIRS); do \
		if [ -f $$dir/Makefile ]; then $(MAKE) -C $$dir clean; fi; \
	done

clean-kernel:
	@$(MAKE) -C $(KERNEL_DIR) clean

# ==============================================================================
# 3. Build Rules
# ==============================================================================

$(INIT_BIN): $(INIT_SRC) | dirs
	@echo " [CC]    Compiling init..."
	@gcc -static -o $@ $<

$(USER_BINS): $(USER_OUT_DIR)/%: force_look | dirs
	@echo " [MAKE]  Entering directory: $(USERPROG_DIR)/$*"
	@$(MAKE) -s -C $(USERPROG_DIR)/$* OUT_BIN=$(abspath $@) --no-print-directory

force_look:
	@true

$(KERNEL_SRC_BIN):
	@echo " [MAKE]  Checking Kernel..."
	@$(MAKE) -C $(KERNEL_DIR) -j$$(nproc) bzImage

$(CORE_IMG): | dirs
	@grub-mkimage -O i386-pc -o $@ -p "(hd0,msdos1)/boot/grub" biosdisk part_msdos ext2 linux configfile normal boot

$(ROOT_IMG): $(INIT_BIN) $(USER_BINS) $(KERNEL_SRC_BIN) $(GRUB_CFG_SRC) | dirs
	@echo " [ROOT]  Generating Root Filesystem..."
	
	@$(DD) if=/dev/zero of=$@ bs=1M count=$(ROOT_SIZE_MB)
	@mkfs.ext4 -q -O ^64bit,^metadata_csum $@

	@echo "         -> Creating Directory Structure..."
	@$(DEBUGFS) -w -R "mkdir /boot" $@ > /dev/null 2>&1
	@$(DEBUGFS) -w -R "mkdir /boot/grub" $@ > /dev/null 2>&1
	@$(DEBUGFS) -w -R "mkdir /bin" $@ > /dev/null 2>&1
	@$(DEBUGFS) -w -R "mkdir /dev" $@ > /dev/null 2>&1
	@$(DEBUGFS) -w -R "mkdir /proc" $@ > /dev/null 2>&1
	@$(DEBUGFS) -w -R "mkdir /sys" $@ > /dev/null 2>&1
	@$(DEBUGFS) -w -R "mkdir /tmp" $@ > /dev/null 2>&1
	@$(DEBUGFS) -w -R "mkdir /home" $@ > /dev/null 2>&1

	@echo "         -> Injecting System Binaries..."
	@$(DEBUGFS) -w -R "write $(INIT_BIN) /init" $@ > /dev/null 2>&1
	@$(DEBUGFS) -w -R "write $(KERNEL_SRC_BIN) /boot/bzImage" $@ > /dev/null 2>&1
	@$(DEBUGFS) -w -R "write $(GRUB_CFG_SRC) /boot/grub/grub.cfg" $@ > /dev/null 2>&1

	@for prog in $(USER_NAMES); do \
		$(DEBUGFS) -w -R "write $(USER_OUT_DIR)/$$prog /bin/$$prog" $@ > /dev/null 2>&1; \
	done

$(HOME_IMG): | dirs
	@echo " [HOME]  Checking Home Filesystem..."
	
	@if [ -f $(DISK_IMG) ]; then \
		echo "         -> Extracting HomeFS from disk.img (Preserving Data)..."; \
		$(DD) if=$(DISK_IMG) of=$@ bs=512 skip=$(HOME_OFFSET) count=$(HOME_SECTORS); \
	elif [ ! -f $@ ]; then \
		echo "         -> Creating new HomeFS (Format)..."; \
		$(DD) if=/dev/zero of=$@ bs=1M count=$(HOME_SIZE_MB); \
		mkfs.ext4 -q -O ^64bit,^metadata_csum $@; \
	else \
		echo "         -> Using existing homefs.img"; \
	fi


$(DISK_IMG): $(ROOT_IMG) $(HOME_IMG) $(CORE_IMG)
	@echo " [ASM]   Assembling disk with partitions..."
	@$(DD) if=/dev/zero of=$@ bs=1M count=$(DISK_SIZE_MB)
	
	@echo "start=$(ROOT_OFFSET), size=$(ROOT_SECTORS), type=83, bootable" > partition.script
	@echo "start=$(HOME_OFFSET), size=$(HOME_SECTORS), type=83" >> partition.script
	@sfdisk $@ < partition.script > /dev/null 2>&1
	@rm partition.script

	@echo "         -> Writing RootFS (p1)..."
	@$(DD) if=$(ROOT_IMG) of=$@ bs=512 seek=$(ROOT_OFFSET) conv=notrunc
	
	@echo "         -> Writing HomeFS (p2)..."
	@$(DD) if=$(HOME_IMG) of=$@ bs=512 seek=$(HOME_OFFSET) conv=notrunc
	
	@echo "         -> Installing GRUB..."
	@$(DD) if=$(GRUB_LIB)/boot.img of=$@ bs=446 count=1 conv=notrunc
	@$(DD) if=$(CORE_IMG) of=$@ bs=512 seek=1 conv=notrunc

# $(DISK_IMG): $(PART_IMG) $(CORE_IMG)
# 	@echo " [ASM]   Assembling disk..."
# 	@$(DD) if=/dev/zero of=$@ bs=1M count=$(DISK_SIZE_MB)
# 	@echo "2048,,83,*" | sfdisk $@ > /dev/null 2>&1
# 	@$(DD) if=$(PART_IMG) of=$@ bs=512 seek=$(SECTOR_OFF) conv=notrunc
# 	@$(DD) if=$(GRUB_LIB)/boot.img of=$@ bs=446 count=1 conv=notrunc
# 	@$(DD) if=$(CORE_IMG) of=$@ bs=512 seek=1 conv=notrunc
