gitVersioningCommand = "git describe --tags --dirty --always"
gitCurrentBranchCommand = "git symbolic-ref -q --short HEAD"

-- Quote the given string input as a C string
function cstrquote(value)
	if value == nil then
		return "\"\""
	end
	result = value:gsub("\\", "\\\\")
	result = result:gsub("\"", "\\\"")
	result = result:gsub("\n", "\\n")
	result = result:gsub("\t", "\\t")
	result = result:gsub("\r", "\\r")
	result = result:gsub("\a", "\\a")
	result = result:gsub("\b", "\\b")
	result = "\"" .. result .. "\""
	return result
end

newaction {
	trigger = "generate-buildinfo",
	description = "Sets up build information file like version.hpp.",
	onWorkspace = function(wks)
		-- get old version number from version.hpp if any
		local oldVersion = "(none)"
		local oldVersionHeader = io.open(wks.location .. "/common/version.hpp", "r")
		if oldVersionHeader ~= nil then
			local oldVersionHeaderContent = assert(oldVersionHeader:read('*l'))
			while oldVersionHeaderContent do
				m = string.match(oldVersionHeaderContent, "#define GIT_DESCRIBE (.+)%s*$")
				if m ~= nil then
						oldVersion = m
				end

				oldVersionHeaderContent = oldVersionHeader:read('*l')
			end
		end

		-- get current version via git
		local proc = assert(io.popen(gitVersioningCommand, "r"))
		local gitDescribeOutput = assert(proc:read('*a')):gsub("%s+", "")
		proc:close()

		-- generate version.hpp with a revision number if not equal
		gitDescribeOutputQuoted = cstrquote(gitDescribeOutput)
		if oldVersion ~= gitDescribeOutputQuoted then
			-- get current git hash and write to version.txt (used by the preliminary updater)
			-- TODO - remove once proper updater and release versioning exists
			local proc = assert(io.popen("git rev-parse HEAD", "r"))
			local gitCommitHash = assert(proc:read('*a')):gsub("%s+", "")
			proc:close()

			-- get whether this is a clean revision (no uncommitted changes)
			proc = assert(io.popen("git status --porcelain", "r"))
			local revDirty = (assert(proc:read('*a')) ~= "")
			if revDirty then revDirty = 1 else revDirty = 0 end
			proc:close()

			-- get current tag name
			proc = assert(io.popen("git describe --tags --abbrev=0"))
			local tagName = proc:read('*l')

			-- get current branch name
			proc = assert(io.popen("git branch --show-current"))
			local branchName = proc:read('*l')

			-- branch for ci
			if branchName == nil or branchName == '' then
				proc = assert(io.popen("git show -s --pretty=%d HEAD"))
				local branchInfo = proc:read('*l')
				m = string.match(branchInfo, ".+,.+, ([^)]+)")
				if m ~= nil then
					branchName = m
				end
			end

			if branchName == nil then
				branchName = "develop"
			end

			print("Detected branch: " .. branchName)

			-- get revision number via git
			local proc = assert(io.popen("git rev-list --count HEAD", "r"))
			local revNumber = assert(proc:read('*a')):gsub("%s+", "")

			print ("Update " .. oldVersion .. " -> " .. gitDescribeOutputQuoted)

			-- write version header
			local versionHeader = assert(io.open(wks.location .. "/common/version.hpp", "w"))
			versionHeader:write("/*\n")
			versionHeader:write(" * Automatically generated by premake5.\n")
			versionHeader:write(" * Do not touch!\n")
			versionHeader:write(" */\n")
			versionHeader:write("\n")
			versionHeader:write("#ifdef GIT_DESCRIBE\n")
			versionHeader:write("#	undef GIT_DESCRIBE\n")
			versionHeader:write("#endif\n")
			versionHeader:write("#ifdef GIT_DIRTY\n")
			versionHeader:write("#	undef GIT_DIRTY\n")
			versionHeader:write("#endif\n")
			versionHeader:write("#ifdef GIT_HASH\n")
			versionHeader:write("#	undef GIT_HASH\n")
			versionHeader:write("#endif\n")
			versionHeader:write("#ifdef GIT_TAG\n")
			versionHeader:write("#	undef GIT_TAG\n")
			versionHeader:write("#endif\n")
			versionHeader:write("#ifdef GIT_BRANCH\n")
			versionHeader:write("#	undef GIT_BRANCH\n")
			versionHeader:write("#endif\n")
			versionHeader:write("\n")
			versionHeader:write("#define GIT_DESCRIBE " .. gitDescribeOutputQuoted .. "\n")
			versionHeader:write("#define GIT_DIRTY " .. revDirty .. "\n")
			versionHeader:write("#define GIT_HASH " .. cstrquote(gitCommitHash) .. "\n")
			versionHeader:write("#define GIT_TAG " .. cstrquote(tagName) .. "\n")
			versionHeader:write("#define GIT_BRANCH " .. cstrquote(branchName) .. "\n")
			versionHeader:close()
		end
	end
}

