import org.doomedsociety.gradlecpp.cfg.BinaryKind
import org.doomedsociety.gradlecpp.msvc.CallingConvention
import org.doomedsociety.gradlecpp.msvc.CodeGenerationKind
import org.doomedsociety.gradlecpp.msvc.CppExceptions
import org.doomedsociety.gradlecpp.msvc.DebugInfoFormat
import org.doomedsociety.gradlecpp.msvc.EnhancedInstructionsSet
import org.doomedsociety.gradlecpp.msvc.ErrorReporting
import org.doomedsociety.gradlecpp.msvc.FloatingPointModel
import org.doomedsociety.gradlecpp.msvc.LinkTimeCodeGenKind
import org.doomedsociety.gradlecpp.msvc.MsvcToolchainConfig
import org.doomedsociety.gradlecpp.msvc.OptimizationLevel
import org.doomedsociety.gradlecpp.msvc.RuntimeChecks
import org.doomedsociety.gradlecpp.msvc.WarningLevel

rootProject.ext.createMsvcConfig = { boolean release, BinaryKind binKind ->
	MsvcToolchainConfig cfg
	if (release) {
		cfg = new MsvcToolchainConfig(
			compilerOptions: new MsvcToolchainConfig.CompilerOptions(
				codeGeneration: CodeGenerationKind.MULTITHREADED,
				optimizationLevel: OptimizationLevel.FULL_OPTIMIZATION,
				debugInfoFormat: DebugInfoFormat.PROGRAM_DATABASE,
				runtimeChecks: RuntimeChecks.DEFAULT,
				cppExceptions: CppExceptions.ENABLED_WITH_SEH,
				warningLevel: WarningLevel.LEVEL_3,
				callingConvention: CallingConvention.CDECL,
				enhancedInstructionsSet: EnhancedInstructionsSet.SSE2,
				floatingPointModel: FloatingPointModel.FAST,

				enableMinimalRebuild: false,
				omitFramePointers: false,
				wholeProgramOptimization: true,
				enabledFunctionLevelLinking: true,
				enableSecurityCheck: true,
				analyzeCode: false,
				sdlChecks: false,
				treatWarningsAsErrors: false,
				treatWchartAsBuiltin: true,
				forceConformanceInForLoopScope: true,

				extraDefines: [
					'WIN32': null,
					'_MBCS': null,
					'NDEBUG': null,
				]
			),
			linkerOptions: new MsvcToolchainConfig.LinkerOptions(
				linkTimeCodeGenKind: LinkTimeCodeGenKind.USE_LTCG,
				errorReportingMode: ErrorReporting.NO_ERROR_REPORT,

				enableIncrementalLinking: false,
				eliminateUnusedRefs: true,
				enableCOMDATFolding: true,
				generateDebugInfo: true,
				dataExecutionPrevention: true,
				randomizedBaseAddress: true,
			),
			librarianOptions: new MsvcToolchainConfig.LibrarianOptions(
				linkTimeCodeGenKind: LinkTimeCodeGenKind.USE_LTCG
			),
			generatePdb: true
		)
	} else {
		// debug
		cfg = new MsvcToolchainConfig(
			compilerOptions: new MsvcToolchainConfig.CompilerOptions(
				codeGeneration: CodeGenerationKind.MULTITHREADED_DEBUG,
				optimizationLevel: OptimizationLevel.DISABLED,
				debugInfoFormat: DebugInfoFormat.PROGRAM_DATABASE,
				runtimeChecks: RuntimeChecks.DEFAULT,
				cppExceptions: CppExceptions.ENABLED_WITH_SEH,
				warningLevel: WarningLevel.LEVEL_3,
				callingConvention: CallingConvention.CDECL,
				enhancedInstructionsSet: EnhancedInstructionsSet.SSE2,
				floatingPointModel: FloatingPointModel.FAST,

				enableMinimalRebuild: true,
				omitFramePointers: false,
				wholeProgramOptimization: false,
				enabledFunctionLevelLinking: true,
				enableSecurityCheck: true,
				analyzeCode: false,
				sdlChecks: false,
				treatWarningsAsErrors: false,
				treatWchartAsBuiltin: true,
				forceConformanceInForLoopScope: true,

				extraDefines: [
					'WIN32': null,
					'_MBCS': null,
					'_DEBUG': null,
				]
			),
			linkerOptions: new MsvcToolchainConfig.LinkerOptions(
				linkTimeCodeGenKind: LinkTimeCodeGenKind.DEFAULT,
				errorReportingMode: ErrorReporting.NO_ERROR_REPORT,

				enableIncrementalLinking: true,
				eliminateUnusedRefs: false,
				enableCOMDATFolding: false,
				generateDebugInfo: true,
				dataExecutionPrevention: true,
				randomizedBaseAddress: true
			),
			librarianOptions: new MsvcToolchainConfig.LibrarianOptions(
				linkTimeCodeGenKind: LinkTimeCodeGenKind.USE_LTCG
			),
			generatePdb: true
		)

		if (binKind == BinaryKind.STATIC_LIBRARY) {
			cfg.compilerConfig.extraDefines['_LIB'] = null
		}
	}

	// Detect and setup UCRT paths
	def ucrtInfo = "getucrtinfo.bat".execute().text
	def m = ucrtInfo =~ /^(.*)\r\n(.*)?$/
	if (!m.find()) {
		return cfg
	}

	def kitPath = m.group(1)
	def ucrtVersion = m.group(2)
	def ucrtCheckFile = new File("${kitPath}Include/${ucrtVersion}/ucrt/stdio.h");
	if (!ucrtCheckFile.exists()) {
		return cfg
	}

	cfg.compilerOptions.args "/FS", "/I${kitPath}Include/${ucrtVersion}/ucrt";
	cfg.linkerOptions.args("/LIBPATH:${kitPath}Lib/${ucrtVersion}/ucrt/x86");

	return cfg
}