#!/bin/sh
set -e

root_dir=$(dirname "$0")
root_dir=$(cd "$root_dir/.." && pwd)
if [ ! -f "$root_dir/LICENSE.txt" ]; then
	echo "Couldn't find the root dir"
	exit 1
fi

configuration=Release
set_rustflags=y
test_gen=y
test_instrs=y
test_build_no_std=y
test_features=y
test_current=y
test_msrv=y
test_code_asm=y

# Minimum supported Rust version
msrv="1.48.0"

new_func() {
	echo
	echo "****************************************************************"
	echo "$1"
	echo "****************************************************************"
	echo
}

generator_check() {
	new_func "Run generator, verify no diff"

	dotnet run -c $configuration -p "$root_dir/src/csharp/Intel/Generator/Generator.csproj"
	git diff --exit-code
}

test_valid_invalid_instructions() {
	new_func "Decode valid and invalid instructions"

	valid_file=$(mktemp)
	invalid_file=$(mktemp)

	# The C# code needs a formatter so add masm feature
	dotnet build -c:$configuration -p:IcedFeatureFlags="DECODER ENCODER OPCODE_INFO INSTR_INFO MASM" "$root_dir/src/csharp/Intel/IcedFuzzer/IcedFuzzer/IcedFuzzer.csproj"
	for bitness in 16 32 64; do
		echo "==== ${bitness}-bit: Generating valid/invalid files ===="
		dotnet run -c:$configuration --no-build -p "$root_dir/src/csharp/Intel/IcedFuzzer/IcedFuzzer/IcedFuzzer.csproj" -- -$bitness -oil "$invalid_file" -ovlc "$valid_file"
		echo "==== ${bitness}-bit: Testing valid instructions ===="
		cargo run --color always --release --manifest-path "$root_dir/src/rust/iced-x86-fzgt/Cargo.toml" -- -b $bitness -f "$valid_file"
		echo "==== ${bitness}-bit: Testing invalid instructions ===="
		cargo run --color always --release --manifest-path "$root_dir/src/rust/iced-x86-fzgt/Cargo.toml" -- -b $bitness -f "$invalid_file" --invalid
	done

	for bitness in 16 32 64; do
		echo "==== ${bitness}-bit (AMD): Generating valid/invalid files ===="
		dotnet run -c:$configuration --no-build -p "$root_dir/src/csharp/Intel/IcedFuzzer/IcedFuzzer/IcedFuzzer.csproj" -- -$bitness -oil "$invalid_file" -ovlc "$valid_file" --amd
		echo "==== ${bitness}-bit (AMD): Testing valid instructions ===="
		cargo run --color always --release --manifest-path "$root_dir/src/rust/iced-x86-fzgt/Cargo.toml" -- -b $bitness -f "$valid_file" --amd
		echo "==== ${bitness}-bit (AMD): Testing invalid instructions ===="
		cargo run --color always --release --manifest-path "$root_dir/src/rust/iced-x86-fzgt/Cargo.toml" -- -b $bitness -f "$invalid_file" --invalid --amd
	done

	rm "$valid_file"
	rm "$invalid_file"
}

build_no_std() {
	new_func "Build no_std"
	curr_dir=$(pwd)
	cd "$root_dir/src/rust/iced-x86"

	echo "==== BUILD DEBUG ===="
	cargo check --color always --no-default-features --features "no_std decoder encoder block_encoder op_code_info instr_info gas intel masm nasm fast_fmt serde code_asm mvex"

	cd "$curr_dir"
}

build_features() {
	new_func "Build one feature at a time"
	curr_dir=$(pwd)
	cd "$root_dir/src/rust/iced-x86"

	set -- \
		"std decoder" \
		"std decoder mvex" \
		"std encoder" \
		"std encoder block_encoder" \
		"std encoder op_code_info" \
		"std instr_info" \
		"std gas" \
		"std intel" \
		"std masm" \
		"std nasm" \
		"std fast_fmt" \
		"std serde" \
		"std code_asm"
	for features in "$@"; do
		echo "==== $features ===="
		cargo check --color always --release --no-default-features --features "$features"
	done

	set -- \
		"no_vex" \
		"no_evex" \
		"no_xop" \
		"no_d3now" \
		"no_vex no_evex no_xop no_d3now"
	for features in "$@"; do
		echo "==== $features ===="
		cargo check --color always --release --features "$features"
	done

	set -- \
		"no_std decoder" \
		"no_std decoder mvex" \
		"no_std encoder" \
		"no_std encoder block_encoder" \
		"no_std encoder op_code_info" \
		"no_std instr_info" \
		"no_std gas" \
		"no_std intel" \
		"no_std masm" \
		"no_std nasm" \
		"no_std fast_fmt" \
		"no_std serde" \
		"no_std code_asm"
	for features in "$@"; do
		echo "==== $features ===="
		cargo check --color always --release --no-default-features --features "$features"
	done

	set -- \
		"std decoder" \
		"std decoder mvex" \
		"std decoder encoder" \
		"std decoder encoder block_encoder" \
		"std decoder encoder op_code_info" \
		"std decoder instr_info" \
		"std decoder gas" \
		"std decoder intel" \
		"std decoder masm" \
		"std decoder nasm" \
		"std decoder fast_fmt" \
		"std decoder serde" \
		"std decoder code_asm"
	for features in "$@"; do
		echo "==== TEST $features ===="
		cargo check --color always --release --tests --no-default-features --features "$features"
	done

	set -- \
		"no_vex" \
		"no_evex" \
		"no_xop" \
		"no_d3now" \
		"no_vex no_evex no_xop no_d3now"
	for features in "$@"; do
		echo "==== TEST $features ===="
		cargo check --color always --release --tests --features "$features"
	done

	cd "$curr_dir"
}

