.PHONY: nix-add-gcroots clean nix-clean disable-githooks react-native-android react-native-ios react-native-desktop test release _list _fix-node-perms _tmpdir-mk _tmpdir-rm

help: SHELL := /bin/sh
help: ##@other Show this help
	@perl -e '$(HELP_FUN)' $(MAKEFILE_LIST)

# This is a code for automatic help generator.
# It supports ANSI colors and categories.
# To add new item into help output, simply add comments
# starting with '##'. To add category, use @category.
GREEN  := $(shell tput -Txterm setaf 2)
RED    := $(shell tput -Txterm setaf 1)
WHITE  := $(shell tput -Txterm setaf 7)
YELLOW := $(shell tput -Txterm setaf 3)
RESET  := $(shell tput -Txterm sgr0)
HELP_FUN = \
		   %help; \
		   while(<>) { push @{$$help{$$2 // 'options'}}, [$$1, $$3] if /^([a-zA-Z\-]+)\s*:.*\#\#(?:@([a-zA-Z\-]+))?\s(.*)$$/ }; \
		   print "Usage: make [target]\n\nSee STARTING_GUIDE.md for more info.\n\n"; \
		   for (sort keys %help) { \
			   print "${WHITE}$$_:${RESET}\n"; \
			   for (@{$$help{$$_}}) { \
				   $$sep = " " x (32 - length $$_->[0]); \
				   print "  ${YELLOW}$$_->[0]${RESET}$$sep${GREEN}$$_->[1]${RESET}\n"; \
			   }; \
			   print "\n"; \
		   }
HOST_OS := $(shell uname | tr '[:upper:]' '[:lower:]')

# This can come from Jenkins
ifndef BUILD_TAG
export BUILD_TAG := $(shell git rev-parse --short HEAD)
endif

# We don't want to use /run/user/$UID because it runs out of space too easilly
export TMPDIR = /tmp/tmp-status-react-$(BUILD_TAG)
# this has to be specified for both the Node.JS server process and the Qt process
export REACT_SERVER_PORT ?= 5001

# Our custom config is located in nix/nix.conf
export NIX_CONF_DIR = $(PWD)/nix
# Defines which variables will be kept for Nix pure shell, use semicolon as divider
export _NIX_KEEP ?= TMPDIR,BUILD_ENV,STATUS_GO_SRC_OVERRIDE,NIMBUS_SRC_OVERRIDE

# Useful for Andoird release builds
TMP_BUILD_NUMBER := $(shell ./scripts/version/gen_build_no.sh | cut -c1-10)

# MacOS root is read-only, read nix/README.md for details
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin)
export NIX_IGNORE_SYMLINK_STORE=1
endif

#----------------
# Nix targets
#----------------

# WARNING: This has to be located right before the targets
ifdef IN_NIX_SHELL
SHELL := env bash
else
SHELL := ./nix/scripts/shell.sh
endif

shell: ##@prepare Enter into a pre-configured shell
ifndef IN_NIX_SHELL
	@ENTER_NIX_SHELL
else
	@echo "${YELLOW}Nix shell is already active$(RESET)"
endif

nix-repl: SHELL := /bin/sh
nix-repl: ##@nix Start an interactive Nix REPL
	nix repl default.nix

nix-gc: export TARGET := default
nix-gc: ##@nix Garbage collect all packages older than 20 days from /nix/store
	nix-collect-garbage --delete-old --delete-older-than 20d

nix-clean: export TARGET := default
nix-clean: ##@nix Remove all status-react build artifacts from /nix/store
	nix/scripts/clean.sh

nix-purge: SHELL := /bin/sh
nix-purge: ##@nix Completely remove Nix setup, including /nix directory
	nix/scripts/purge.sh

nix-add-gcroots: export TARGET := default
nix-add-gcroots: ##@nix Add Nix GC roots to avoid status-react expressions being garbage collected
	nix/scripts/gcroots.sh

nix-update-gradle: ##@nix Update maven nix expressions based on current gradle setup
	nix/mobile/android/maven-and-npm-deps/maven/generate-nix.sh

nix-update-clojure: export TARGET := clojure
nix-update-clojure: ##@nix Update maven nix expressions based on current clojure setup
	nix/deps/clojure/generate.sh

nix-update-gems: export TARGET := default
nix-update-gems: ##@nix Update Ruby gems in fastlane/Gemfile.lock and fastlane/gemset.nix
	fastlane/update.sh

nix-update-pods: export TARGET := ios
nix-update-pods: ##@nix Update CocoaPods in ios/Podfile.lock
	cd ios && pod update

#----------------
# General targets
#----------------

_fix-node-perms: SHELL := /bin/sh
_fix-node-perms: ##@prepare Fix permissions so that directory can be cleaned
	$(shell test -d node_modules && chmod -R 744 node_modules)
	$(shell test -d node_modules.tmp && chmod -R 744 node_modules.tmp)

_tmpdir-mk: SHELL := /bin/sh
_tmpdir-mk: ##@prepare Create a TMPDIR for temporary files
	@mkdir -p "$(TMPDIR)"
# Make sure TMPDIR exists every time make is called
-include _tmpdir-mk

_tmpdir-rm: SHELL := /bin/sh
_tmpdir-rm: ##@prepare Remove TMPDIR
	rm -fr "$(TMPDIR)"

clean: SHELL := /bin/sh
clean: _fix-node-perms _tmpdir-rm ##@prepare Remove all output folders
	git clean -dxf

watchman-clean: export TARGET := watchman
watchman-clean: ##@prepare Delete repo directory from watchman
	watchman watch-del $${STATUS_REACT_HOME}

pod-install: export TARGET := ios
pod-install: ##@prepare Run 'pod install' to install podfiles and update Podfile.lock
	cd ios && pod install; cd --

update-fleets: ##@prepare Download up-to-date JSON file with current fleets state
	curl -s https://fleets.status.im/ \
		| jq --indent 4 --sort-keys . \
		> resources/config/fleets.json

keystore: export TARGET := keytool
keystore: export KEYSTORE_PATH ?= $(HOME)/.gradle/status-im.keystore
keystore: ##@prepare Generate a Keystore for signing Android APKs
	@./scripts/generate-keystore.sh

#----------------
# Release builds
#----------------
release: release-android release-ios ##@build build release for Android and iOS

release-android: export TARGET := android
release-android: export BUILD_ENV ?= prod
release-android: export BUILD_TYPE ?= nightly
release-android: export BUILD_NUMBER ?= $(TMP_BUILD_NUMBER)
release-android: export KEYSTORE_PATH ?= $(HOME)/.gradle/status-im.keystore
release-android: export ANDROID_ABI_SPLIT ?= false
release-android: export ANDROID_ABI_INCLUDE ?= armeabi-v7a;arm64-v8a;x86
release-android: keystore ##@build build release for Android
	scripts/release-android.sh

release-ios: export TARGET ?= ios
release-ios: export BUILD_ENV ?= prod
release-ios: watchman-clean ##@build build release for iOS release
	@git clean -dxf -f target/ios && \
	$(MAKE) jsbundle-ios && \
	scripts/copy-translations.sh && \
	xcodebuild -workspace ios/StatusIm.xcworkspace -scheme StatusIm -configuration Release -destination 'generic/platform=iOS' -UseModernBuildSystem=N clean archive

release-desktop: export TARGET ?= $(HOST_OS)
release-desktop: ##@build build release for desktop release based on TARGET
	@$(MAKE) jsbundle-desktop && \
	scripts/copy-translations.sh && \
	scripts/build-desktop.sh; \
	$(MAKE) watchman-clean

release-windows-desktop: export TARGET ?= windows
release-windows-desktop: ##@build build release for windows desktop release
	@$(MAKE) jsbundle-desktop && \
	scripts/copy-translations.sh && \
	scripts/build-desktop.sh; \
	$(MAKE) watchman-clean

prod-build-android: jsbundle-android ##@legacy temporary legacy alias for jsbundle-android
	@echo "${YELLOW}This a deprecated target name, use jsbundle-android.$(RESET)"

jsbundle-android: SHELL := /bin/sh
jsbundle-android: export TARGET ?= android
jsbundle-android: export BUILD_ENV ?= prod
jsbundle-android: ##@jsbundle Compile JavaScript and Clojurescript into app directory
	# Call nix-build to build the 'targets.mobile.android.jsbundle' attribute and copy the.js files to the project root
	nix/scripts/build.sh targets.mobile.android.jsbundle && \
	mv result/* ./

prod-build-ios: jsbundle-ios ##@legacy temporary legacy alias for jsbundle-ios
	@echo "${YELLOW}This a deprecated target name, use jsbundle-ios.$(RESET)"

jsbundle-ios: export TARGET ?= ios
jsbundle-ios: export BUILD_ENV ?= prod
jsbundle-ios: ##@jsbundle Compile JavaScript and Clojure into index.ios.js
	yarn shadow-cljs release ios

#--------------
# status-go lib
# -------------

status-go-desktop: SHELL := /bin/sh
status-go-desktop: ##@status-go Compile status-go for desktop app
	nix/scripts/build.sh targets.status-go.desktop

status-go-android: SHELL := /bin/sh
status-go-android: ##@status-go Compile status-go for Android app
	nix/scripts/build.sh targets.status-go.mobile.android

status-go-ios: SHELL := /bin/sh
status-go-ios: ##@status-go Compile status-go for iOS app
	nix/scripts/build.sh targets.status-go.mobile.ios

#--------------
# Clojure REPL
# -------------

_watch-%: ##@watch Start development for device
	$(eval SYSTEM := $(word 2, $(subst -, , $@)))
	$(eval DEVICE := $(word 3, $(subst -, , $@)))
	yarn shadow-cljs watch $(SYSTEM)

watch-ios-real: export TARGET := ios
watch-ios-real: _watch-ios-real
watch-ios-simulator: export TARGET := ios
watch-ios-simulator: _watch-ios-simulator
watch-android-real: export TARGET := android
watch-android-real: _watch-android-real
watch-android-avd: export TARGET := android
watch-android-avd: _watch-android-avd
watch-android-genymotion: export TARGET ?= android
watch-android-genymotion: _watch-android-genymotion

watch-desktop: export TARGET ?= $(HOST_OS)
watch-desktop:
	clj -R:dev build.clj watch --platform desktop

desktop-server: export TARGET ?= $(HOST_OS)
desktop-server:
	node ubuntu-server.js

#--------------
# Run React-Native app
#--------------
_run-%:
	$(eval SYSTEM := $(word 2, $(subst -, , $@)))
	npx react-native run-$(SYSTEM)

# TODO: Migrate this to a Nix recipe, much the same way as nix/mobile/android/targets/release-android.nix
run-android: export TARGET := android
run-android: ##@run Build Android APK and start it on the device
	npx react-native run-android --appIdSuffix debug

run-desktop: export TARGET ?= $(HOST_OS)
run-desktop: _run-desktop ##@run Run Desktop build

SIMULATOR=
run-ios: export TARGET := ios
run-ios: ##@run Build iOS app and start it in a simulator/device
ifneq ("$(SIMULATOR)", "")
	npx react-native run-ios --simulator="$(SIMULATOR)"
else
	npx react-native run-ios
endif

#--------------
# Tests
#--------------

lint: export TARGET := clojure
lint: ##@test Run code style checks
	yarn clj-kondo --confg .clj-kondo/config.edn --lint src && \
	clojure -Sdeps '{:deps {cljfmt {:mvn/version "0.6.7"}}}' \
		-m cljfmt.main check src/status_im/core.cljs $(git diff --diff-filter=d --cached --name-only src) \
		--indents indentation.edn

lint-fix: export TARGET := clojure
lint-fix: ##@test Run code style checks and fix issues
	clojure -Sdeps '{:deps {cljfmt {:mvn/version "0.6.7"}}}' \
		-m cljfmt.main fix src \
		--indents indentation.edn

test: export TARGET := clojure
test: ##@test Run tests once in NodeJS
	yarn shadow-cljs compile mocks && \
	yarn shadow-cljs compile test && \
	node --require ./test-resources/override.js target/test/test.js

coverage: ##@test Run tests once in NodeJS generating coverage
	@scripts/run-coverage.sh

#--------------
# Other
#--------------
react-native-desktop: export TARGET ?= $(HOST_OS)
react-native-desktop: export _NIX_PURE ?= true
react-native-desktop: ##@other Start react native packager
	@scripts/start-react-native.sh

react-native-android: export TARGET := android
react-native-android: export _NIX_PURE ?= true
react-native-android: ##@other Start react native packager for Android client
	@scripts/start-react-native.sh

react-native-ios: export TARGET := ios
react-native-ios: export _NIX_PURE ?= true
react-native-ios: ##@other Start react native packager for Android client
	@scripts/start-react-native.sh

geth-connect: export TARGET := android-env
geth-connect: ##@other Connect to Geth on the device
	adb forward tcp:8545 tcp:8545 && \
	build/bin/geth attach http://localhost:8545

android-clean: export TARGET := android
android-clean: ##@prepare Clean Gradle state
	git clean -dxf -f ./android/app/build
	[[ -d android/.gradle ]] && cd android && ./gradlew clean

android-ports: export TARGET := android-env
android-ports: ##@other Add proxies to Android Device/Simulator
	adb reverse tcp:8081 tcp:8081 && \
	adb reverse tcp:3449 tcp:3449 && \
	adb reverse tcp:4567 tcp:4567 && \
	adb forward tcp:5561 tcp:5561

android-devices: export TARGET := android-env
android-devices: ##@other Invoke adb devices
	adb devices

android-logcat: export TARGET := android-env
android-logcat: ##@other Read status-react logs from Android phone using adb
	adb logcat | grep -e RNBootstrap -e ReactNativeJS -e ReactNative -e StatusModule -e StatusNativeLogs -e 'F DEBUG   :' -e 'Go      :' -e 'GoLog   :' -e 'libc    :'

android-install: export TARGET := android-env
android-install: export BUILD_TYPE ?= release
android-install: ##@other Install APK on device using adb
	adb install result/app-$(BUILD_TYPE).apk

_list: SHELL := /bin/sh
_list:
	@$(MAKE) -pRrq -f $(lastword $(MAKEFILE_LIST)) : 2>/dev/null | awk -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' | sort | egrep -v -e '^[^[:alnum:]]' -e '^$@$$'

_unknown-startdev-target-%: SHELL := /bin/sh
_unknown-startdev-target-%:
	@ echo "Unknown target device '$*'. Supported targets:"; \
	${MAKE} _list | grep "watch-" | sed s/watch-/startdev-/; \
	exit 1

_startdev-%: SHELL := /bin/sh
_startdev-%:
	$(eval SYSTEM := $(word 2, $(subst -, , $@)))
	$(eval DEVICE := $(word 3, $(subst -, , $@)))
	@ if [ -z "$(DEVICE)" ]; then \
		$(MAKE) watch-$(SYSTEM) || $(MAKE) _unknown-startdev-target-$@; \
	else \
		$(MAKE) watch-$(SYSTEM)-$(DEVICE) || $(MAKE) _unknown-startdev-target-$@; \
	fi

startdev-android-avd: export TARGET = android
startdev-android-avd: _startdev-android-avd
startdev-android-avd:  ##@startdev Compile Clojure for Android AVD
startdev-android-genymotion: export TARGET = android
startdev-android-genymotion: _startdev-android-genymotion
startdev-android-genymotion: ##@startdev Compile Clojure for Genymotion
startdev-android-real: export TARGET = android
startdev-android-real: _startdev-android-real
startdev-android-real: ##@startdev Compile Clojure for Android real device
startdev-ios-real: export TARGET = ios
startdev-ios-real: _startdev-ios-real
startdev-ios-real: ##@startdev Compile Clojure for iOS device
startdev-ios-simulator: export TARGET = ios
startdev-ios-simulator: _startdev-ios-simulator
startdev-ios-simulator: ##@startdev Compile Clojure for iOS simulator

startdev-desktop: export TARGET ?= $(HOST_OS)
startdev-desktop: _startdev-desktop

re-frisk: export TARGET := clojure
re-frisk: ##@re-frisk start re-frisk server
	yarn shadow-cljs run re-frisk-remote.core/start

repl-ios: export TARGET := clojure
repl-ios: ##@repl-ios start repl for ios project
	yarn shadow-cljs cljs-repl ios

repl-android: export TARGET := clojure
repl-android: ##@repl-android start repl for android project
	yarn shadow-cljs cljs-repl android