workspace "iw8-mod"
startproject "client"
filename "iw8_%{_ACTION}"

configurations { "Debug", "Release" }
platforms { "x64" }

warnings "Extra"
characterset "Unicode"
exceptionhandling "SEH" 

flags { "NoPCH", "MultiProcessorCompile" }
includedirs { "$(SolutionDir)" }

buildoptions { "/sdl-" }

cppdialect "C++latest"

filter "configurations:Release"
	defines { "_NDEBUG" }
	optimize "Full"
	intrinsics "on"
	functionlevellinking "on"

filter "configurations:Debug"
	defines { "_DEBUG" }
	symbols "On"

filter "system:Windows"
	systemversion "latest"

buildDir = "$(SolutionDir)\\build\\$(SolutionName)\\$(PlatformName)\\$(ProjectName)\\"
intBuildDir = "$(SolutionDir)\\build\\$(SolutionName)-int\\$(PlatformName)\\$(ProjectName)\\"

project "client"
	location "client"
	kind "SharedLib"
	language "C++"
	targetname "XInput9_1_0"

	files {
		"client/**.hpp",
		"client/**.cpp",
		"client/**.h",
		"client/**.c",
		"client/**.rc",
		"client/resources/**.*"
	}
	vpaths {
		["*"] = {}
	}
	links {
		"backward-cpp",
		"curl",
		"common",
		"discord-rpc",
		"imgui",
		"minhook"
	}
	includedirs {
		"client/",
		"common/",
		"vendor/asmjit/src/",
		"vendor/backward-cpp/",
		"vendor/curl/include/",
		"vendor/discord-rpc/",
		"vendor/gsl/include/",
		"vendor/imgui/",
		"vendor/json/single_include/",
		"vendor/minhook/include/"
	}
	defines {
		"_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR",
		"NOMINMAX",
		"CURL_STRICTER",
		"CURL_STATICLIB",
		"CURL_DISABLE_LDAP",
		"USE_SCHANNEL",
		"USE_WINDOWS_SSPI",
		"USE_THREADS_WIN32",
		"BUILDING_LIBCURL"
	}
	prebuildcommands {
		"cd .. && .\\tools\\premake\\premake5.exe generate-buildinfo"
	}
	disablewarnings {
		"4201",	-- C4201: nameless struct/union
		"6031"	-- C6031: return value ignored (persistent with LOG macro, even though the return value is *not* ignored)
	}

	targetdir(buildDir)
	objdir(intBuildDir)

project "common"
	location "common"
	kind "StaticLib"
	language "C++"

	files {
		"common/**.hpp",
		"common/**.cpp"
	}
	vpaths {
		["*"] = {}
	}
	links {
		"backward-cpp",
		"imgui",
		"minhook"
	}
	includedirs {
		"common/",
		"vendor/asmjit/src/",
		"vendor/backward-cpp/",
		"vendor/gsl/include/",
		"vendor/imgui/",
		"vendor/json/single_include/",
		"vendor/minhook/include/"
	}
	defines {
		"_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR",
		"NOMINMAX"
	}
	prebuildcommands {
		"cd .. && .\\tools\\premake\\premake5.exe generate-buildinfo"
	}
	disablewarnings {
		"4201",	-- C4201: nameless struct/union
		"6031"	-- C6031: return value ignored (persistent with LOG macro, even though the return value is *not* ignored)
	}

	targetdir(buildDir)
	objdir(intBuildDir)

