SHELL = /bin/sh
SRCDIR := $(realpath $(dir $(lastword $(MAKEFILE_LIST))))
PYPACKAGES := debugpy pandas pypng requests openpyxl
BUILD_LANGS := japanese english french german italian spanishEU spanishLA korean chineseSI chineseTR portugueseBR
BUILD_TYPES := release debug
BUILD_XLSXS := remote local

UID := $(shell id -u)
GID := $(shell id -g)
export UID
export GID

.ONESHELL:

default: help

.PHONY: help
help: # Display this help message
	@tabs 2
	@printf "\033[1;4mTargets\033[0m\n"	
# shows help for all targets that follow the format of "target: #", where target includes a-z, A-Z, 0-9, and _ characters
	@grep -E '^[a-zA-Z0-9_-]+: #'  Makefile | while read -r l; do printf "\t\033[1;32m$$(echo $$l | cut -f 1 -d':')\033[00m:$$(echo $$l | cut -f 2- -d'#')\n"; done

# if [ $(id | grep -c docker) = 0 ]; then
# $1 = command name
# $2 = package name
define check_package
	@if [ "$(2)" = "docker" ]; then
		if ! command -v $(1) >/dev/null 2>&1; then
			printf "\033[1;31mDocker is not installed!\n"
			printf "Please install Docker through your package manager.\033[0m\n"
			exit
		elif [ "$$(uname)" = "Darwin" ]; then
			echo "macOS detected; skipping group check."
		elif [ "$$(id | grep -c docker)" = 0 ]; then
			printf "\033[1;31mUser is not in docker group!\033[0m\n"
			if [ "$$(cat /etc/group | grep -c docker)" = 0 ]; then
				echo "Creating docker group..."
				sudo groupadd docker
			fi
			echo "Adding user to docker group..."
			sudo usermod -aG docker $$USER
			echo
			printf "\033[1;31mPlease log out and log back in for this to take effect.\033[0m\n"
			exit
		else
			echo "Docker is installed and user is in docker group."
		fi
	else
		if ! command -v $(1) >/dev/null 2>&1; then
			printf "\033[1;31m$(2) not found. Installing...\033[0m\n"
			if [ "$(2)" = "rgbds" ]; then
				mkdir -p rgbds && curl -L https://github.com/gbdev/rgbds/releases/download/v1.0.2+hotfix/rgbds-linux-x86_64.tar.xz | tar -xJ -C rgbds && cd rgbds && ./install.sh && cd .. && rm -rf rgbds
			else
				apt update && apt install -y $(1)
			fi
		else
			echo "$(2) found."
		fi
	fi
endef

.PHONY: setup
setup: # Setup all required dependencies
	@if [ -f /.dockerenv ] || [ -n "$$GITPOD" ]; then
		if [ -z "$$IS_PTGB" ]; then
			printf "\033[1;31mRunning inside an non-Poké Transporter GB Docker shell!\n"
			printf "Please run this on the host machine or inside the Poké Transporter GB's Docker shell.\033[0m"
			exit
		else
			echo "Running in Docker shell; continuing..."
		fi
	else
		echo "Running outside of Docker shell. Checking Docker installation..."
		$(call check_package,docker,docker)
		if [ "$$(docker container ls -f "name=ptgb" --format "{{.Names}}")" = "ptgb" ]; then
			echo "Docker container found and running."
		elif [ "$$(docker container ls -af "name=ptgb" --format "{{.Names}}")" = "ptgb" ]; then
			echo "Docker container found; starting..."
			docker start ptgb >/dev/null
		else
			echo "Docker container does not exist."
			echo "Checking status of docker buildx..."
			if docker buildx version >/dev/null 2>&1; then
				echo "buildx installed. Continuing..."
			else
				printf "\033[1;31mbuildx is not installed!\n"
				printf "\033[1;31mPlease install through your package manager.\n"
				exit
			fi
			echo "Creating Docker image..."
			mkdir -p $(SRCDIR)/docker-build && cd $(SRCDIR)/docker-build
			DOCKER_BUILDKIT=1 docker build --platform linux/amd64 -t ptgb-builder:latest --build-arg UID=$$UID --build-arg GID=$$GID -f $(SRCDIR)/Dockerfile $(SRCDIR)/docker-build
			rm -rf $(SRCDIR)/docker-build
			echo
			echo "Creating Docker container..."
			docker container create --platform linux/amd64 -w /ptgb -v $(SRCDIR):/ptgb --name ptgb -it ptgb-builder:latest
			echo "Starting Docker container..."
			docker start ptgb >/dev/null
		fi
		echo
		docker exec ptgb make --no-print-directory -C /ptgb _setup
		echo
		printf "\033[1;32mEverything is set up!\033[0m\n"
	fi

.PHONY: _setup
_setup:
	@echo "Checking Docker container's packages..."
# Python
	$(call check_package,python3,python3)
	
	for package in $(PYPACKAGES); do
		if pip show "$$package" > /dev/null 2>&1; then
			echo "$$package found."
		else
			echo "Installing $$package"
			pip install --root-user-action=ignore "$$package"
		fi
	done

# rgbds
	$(call check_package,rgbasm,rgbds)
# jq
	$(call check_package,jq,jq)

# Install the formatter in existing containers as well as new images.
	if ! command -v clang-format-19 >/dev/null 2>&1; then
		sudo apt-get update && sudo apt-get install -y clang-format-19 || exit $$?
	fi

