# bmp2tile-mac -- plain Makefile build, producing two binaries:
#
#   bmp2tile      the GUI app (SDL3 + Dear ImGui). Also supports command-
#                 line mode -- run it with arguments and it behaves like
#                 bmp2tile_cli, with no args it launches the GUI.
#   bmp2tile_cli  command-line only, with ZERO dependency on SDL or
#                 ImGui -- it doesn't link either library at all, since
#                 its own entry point (src/main_cli.cpp) never touches
#                 App.h. `make cli` builds only this one, and doesn't
#                 need SDL3 installed at all, and needs no network access.
#
# stb_image.h is vendored in third_party/stb/ -- no fetch needed for
# either binary.
#
# `make gui` needs network access on first run, to `git clone` Dear ImGui
# (docking branch) into third_party/imgui/. `make cli` never needs
# network access at all.
#
# Requires for `bmp2tile`: Xcode command line tools, SDL3 (`brew install
# sdl3`), git. Requires for `bmp2tile_cli`: just Xcode command line tools.

APP_NAME     := bmp2tile
CLI_APP_NAME := bmp2tile_cli
BUILD_DIR    := build
THIRD_PARTY  := third_party
IMGUI_DIR    := $(THIRD_PARTY)/imgui
STB_DIR      := $(THIRD_PARTY)/stb

CXX := clang++

# -MMD -MP makes the compiler emit a .d file next to each .o listing the
# headers that .o actually included, which is pulled back in at the bottom
# of this file. Without it, make only knows an .o depends on its .cpp, so
# editing a header (e.g. adding a field to App.h) rebuilds only the .cpp
# files that themselves changed -- leaving every other .o addressing that
# struct's members at their old offsets. That's an ODR violation, and it
# shows up as impossible-looking memory corruption at runtime, not as a
# build error. -MP adds phony targets for the headers so deleting or
# renaming one doesn't wedge the build with "No rule to make target".
DEPFLAGS := -MMD -MP

# Flags for the SDL/ImGui-free CLI build.
CXXFLAGS_CLI := -std=c++20 -Wall -Wextra -O2 -Isrc -I$(STB_DIR) $(DEPFLAGS)

# Flags for the GUI build -- everything CLI has, plus ImGui/SDL3.
# Deliberately `=` (lazy/recursive), not `:=`: with immediate assignment,
# the pkg-config shell call below would run the moment the Makefile is
# parsed, regardless of which target was actually requested -- meaning
# `make cli` would print scary "sdl3 not found" errors on a machine
# without SDL3 installed at all, even though the CLI build never needs
# it. Lazy assignment means this only runs when a GUI recipe actually
# expands the variable.
CXXFLAGS_GUI = $(CXXFLAGS_CLI) -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends $(shell pkg-config --cflags sdl3)
LDFLAGS_GUI  = $(shell pkg-config --libs sdl3)

# --- sources -------------------------------------------------------------

CORE_SOURCES := \
    src/Cli.cpp \
    src/core/Image.cpp \
    src/core/TileSet.cpp \
    src/core/Palette.cpp \
    src/core/compressors/Zx7.cpp \
    src/core/compressors/PsGaiden.cpp \
    src/core/compressors/Stc0.cpp \
    src/core/compressors/Stc4.cpp \
    src/core/compressors/Stm.cpp \
    src/core/compressors/ApLib.cpp \
    src/core/compressors/RawBpp.cpp

GUI_ONLY_SOURCES := \
    src/main.cpp \
    src/App.cpp \
    src/Theme.cpp \
    src/ui/SourceTab.cpp \
    src/ui/TilesTab.cpp \
    src/ui/TilemapTab.cpp \
    src/ui/PaletteTab.cpp \
    src/ui/CompressionFormatCombo.cpp \
    $(IMGUI_DIR)/imgui.cpp \
    $(IMGUI_DIR)/imgui_draw.cpp \
    $(IMGUI_DIR)/imgui_tables.cpp \
    $(IMGUI_DIR)/imgui_widgets.cpp \
    $(IMGUI_DIR)/backends/imgui_impl_sdl3.cpp \
    $(IMGUI_DIR)/backends/imgui_impl_sdlrenderer3.cpp

CLI_ONLY_SOURCES := \
    src/main_cli.cpp

GUI_OBJECTS := $(patsubst %.cpp,$(BUILD_DIR)/gui/%.o,$(CORE_SOURCES) $(GUI_ONLY_SOURCES))
CLI_OBJECTS := $(patsubst %.cpp,$(BUILD_DIR)/cli/%.o,$(CORE_SOURCES) $(CLI_ONLY_SOURCES))

# One .d per .o, generated by DEPFLAGS during compilation (see above).
DEPFILES := $(GUI_OBJECTS:.o=.d) $(CLI_OBJECTS:.o=.d)

.PHONY: all deps gui cli bundle clean

all: cli gui

# `gui` fetches ImGui first, then re-invokes make in a fresh pass to
# actually build. This two-pass structure matters: Make plans its whole
# dependency graph before running any recipes, so if the object-file
# rules below were evaluated in the *same* pass as the git clone, Make
# would look for e.g. third_party/imgui/imgui.cpp before the clone has
# created it and bail out with "No rule to make target". Recursing into
# a second `make` invocation means that pass only starts planning after
# the clone is done and the file genuinely exists.
#
# `cli` needs no two-pass dance and no network access at all -- stb is
# vendored and ImGui isn't used.
gui: deps
	$(MAKE) $(BUILD_DIR)/$(APP_NAME)

cli: $(BUILD_DIR)/$(CLI_APP_NAME)

# --- fetch third-party sources (imgui only; stb is vendored) ---
deps: $(IMGUI_DIR)/.fetched

$(IMGUI_DIR)/.fetched:
	mkdir -p $(THIRD_PARTY)
	git clone --depth 1 --branch docking https://github.com/ocornut/imgui.git $(IMGUI_DIR)
	touch $@

# --- link ---
$(BUILD_DIR)/$(APP_NAME): $(GUI_OBJECTS)
	$(CXX) $(GUI_OBJECTS) -o $@ $(LDFLAGS_GUI)

$(BUILD_DIR)/$(CLI_APP_NAME): $(CLI_OBJECTS)
	$(CXX) $(CLI_OBJECTS) -o $@

# --- compile ---
$(BUILD_DIR)/gui/%.o: %.cpp
	mkdir -p $(dir $@)
	$(CXX) $(CXXFLAGS_GUI) -c $< -o $@

$(BUILD_DIR)/cli/%.o: %.cpp
	mkdir -p $(dir $@)
	$(CXX) $(CXXFLAGS_CLI) -c $< -o $@

# --- wrap the GUI binary in a minimal .app bundle for double-click launch ---
bundle: gui
	mkdir -p $(BUILD_DIR)/$(APP_NAME).app/Contents/MacOS
	cp $(BUILD_DIR)/$(APP_NAME) $(BUILD_DIR)/$(APP_NAME).app/Contents/MacOS/$(APP_NAME)
	cp Info.plist $(BUILD_DIR)/$(APP_NAME).app/Contents/Info.plist

clean:
	rm -rf $(BUILD_DIR)

# Pull in the generated header dependencies. Wildcard rather than the
# DEPFILES list directly, so a clean tree (no .d files yet) is fine --
# nothing to include on the first build, and the .d files written by that
# build are picked up by every build after it. Must come last.
-include $(wildcard $(DEPFILES))