group "vendor"
	-- vendor
	project "backward-cpp"
		location "vendor/%{prj.name}"
		kind "StaticLib"
		language "C++"

		files {
			"vendor/%{prj.name}/backward.hpp",
			"vendor/%{prj.name}/backward.cpp"
		}
		vpaths {
			["*"] = {}
		}
		includedirs {
			"vendor/%{prj.name}/"
		}
		defines {
			"_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR",
			"NOMINMAX"
		}
		disablewarnings {
			"4996"	-- C4996: This function or variable may be unsafe. Consider using [...] instead.
		}

		targetdir(buildDir)
		objdir(intBuildDir)
	project "curl"
		location "vendor/%{prj.name}"
		kind "StaticLib"
		language "C"

		files {
			"vendor/%{prj.name}/include/**.h",
			"vendor/%{prj.name}/lib/**.h",
			"vendor/%{prj.name}/lib/**.c"
		}
		vpaths {
			["*"] = {}
		}
		links {
			"Crypt32.lib"
		}
		includedirs {
			"vendor/%{prj.name}/include/",
			"vendor/%{prj.name}/lib/"
		}
		defines {
			"_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR",
			"CURL_STRICTER",
			"CURL_STATICLIB",
			"CURL_DISABLE_LDAP",
			"USE_SCHANNEL",
			"USE_WINDOWS_SSPI",
			"USE_THREADS_WIN32",
			"BUILDING_LIBCURL"
		}
		disablewarnings {
			"4005"	-- C4005: macro redefinition
		}

		targetdir(buildDir)
		objdir(intBuildDir)
	project "discord-rpc"
		location "vendor/%{prj.name}"
		kind "StaticLib"
		language "C++"

		files {
			"vendor/%{prj.name}/**.h",
			"vendor/%{prj.name}/**.cpp"
		}
		vpaths {
			["*"] = {}
		}
		includedirs {
			"vendor/%{prj.name}/"
		}
		defines {
			"_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR"
		}
		disablewarnings {
			"4996",	-- C4996: This function or variable may be unsafe. Consider using [...] instead.
			"5054"	-- C5054: operator '==': deprecated between enumerations of different types
		}

		targetdir(buildDir)
		objdir(intBuildDir)
	project "imgui"
		location "vendor/%{prj.name}"
		kind "StaticLib"
		language "C++"

		files {
			"vendor/%{prj.name}/*.h",
			"vendor/%{prj.name}/*.cpp",
			"vendor/%{prj.name}/backends/imgui_impl_dx12.h",
			"vendor/%{prj.name}/backends/imgui_impl_dx12.cpp",
			"vendor/%{prj.name}/backends/imgui_impl_win32.h",
			"vendor/%{prj.name}/backends/imgui_impl_win32.cpp"
		}
		vpaths {
			["*"] = {}
		}
		includedirs {
			"vendor/%{prj.name}/"
		}
		defines {
			"_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR"
		}

		targetdir(buildDir)
		objdir(intBuildDir)
	project "asmjit"
		location "vendor/%{prj.name}"
		kind "StaticLib"
		language "C++"

		files {
			"vendor/%{prj.name}/src/**.h",
			"vendor/%{prj.name}/src/**.cpp"
		}
		vpaths {
			["*"] = {}
		}
		includedirs {
			"vendor/%{prj.name}/src/"
		}
		defines {
			"_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR",
			"ASMJIT_STATIC"
		}
		disablewarnings {
			"5054"	-- C5054: operator '==': deprecated between enumerations of different types
		}

		targetdir(buildDir)
		objdir(intBuildDir)
	project "json"
		location "vendor/%{prj.name}"
		kind "StaticLib"
		language "C++"

		files {
			"vendor/%{prj.name}/single_include/**.hpp"
		}
		vpaths {
			["*"] = {}
		}
		includedirs {
			"vendor/%{prj.name}/single_include/"
		}

		targetdir(buildDir)
		objdir(intBuildDir)
	project "minhook"
		location "vendor/%{prj.name}"
		kind "StaticLib"
		language "C++"

		files {
			"vendor/%{prj.name}/include/**.h",
			"vendor/%{prj.name}/src/**.h",
			"vendor/%{prj.name}/src/**.c"
		}
		vpaths {
			["*"] = {}
		}
		includedirs {
			"vendor/%{prj.name}/include/",
			"vendor/%{prj.name}/src/"
		}
		defines {
			"_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR"
		}
		disablewarnings {
			"4100",	-- C4100: '[...]': unreferenced formal parameter
			"4201",	-- C4201: nonstandard extension used: nameless struct/union
			"4206",	-- C4206: nonstandard extension used: translation unit is empty
			"4244",	-- C4244: '=': conversion from '[...]' to '[...]', possible loss of data
			"4310",	-- C4310: cast truncates constant value
			"4701",	-- C4701: potentially uninitialized local variable '[...]' used
			"4706"	-- C4706: assignment within conditional expression
		}

		targetdir(buildDir)
		objdir(intBuildDir)