.PHONY: format check-format
format: FORMAT_FLAGS := -i
check-format: FORMAT_FLAGS := --dry-run --Werror
format: # Format only PTGB source and include folders
check-format: # Check only PTGB formatting without changing files
format check-format:
	@find source include \
		-type f \( -name '*.c' -o -name '*.cc' -o -name '*.cpp' -o -name '*.cxx' \
		-o -name '*.h' -o -name '*.hh' -o -name '*.hpp' -o -name '*.hxx' \) -print0 | \
		xargs -0 -r clang-format-19 --style=file --fallback-style=none $(FORMAT_FLAGS)

# $1 = command name
# $2 = package name
define check_package_host
	@if ! command -v $(1) >/dev/null 2>&1; then
		printf "\033[1;31m$(2) is not installed!\n"
		printf "Please install $(2) through your package manager.\033[0m\n"
		exit
	fi
endef

.PHONY: setup_no_docker
# hidden legacy build option
setup_no_docker:
	$(call check_package_host,python3,python3)
	@if [ ! -e "$(SRCDIR)/.venv/bin/activate" ]; then
		echo "venv not found. Creating venv..."
		python3 -m venv .venv
	fi
	@if [ -z "$$VIRTUAL_ENV" ]; then
		echo "Activating venv..."
		source $(SRCDIR)/.venv/bin/activate
	fi

	@for package in $(PYPACKAGES); do
		if pip show "$$package" > /dev/null 2>&1; then
			echo "$$package found."
		else
			echo "$$package is not installed. Installing..."
			pip install "$$package"
		fi
	done
	$(call check_package_host,rgbasm,rgbds)

	@printf "\033[1;32mEverything is set up!\033[0m\n"

.PHONY: configure
configure: # Specify ROM Language, ROM Type, and Text Source
	$(call check_package_host,dialog,dialog)
	$(call check_package_host,jq,jq)
	
# generate default options.json (based on makefile vars) if it doesn't already exist
	@touch $(SRCDIR)/options.json

	set -- $(BUILD_LANGS)
	langs=$$(printf '%s\n' "$$@" | jq -R -s 'split("\n")[:-1]')

	set -- $(BUILD_TYPES)
	types=$$(printf '%s\n' "$$@" | jq -R -s 'split("\n")[:-1]')

	set -- $(BUILD_XLSXS)
	xlsxs=$$(printf '%s\n' "$$@" | jq -R -s 'split("\n")[:-1]')

	jq -n \
		--argjson langs "$$langs" \
		--argjson types "$$types" \
		--argjson xlsxs "$$xlsxs" \
		'{
			BUILD_LANGS: $$langs,
			BUILD_TYPES: $$types,
			BUILD_XLSXS: $$xlsxs,
			selected: {
				lang: "",
				type: "",
				xlsx: ""
			}
		}' > "$(SRCDIR)/options.json"

	menu() {
		title="$$1"
		json="$$2"
		array_key="$$3"

		menu_args=""
		i=1

		while IFS= read -r item; do
			menu_args="$$menu_args $$i $$item"
			i=$$((i + 1))
		done <<-EOF
			$$(jq -r ".$${array_key}[]" "$$json")
		EOF
		choice=$$(dialog --stdout \
			--menu "$$title" 0 0 15 \
			$$menu_args)

		status=$$?

		case $$status in
			0)
				jq -r ".$${array_key}[$$((choice - 1))]" "$$json"
				return 0
				;;
			1)
				printf "\033[1;31mUser canceled dialog.\033[0m\n" >&2
				return 1
				;;
			255)
				printf "\033[1;31mUser pressed escape.\033[0m\n" >&2
				return 1
				;;
			*)
				printf "\033[1;31mUnknown error occurred.\033[0m\n" >&2
				return 1
				;;
		esac
	}

# prompt user for selections
	if ! LANG=$$(menu "ROM Language" options.json "BUILD_LANGS"); then
		exit 1
	fi
	if ! TYPE=$$(menu "ROM Type" options.json "BUILD_TYPES"); then
		exit 1
	fi
	if ! XLSX=$$(menu "Text Source" options.json "BUILD_XLSXS"); then
		exit 1
	fi	
	clear
	tmp=$$(mktemp)
# store selections in options.json
	jq \
		--arg lang "$$LANG" \
		--arg type "$$TYPE" \
		--arg xlsx "$$XLSX" \
		'.selected.lang = $$lang | .selected.type = $$type | .selected.xlsx = $$xlsx' \
		$(SRCDIR)/options.json > "$$tmp" && mv "$$tmp" $(SRCDIR)/options.json

	@printf "\033[1;32mBuild configured!\033[0m\n"

.PHONY: clean
clean: # Cleans the build directory
	@echo "Starting Docker container..."
	@docker start ptgb >/dev/null
	@docker exec ptgb make -f /ptgb/container.mk -C /ptgb SRCDIR="/ptgb" PTGB_PROJECT_NAME="$(notdir $(SRCDIR))" clean

.PHONY: clean_no_docker
# hidden legacy build option
clean_no_docker:
	@echo "Cleaning..."
	make -f $(SRCDIR)/container.mk clean SRCDIR="$(SRCDIR)"

.PHONY: build
build: # Builds the ROM
	@echo "Starting Docker container..."
	@docker start ptgb >/dev/null
	@docker exec ptgb make -f /ptgb/container.mk -C /ptgb SRCDIR="/ptgb" PTGB_PROJECT_NAME="$(notdir $(SRCDIR))" all

.PHONY: clean_no_docker
# hidden legacy build option
build_no_docker:
	@if [ -z "$$VIRTUAL_ENV" ]; then
		echo "Activating venv..."
		source $(SRCDIR)/.venv/bin/activate
	fi
	@echo "Building..."
	make -f $(SRCDIR)/container.mk all SRCDIR="$(SRCDIR)"