build_test_current_version() {
	new_func "Build, test (current version)"
	curr_dir=$(pwd)
	cd "$root_dir/src/rust/iced-x86"

	echo "Rust version"
	rustc --version

	echo "==== CLIPPY RELEASE --tests ===="
	cargo clippy --color always --release --features "serde code_asm mvex" --tests

	echo "==== FORMAT CHECK ===="
	cargo fmt -- --color always --check

	echo "==== DOC ===="
	cargo doc --color always --features "serde code_asm mvex"

	echo "==== BUILD RELEASE ===="
	cargo check --color always --release --features "serde code_asm mvex"

	echo "==== TEST RELEASE ===="
	extra_args=""
	if [ "$test_code_asm" != "y" ]; then
		extra_args="-- --skip lib.rs"
	fi
	cargo test --color always --release --features "serde $test_code_asm_feat mvex" $extra_args

	# Make sure the two read-mem methods behave the same
	# Also test serde code. It needs encoder to also test 'db x,y,z', see serde tests
	echo "==== TEST DEBUG: std decoder encoder serde __internal_flip ===="
	cargo test --color always --tests --no-default-features --features "std decoder encoder serde __internal_flip"

	echo "==== TEST DEBUG ===="
	cargo test --color always --tests --features "serde $test_code_asm_feat mvex"

	echo "==== BUILD RELEASE wasm32-unknown-unknown ===="
	cargo check --color always --target wasm32-unknown-unknown --release --features "serde code_asm mvex"

	echo "==== PUBLISH DRY-RUN ===="
	# It fails on Windows (GitHub CI) without this, claiming that some random number of Rust files are dirty.
	# Redirect to /dev/null so it won't hang (waiting for us to scroll) if it finds modified lines
	git status > /dev/null
	git diff > /dev/null
	cargo publish --color always --features "serde code_asm mvex" --dry-run

	cd "$curr_dir"
}

build_test_msrv() {
	new_func "Build minimum supported Rust version: $msrv"

	curr_dir=$(pwd)
	cd "$root_dir/src/rust/iced-x86"

	echo "*** If this fails, install Rust $msrv"

	echo "==== BUILD DEBUG ===="
	cargo +$msrv check --color always --features "serde code_asm mvex"

	echo "==== BUILD DEBUG default features ===="
	# Build with default features since that's what most people probably use
	cargo +$msrv check --color always

	# We don't test it since we only guarantee that the crate can be built. Testing is for iced devs.
	# We test it when using the latest rustc, but not rustc MSRV.
	# This also speeds up CI since code_asm feature's tests take a very long time to compile.

	cd "$curr_dir"
}

clear_test_vars() {
	test_gen=n
	test_instrs=n
	test_build_no_std=n
	test_features=n
	test_current=n
	test_msrv=n
}

while [ "$#" -gt 0 ]; do
	case $1 in
	--no-tests) clear_test_vars ;;
	--quick-check)
		clear_test_vars
		test_current=y
		test_code_asm=n
		;;
	--no-dotnet)
		test_gen=n
		test_instrs=n
		;;

	--no-gen) test_gen=n ;;
	--no-instrs) test_instrs=n ;;
	--no-no_std) test_build_no_std=n ;;
	--no-features) test_features=n ;;
	--no-current) test_current=n ;;
	--no-msrv) test_msrv=n ;;

	--test-gen) test_gen=y ;;
	--test-instrs) test_instrs=y ;;
	--test-no_std) test_build_no_std=y ;;
	--test-features) test_features=y ;;
	--test-current) test_current=y ;;
	--test-msrv) test_msrv=y ;;

	--no-set-rustflags) set_rustflags=n ;;
	*) echo "Unknown arg: $1"; exit 1 ;;
	esac
	shift
done

echo
echo "=================================================="
echo "Rust build"
echo "=================================================="
echo

if [ "$set_rustflags" = "y" ]; then
	export RUSTFLAGS="-D warnings"
fi

echo "rustup show"
rustup show

if [ "$test_code_asm" = "y" ]; then
	test_code_asm_feat="code_asm"
else
	test_code_asm_feat=""
fi

if [ "$test_gen" = "y" ] || [ "$test_instrs" = "y" ]; then
	echo "dotnet version (if this fails, install .NET or use --no-dotnet)"
	dotnet --version
fi

if [ "$test_gen" = "y" ]; then
	generator_check
fi
if [ "$test_instrs" = "y" ]; then
	test_valid_invalid_instructions
fi
if [ "$test_build_no_std" = "y" ]; then
	build_no_std
fi
if [ "$test_features" = "y" ]; then
	build_features
fi
if [ "$test_current" = "y" ]; then
	build_test_current_version
fi
if [ "$test_msrv" = "y" ]; then
	build_test_msrv
fi
