BASEROM = baserom.z64
TARGET = pokestadium
NON_MATCHING ?= 0
RUN_CC_CHECK ?= 1
WERROR ?= 0

# Fail early if baserom does not exist
ifeq ($(wildcard $(BASEROM)),)
$(error Baserom `$(BASEROM)' not found.)
endif

UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Linux)
  HOST_OS := linux
else ifeq ($(UNAME_S),Darwin)
  HOST_OS := macos
else
  $(error Unsupported host/building OS <$(UNAME_S)>)
endif

FRAGMENT_DIRS :=    \
    fragments/1/    \
    fragments/2/    \
    fragments/3/    \
    fragments/4/    \
    fragments/5/    \
    fragments/6/    \
    fragments/7/    \
    fragments/8/    \
    fragments/9/    \
    fragments/10/   \
    fragments/11/   \
    fragments/12/   \
    fragments/13/   \
    fragments/14/   \
    fragments/15/   \
    fragments/16/   \
    fragments/17/   \
    fragments/18/   \
    fragments/19/   \
    fragments/20/   \
    fragments/21/   \
    fragments/22/   \
    fragments/23/   \
    fragments/24/   \
    fragments/25/   \
    fragments/26/   \
    fragments/27/   \
    fragments/28/   \
    fragments/29/   \
    fragments/30/   \
    fragments/31/   \
    fragments/32/   \
    fragments/33/   \
    fragments/34/   \
    fragments/35/   \
    fragments/36/   \
    fragments/37/   \
    fragments/38/   \
    fragments/39/   \
    fragments/40/   \
    fragments/41/   \
    fragments/42/   \
    fragments/43/   \
    fragments/44/   \
    fragments/45/   \
    fragments/46/   \
    fragments/47/   \
    fragments/48/   \
    fragments/49/   \
    fragments/50/   \
    fragments/51/   \
    fragments/52/   \
    fragments/53/   \
    fragments/54/   \
    fragments/55/   \
    fragments/56/   \
    fragments/57/   \
    fragments/58/   \
    fragments/59/   \
    fragments/60/   \
    fragments/61/   \
    fragments/62/   \
    fragments/63/   \
    fragments/64/   \
    fragments/65/   \
    fragments/66/   \
    fragments/67/   \
    fragments/68/   \
    fragments/69/   \
    fragments/70/   \
    fragments/71/   \
    fragments/72/   \
    fragments/73/   \
    fragments/74/   \
    fragments/75/   \
    fragments/76/   \
    fragments/77/

BUILD_DIR := build
ROM := $(TARGET).z64
ELF := $(BUILD_DIR)/$(TARGET).elf
LD_SCRIPT := $(TARGET).ld
LD_MAP := $(BUILD_DIR)/$(TARGET).map
# TODO: Recursively make fragment folders correctly.
ASM_DIRS := asm asm/os asm/libleo asm/libultra asm/libultra/os asm/libultra/io asm/libultra/gu asm/libultra/libc asm/libultra/al asm/data
DATA_DIRS := bin assets
SRC_DIRS := $(shell find src -type d)

# Append FRAGMENT_DIRS to each dirs.
ASM_DIRS += $(addprefix asm/,$(FRAGMENT_DIRS))
ASM_DIRS += $(addprefix asm/data/,$(FRAGMENT_DIRS))
DATA_DIRS += $(addprefix assets/,$(FRAGMENT_DIRS))

C_FILES := $(foreach dir,$(SRC_DIRS),$(wildcard $(dir)/*.c))
S_FILES := $(foreach dir,$(ASM_DIRS),$(wildcard $(dir)/*.s))
DATA_FILES := $(foreach dir,$(DATA_DIRS),$(wildcard $(dir)/*.bin))

# Object files
O_FILES := $(foreach file,$(C_FILES),$(BUILD_DIR)/$(file:.c=.c.o)) \
           $(foreach file,$(S_FILES),$(BUILD_DIR)/$(file:.s=.s.o)) \
           $(foreach file,$(DATA_FILES),$(BUILD_DIR)/$(file:.bin=.bin.o)) \

DECOMP_C_OBJS := $(filter %.c.o,$(filter-out $(BUILD_DIR)/src/libultra%,$(O_FILES)))
DECOMP_POKE_STADIUM := $(filter %.c.o,$(filter-out $(BUILD_DIR)/src/libleo%,$(DECOMP_C_OBJS)))
DECOMP_LIBLEO := $(filter $(BUILD_DIR)/src/libleo/%.c.o,$(O_FILES))

DEP_FILES := $(O_FILES:.o=.d) $(DECOMP_C_OBJS:.o=.asmproc.d)

SPLAT_YAML := splat.yaml
SPLAT = $(PYTHON) tools/n64splat/split.py $(SPLAT_YAML)

##################### Compiler Options #######################
findcmd = $(shell type $(1) >/dev/null 2>/dev/null; echo $$?)

ifeq ($(call findcmd,mips-linux-gnu-ld),0)
  CROSS := mips-linux-gnu-
else ifeq ($(call findcmd,mips64-elf-ld),0)
  CROSS := mips64-elf-
else ifeq ($(call findcmd,mips-elf-ld),0)
  CROSS := mips-elf-
else
  $(error Missing cross compilation toolchain)
endif

AS = $(CROSS)as
LD = $(CROSS)ld
OBJDUMP = $(CROSS)objdump
OBJCOPY = $(CROSS)objcopy
ifeq ($(HOST_OS),macos)
  CPP := clang -E -P -x c
else
  CPP := cpp
endif

#CC         := $(QEMU_IRIX) -L tools/ido7.1_compiler tools/ido7.1_compiler/usr/bin/cc
#CC_OLD     := $(QEMU_IRIX) -L tools/ido5.3_compiler tools/ido5.3_compiler/usr/bin/cc

CC = tools/ido_recomp/$(HOST_OS)/7.1/cc
CC_OLD = tools/ido_recomp/$(HOST_OS)/5.3/cc
ASMPROC = python3 tools/asmproc/build.py
ASMPROC_FLAGS :=

MIPS_VERSION := -mips2

OPTFLAGS := -O2

OBJDUMP_FLAGS := -d -r -z -Mreg-names=32

# include locations
INC_DIRS := include include/PR include/audio .
IINCS := $(foreach d,$(INC_DIRS),-I$d)
# defines for SGI IDO
CDEFS := -D_LANGUAGE_C -DF3DEX_GBI_2 -DNDEBUG 

ifneq ($(RUN_CC_CHECK),0)
  CHECK_WARNINGS := -Wall -Wextra -Wno-unknown-pragmas
  CHECK_DEFS := -D_MIPS_SZLONG=32 -DNON_MATCHING
  CHECK_FLAGS := -fno-builtin -funsigned-char -fdiagnostics-color -std=gnu89 $(CDEFS) $(CHECK_DEFS) $(IINCS) -nostdinc
  CC_CHECK := gcc -fsyntax-only

  ifeq ($(HOST_OS),linux)
    CC_CHECK += -m32
  endif

  ifneq ($(WERROR),0)
    CHECK_WARNINGS += -Werror
  endif
else
  CC_CHECK := @:
endif

######################## Flags #############################

ASFLAGS = -EB -mtune=vr4300 -march=vr4300 $(IINCS) -32

# we support Microsoft extensions such as anonymous structs, which the compiler does support but warns for their usage. Surpress the warnings with -woff.
CFLAGS  = -G 0 -non_shared -Xfullwarn -Xcpluscomm $(IINCS) -Wab,-r4300_mul $(CDEFS) -woff 649,838,712,807 $(MIPS_VERSION)

LDFLAGS = -T undefined_syms.txt -T undefined_syms_auto.txt -T undefined_funcs_auto.txt -T $(BUILD_DIR)/$(LD_SCRIPT) -Map $(BUILD_DIR)/$(TARGET).map --no-check-sections


######################## Targets #############################

$(foreach dir,$(SRC_DIRS) $(ASM_DIRS) $(DATA_DIRS) $(COMPRESSED_DIRS) $(MAP_DIRS) $(BGM_DIRS),$(shell mkdir -p build/$(dir)))

# The reimplementations of the string functions need to treat char as signed.
build/src/hal_libc.c.o: CFLAGS += -signed

# Libultra O1 files
build/src/libultra/os/sendmesg.c.o: OPTFLAGS := -O1
build/src/libultra/os/stopthread.c.o: OPTFLAGS := -O1
build/src/libultra/os/recvmesg.c.o: OPTFLAGS := -O1
build/src/libultra/os/destroythread.c.o: OPTFLAGS := -O1
build/src/libultra/os/dequeuethread.c.o: OPTFLAGS := -O1
build/src/libultra/os/createthread.c.o: OPTFLAGS := -O1
build/src/libultra/os/virtualtophysical.c.o: OPTFLAGS := -O1
build/src/libultra/os/initialize.c.o: OPTFLAGS := -O1
build/src/libultra/os/epilinkhandle.c.o: OPTFLAGS := -O1
build/src/libultra/os/getmemsize.c.o: OPTFLAGS := -O1
build/src/libultra/os/seteventmesg.c.o: OPTFLAGS := -O1
build/src/libultra/os/createmesgqueue.c.o: OPTFLAGS := -O1
build/src/libultra/os/jammesg.c.o: OPTFLAGS := -O1
build/src/libultra/os/setthreadpri.c.o: OPTFLAGS := -O1
build/src/libultra/os/gettime.c.o: OPTFLAGS := -O1
build/src/libultra/os/timerintr.c.o: OPTFLAGS := -O1
build/src/libultra/os/setglobalintmask.c.o: OPTFLAGS := -O1
build/src/libultra/os/resetglobalintmask.c.o: OPTFLAGS := -O1
build/src/libultra/os/settime.c.o: OPTFLAGS := -O1
build/src/libultra/os/yieldthread.c.o: OPTFLAGS := -O1
build/src/libultra/os/settimer.c.o: OPTFLAGS := -O1
build/src/libultra/os/startthread.c.o: OPTFLAGS := -O1
build/src/libultra/os/sethwinterrupt.c.o: OPTFLAGS := -O1
build/src/libultra/io/pigetcmdq.c.o: OPTFLAGS := -O1
build/src/libultra/io/sptask.c.o: OPTFLAGS := -O1
build/src/libultra/io/controller.c.o: OPTFLAGS := -O1
build/src/libultra/io/conteeplongread.c.o: OPTFLAGS := -O1
build/src/libultra/io/viblack.c.o: OPTFLAGS := -O1
build/src/libultra/io/visetmode.c.o: OPTFLAGS := -O1
build/src/libultra/io/virepeatline.c.o: OPTFLAGS := -O1
build/src/libultra/io/pfsisplug.c.o: OPTFLAGS := -O1
build/src/libultra/io/epidma.c.o: OPTFLAGS := -O1
build/src/libultra/io/dpctr.c.o: OPTFLAGS := -O1
build/src/libultra/io/conteeplongwrite.c.o: OPTFLAGS := -O1
build/src/libultra/io/si.c.o: OPTFLAGS := -O1
build/src/libultra/io/sp.c.o: OPTFLAGS := -O1
build/src/libultra/io/viswapbuf.c.o: OPTFLAGS := -O1
build/src/libultra/io/sptaskyielded.c.o: OPTFLAGS := -O1
build/src/libultra/io/vi.c.o: OPTFLAGS := -O1
build/src/libultra/io/visetevent.c.o: OPTFLAGS := -O1
build/src/libultra/io/aisetnextbuf.c.o: OPTFLAGS := -O1
build/src/libultra/io/ai.c.o: OPTFLAGS := -O1
build/src/libultra/io/vigetcurrframebuf.c.o: OPTFLAGS := -O1
build/src/libultra/io/spsetpc.c.o: OPTFLAGS := -O1
build/src/libultra/libc/ll.c.o: OPTFLAGS := -O1
build/src/libultra/libc/ll.c.o: MIPS_VERSION := -mips3 -32
build/src/libultra/libc/llcvt.c.o: OPTFLAGS := -O1
build/src/libultra/libc/llcvt.c.o: MIPS_VERSION := -mips3 -32

# cheap hack. TODO: Make the override better
build/src/libultra/io/gbpakpower.c.o: OPTFLAGS += -Wo,-loopunroll,0
build/src/libultra/io/gbpakinit.c.o: OPTFLAGS += -Wo,-loopunroll,0
build/src/libultra/io/gbpakselectbank.c.o: OPTFLAGS += -Wo,-loopunroll,0

build/src/C030.c.o: OPTFLAGS += -Wo,-loopunroll,0

# Libultra misc
build/src/libultra/gu/scale.c.o: CC := $(CC_OLD)
build/src/libultra/gu/scale.c.o: OPTFLAGS := -O3
build/src/libultra/gu/translate.c.o: CC := $(CC_OLD)
build/src/libultra/gu/translate.c.o: OPTFLAGS := -O3
build/src/libultra/gu/mtxcatf.c.o: CC := $(CC_OLD)
build/src/libultra/gu/mtxcatf.c.o: OPTFLAGS := -O3
build/src/libultra/gu/mtxcatl.c.o: CC := $(CC_OLD)
build/src/libultra/gu/mtxcatl.c.o: OPTFLAGS := -O3
build/src/libultra/gu/rotateRPY.c.o: CC := $(CC_OLD)
build/src/libultra/gu/rotateRPY.c.o: OPTFLAGS := -O3
build/src/libultra/gu/rotate.c.o: CC := $(CC_OLD)
build/src/libultra/gu/rotate.c.o: OPTFLAGS := -O3
build/src/libultra/gu/ortho.c.o: CC := $(CC_OLD)
build/src/libultra/gu/ortho.c.o: OPTFLAGS := -O3
build/src/libultra/gu/sinf.c.o: CC := $(CC_OLD)
build/src/libultra/gu/cosf.c.o: CC := $(CC_OLD)
build/src/libultra/gu/random.c.o: CC := $(CC_OLD)
build/src/libultra/libc/xprintf.c.o: CC := $(CC_OLD)
build/src/libultra/libc/xprintf.c.o: OPTFLAGS := -O3
build/src/libultra/libc/xldtob.c.o: CC := $(CC_OLD)
build/src/libultra/libc/xldtob.c.o: OPTFLAGS := -O3
build/src/libultra/libc/xlitob.c.o: CC := $(CC_OLD)
build/src/libultra/libc/xlitob.c.o: OPTFLAGS := -O3
build/src/libultra/libc/string.c.o: CC := $(CC_OLD)
build/src/libultra/libc/string.c.o: OPTFLAGS := -O3
build/src/libultra/io/controller.c.o: CC := $(CC_OLD)
build/src/libultra/io/contreaddata.c.o: CC := $(CC_OLD)
build/src/libultra/io/devmgr.c.o: CC := $(CC_OLD)
build/src/libultra/os/initialize.c.o: CC := $(CC_OLD)
build/src/libultra/io/pfsgetstatus.c.o: CC := $(CC_OLD)
build/src/libultra/io/epiread.c.o: CC := $(CC_OLD)
build/src/libultra/al/reverb.c.o: CC := $(CC_OLD)
build/src/libultra/al/bnkf.c.o: CC := $(CC_OLD)
build/src/libultra/al/bnkf.c.o: OPTFLAGS := -O3
build/src/libultra/al/load.c.o: CC := $(CC_OLD)
build/src/libultra/al/load.c.o: OPTFLAGS := -O3
build/src/libultra/al/synthesizer.c.o: CC := $(CC_OLD)
build/src/libultra/al/drvrNew.c.o: CC := $(CC_OLD)
build/src/libultra/al/mainbus.c.o: CC := $(CC_OLD)
build/src/libultra/al/mainbus.c.o: OPTFLAGS := -O3
build/src/libultra/al/auxbus.c.o: CC := $(CC_OLD)
build/src/libultra/al/auxbus.c.o: OPTFLAGS := -O3
build/src/libultra/al/env.c.o: CC := $(CC_OLD)
build/src/libultra/al/resample.c.o: CC := $(CC_OLD)
build/src/libultra/al/resample.c.o: OPTFLAGS := -O3
build/src/libultra/al/save.c.o: CC := $(CC_OLD)
build/src/libultra/al/heapalloc.c.o: CC := $(CC_OLD)
build/src/libultra/gu/mtxutil.c.o: CC := $(CC_OLD)
build/src/libultra/gu/mtxutil.c.o: OPTFLAGS := -O3
build/src/libultra/io/pfsisplug.c.o: CC := $(CC_OLD)
build/src/libultra/io/conteepprobe.c.o: CC := $(CC_OLD)
build/src/libultra/io/conteepwrite.c.o: CC := $(CC_OLD)
build/src/libultra/io/conteepread.c.o: CC := $(CC_OLD)
build/src/libultra/io/contpfs.c.o: CC := $(CC_OLD)
build/src/libultra/io/viswapcontext.c.o: CC := $(CC_OLD)
build/src/libultra/io/contramwrite.c.o: CC := $(CC_OLD)
build/src/libultra/io/contramread.c.o: CC := $(CC_OLD)
build/src/libultra/io/crc.c.o: CC := $(CC_OLD)
build/src/libultra/libc/ldiv.c.o: CC := $(CC_OLD)
build/src/libultra/io/vimgr.c.o: CC := $(CC_OLD)
build/src/libultra/io/visetxscale.c.o: CC := $(CC_OLD)
build/src/libultra/io/visetxscale.c.o: OPTFLAGS := -O1
build/src/libultra/io/visetyscale.c.o: CC := $(CC_OLD)
build/src/libultra/io/visetyscale.c.o: OPTFLAGS := -O1
build/src/libultra/al/synallocfx.c.o: CC := $(CC_OLD)
build/src/libultra/al/synallocfx.c.o: OPTFLAGS := -O3

build/src/libultra/io/gbpakcheckconnector.c.o: CC := $(CC_OLD)
build/src/libultra/io/gbpakreadid.c.o: CC := $(CC_OLD)
build/src/libultra/io/gbpakreadwrite.c.o: CC := $(CC_OLD)
build/src/libultra/io/gbpakselectbank.c.o: CC := $(CC_OLD)

# run ASM-processor on non-libultra source files
$(DECOMP_POKE_STADIUM): CC := $(ASMPROC) $(ASMPROC_FLAGS) $(CC) -- $(AS) $(ASFLAGS) --
$(DECOMP_LIBLEO): CC := $(ASMPROC) $(ASMPROC_FLAGS) $(CC_OLD) -- $(AS) $(ASFLAGS) --

# turn off syntax checking errors for libultra
build/src/libultra/al/%.c.o: CHECK_WARNINGS := -w
build/src/libultra/gu/%.c.o: CHECK_WARNINGS := -w
build/src/libultra/io/%.c.o: CHECK_WARNINGS := -w
build/src/libultra/libc/%.c.o: CHECK_WARNINGS := -w
build/src/libultra/os/%.c.o: CHECK_WARNINGS := -w

######################## Build #############################

default: all

LD_SCRIPT = $(TARGET).ld

rom: $(ROM)

all: $(BUILD_DIR) $(BUILD_DIR)/$(ROM) verify

distclean:
	rm -rf asm bin assets $(BUILD_DIR) undefined_syms_auto.txt undefined_funcs_auto.txt

clean:
	rm -rf $(BUILD_DIR)

submodules:
	git submodule update --init --recursive

split:
	rm -rf $(DATA_DIRS) $(ASM_DIRS) && ./tools/n64splat/split.py $(SPLAT_YAML)

setup: distclean submodules split

expected: 
	$(RM) -r expected/
	mkdir -p expected/
	cp -r $(BUILD_DIR) expected/$(BUILD_DIR)

$(BUILD_DIR):
	echo $(C_FILES)
	mkdir $(BUILD_DIR)

$(BUILD_DIR)/$(LD_SCRIPT): $(LD_SCRIPT)
	@mkdir -p $(shell dirname $@)
	$(CPP) -P -DBUILD_DIR=$(BUILD_DIR) -o $@ $<

$(BUILD_DIR)/$(TARGET).bin: $(BUILD_DIR)/$(TARGET).elf
	$(OBJCOPY) $< $@ -O binary --gap-fill 0xFF

$(BUILD_DIR)/$(TARGET).elf: $(O_FILES) $(BUILD_DIR)/$(LD_SCRIPT)
	@$(LD) $(LDFLAGS) -o $@

$(BUILD_DIR)/%.c.o: %.c
	$(CC_CHECK) $(CHECK_FLAGS) $(CHECK_WARNINGS) -MMD -MP -MT $@ -MF $(@:.o=.d) $<
	$(CC) -c $(CFLAGS) $(OPTFLAGS) -o $@ $<

$(BUILD_DIR)/src/libultra/libc/ll.c.o: src/libultra/libc/ll.c
	$(CC) -c $(CFLAGS) $(OPTFLAGS) -o $@ $<
	python3 tools/set_o32abi_bit.py $@
	@$(OBJDUMP) $(OBJDUMP_FLAGS) $@ > $(@:.o=.s)

$(BUILD_DIR)/src/libultra/libc/llcvt.c.o: src/libultra/libc/llcvt.c
	$(CC) -c $(CFLAGS) $(OPTFLAGS) -o $@ $<
	python3 tools/set_o32abi_bit.py $@
	@$(OBJDUMP) $(OBJDUMP_FLAGS) $@ > $(@:.o=.s)

$(BUILD_DIR)/%.s.o: %.s
	iconv --from UTF-8 --to EUC-JP $^ | $(AS) $(ASFLAGS) -o $@

$(BUILD_DIR)/%.bin.o: %.bin
	$(LD) -r -b binary -o $@ $<

# final z64 updates checksum
$(BUILD_DIR)/$(ROM): $(BUILD_DIR)/$(TARGET).bin
	@cp $< $@

verify: $(BUILD_DIR)/$(ROM)
	md5sum -c checksum.md5

.PHONY: all clean distclean default split setup

print-% : ; $(info $* is a $(flavor $*) variable set to [$($*)]) @true

-include $(DEP_FILES)
