import gradlecpp.RegamedllPlayTestPlugin
import gradlecpp.RegamedllPlayTestTask
import gradlecpp.VelocityUtils
import org.doomedsociety.gradlecpp.GradleCppUtils
import org.doomedsociety.gradlecpp.LazyNativeDepSet
import org.doomedsociety.gradlecpp.cfg.ToolchainConfig
import org.doomedsociety.gradlecpp.cfg.ToolchainConfigUtils
import org.doomedsociety.gradlecpp.gcc.GccToolchainConfig
import org.doomedsociety.gradlecpp.msvc.EnhancedInstructionsSet
import org.doomedsociety.gradlecpp.msvc.FloatingPointModel
import org.doomedsociety.gradlecpp.msvc.MsvcToolchainConfig
import org.doomedsociety.gradlecpp.toolchain.icc.Icc
import org.doomedsociety.gradlecpp.toolchain.icc.IccCompilerPlugin
import org.gradle.language.cpp.CppSourceSet
import org.gradle.nativeplatform.NativeBinarySpec
import org.gradle.nativeplatform.NativeExecutableSpec
import org.gradle.nativeplatform.NativeLibrarySpec
import org.gradle.nativeplatform.SharedLibraryBinarySpec
import regamedll.testdemo.RegamedllDemoRunner
import versioning.RegamedllVersionInfo
import org.apache.commons.io.FilenameUtils
import org.apache.commons.compress.archivers.ArchiveInputStream

apply plugin: 'cpp'
apply plugin: IccCompilerPlugin
apply plugin: RegamedllPlayTestPlugin
apply plugin: gradlecpp.CppUnitTestPlugin

repositories {
	maven {
		url 'http://nexus.rehlds.org/nexus/content/repositories/regamedll-releases/'
	}
}

configurations {
	regamedll_tests
}

dependencies {
	//regamedll_tests 'regamedll.testdemos:cstrike-fulltests-2:2.0'
	//regamedll_tests 'regamedll.testdemos:czero-ladder_hard:2.0'
	//regamedll_tests 'regamedll.testdemos:cstrike-multiplayer-2:2.0'
	//regamedll_tests 'regamedll.testdemos:cstrike-hostage-2:2.0'
	//regamedll_tests 'regamedll.testdemos:cstrike-weapon-1:1.0'
	//regamedll_tests 'regamedll.testdemos:czero-czbot_game-2:2.0'

}

project.ext.dep_bzip2 = project(':dep/bzip2')
project.ext.dep_cppunitlite = project(':dep/cppunitlite')

void createIntergrationTestTask(NativeBinarySpec b) {
	boolean regamedllFixes = b.flavor.name.contains('regamedllFixes')

	if (!(b instanceof SharedLibraryBinarySpec)) return
	if (!GradleCppUtils.windows) return
	if (regamedllFixes) return

	def libLinkTask = GradleCppUtils.getLinkTask(b)
	String unitTestTask = b.hasProperty('cppUnitTestTask') ? b.cppUnitTestTask : null

	def depFiles = []
	depFiles.addAll(libLinkTask.outputs.files.files)

	def demoItgTestTask = project.tasks.create(b.namingScheme.getTaskName('demoItgTest'), RegamedllPlayTestTask)
	demoItgTestTask.with {
		regamedllImageRoot = new File(project.projectDir, '_regamedllTestImg')
		regamedllTestLogs = new File(this.project.buildDir, "_regamedllTestLogs/${b.name}")
		testDemos = project.configurations.regamedll_tests
		testFor = b

	//inputs/outputs for up-to-date check
	inputs.files depFiles
	inputs.files testDemos.files
	outputs.dir regamedllTestLogs

	//dependencies on library and test executable
	dependsOn libLinkTask
	if (unitTestTask) {
		dependsOn unitTestTask
	}

	postExtractAction {
		def binaryOutFile = GradleCppUtils.getBinaryOutputFile(b)
			GradleCppUtils.copyFile(binaryOutFile, new File(regamedllImageRoot, binaryOutFile.name), true)
		}
	}

	b.buildTask.dependsOn demoItgTestTask
}

void setupUnitTests(NativeBinarySpec bin) {
	boolean unitTestExecutable = bin.component.name.endsWith('_tests')
	if (!unitTestExecutable) return

	GradleCppUtils.getLinkTask(bin).doLast {
		String srcPath = '' + projectDir + (GradleCppUtils.windows ? '/lib/steam_api.dll' : '/lib/linux32/libsteam_api.so')
		String dstPath = bin.executableFile.parent + (GradleCppUtils.windows ? '/steam_api.dll' : '/libsteam_api.so')
		GradleCppUtils.copyFile(srcPath, dstPath, true)
	}
}

void postEvaluate(NativeBinarySpec b) {

	// attach generateAppVersion task to all 'compile source' tasks
	GradleCppUtils.getCompileTasks(b).each { Task t ->
		t.dependsOn project.generateAppVersion
	}

	setupUnitTests(b)
	createIntergrationTestTask(b)
}

void setupToolchain(NativeBinarySpec b)
{
	boolean unitTestExecutable = b.component.name.endsWith('_tests')
	boolean mpLib = b.name.toLowerCase().contains('mp')
	boolean regamedllFixes = b.flavor.name.contains('regamedllFixes')

	ToolchainConfig cfg = rootProject.createToolchainConfig(b)

	cfg.projectInclude(project, '', '/engine', '/common', '/dlls', '/game_shared', '/pm_shared', '/regamedll', '/testsuite', '/hookers', '/public', '/public/regamedll')
	cfg.projectInclude(dep_bzip2, '/include')

	if (unitTestExecutable)
	{
		cfg.projectInclude(dep_cppunitlite, '/include')
		b.lib LazyNativeDepSet.create(dep_cppunitlite, 'cppunitlite', b.buildType.name, true)
	}
	b.lib LazyNativeDepSet.create(dep_bzip2, 'bzip2', b.buildType.name, true)

	cfg.singleDefines 'USE_BREAKPAD_HANDLER', 'DEDICATED', 'REGAMEDLL_SELF', 'CLIENT_WEAPONS'

	if (cfg instanceof MsvcToolchainConfig)
	{
		cfg.compilerOptions.pchConfig = new MsvcToolchainConfig.PrecompiledHeadersConfig(
			enabled: true,
			pchHeader: 'precompiled.h',
			pchSourceSet: 'regamedll_pch'
		);

		cfg.singleDefines('_CRT_SECURE_NO_WARNINGS')
		if (!regamedllFixes)
		{
			cfg.compilerOptions.floatingPointModel = FloatingPointModel.PRECISE
			cfg.compilerOptions.enhancedInstructionsSet = EnhancedInstructionsSet.DISABLED
		}

		if (mpLib)
		{
			cfg.linkerOptions.randomizedBaseAddress = false
			cfg.linkerOptions.baseAddress = '0x4970000'
		}

		cfg.projectLibpath(project, '/lib')
		cfg.extraLibs 'steam_api.lib', 'psapi.lib', 'ws2_32.lib', 'kernel32.lib', 'user32.lib', 'advapi32.lib'
	}
	else if (cfg instanceof GccToolchainConfig) {
		cfg.compilerOptions.pchConfig = new GccToolchainConfig.PrecompilerHeaderOptions(
			enabled: true,
			pchSourceSet: 'regamedll_pch'
		);

		cfg.compilerOptions.languageStandard = 'c++0x'
		cfg.defines([
			'_stricmp': 'strcasecmp',
			'_strnicmp': 'strncasecmp',
			'_strdup': 'strdup',
			'_unlink': 'unlink',
			'_vsnprintf': 'vsnprintf',
			'_write' : 'write',
			'_close' : 'close',
		])

		cfg.linkerOptions.args '-no-opt-class-analysis'
		cfg.compilerOptions.args '-Qoption,cpp,--treat_func_as_string_literal_cpp', '-g'
		cfg.projectLibpath(project, '/lib/linux32')
		cfg.extraLibs 'dl', 'm', 'stdc++', 'steam_api'
	}

	if (mpLib && GradleCppUtils.windows && !unitTestExecutable) {
		cfg.linkerOptions.definitionFile = "${projectDir}\\msvc\\mp.def";
	}

	if (!unitTestExecutable && !mpLib)
	{
		cfg.singleDefines 'HOOK_GAMEDLL', 'CSTRIKE'
	}

	if (unitTestExecutable)
	{
		cfg.singleDefines 'REGAMEDLL_UNIT_TESTS'
	}

	if (regamedllFixes)
	{
		cfg.singleDefines 'REGAMEDLL_FIXES', 'REGAMEDLL_CHECKS'
	}

	ToolchainConfigUtils.apply(project, cfg, b)

	GradleCppUtils.onTasksCreated(project, 'postEvaluate', {
		postEvaluate(b)
	})
}

class RegamedllSrc {
	static void regamedll_src(def h) {
		h.regamedll_src(CppSourceSet) {

			source {
				srcDirs "engine", "dlls", "game_shared", "pm_shared", "regamedll", "public", "version"
				if (GradleCppUtils.windows) {
					srcDirs "testsuite"
				}

				include "**/*.cpp"
				exclude "precompiled.cpp"

				if (GradleCppUtils.windows) {
					exclude "tier0/platform_linux.cpp"
				} else {
					exclude "tier0/platform_win32.cpp"
				}

				//exclude "interface.cpp"
			}

			source {
				srcDirs "hookers"
				include "**/*.cpp"
				exclude "6153_hooker.cpp", "hooker.cpp", "main.cpp", "main_mp.cpp"
				// if (!GradleCppUtils.windows)
					//	exclude "regamedll_debug.cpp"
			}
		}
	}

	static void regamedll_pch(def h) {
		h.regamedll_pch(CppSourceSet) {
			source {
				srcDirs "regamedll"
				include "precompiled.cpp"
			}
		}
	}

	static void regamedll_hooker_src(def h) {
		h.regamedll_hooker_src(CppSourceSet) {
			source {
				srcDirs "hookers"
				include "6153_hooker.cpp", "hooker.cpp"
			}
		}
	}

	static void regamedll_hooker_main_src(def h) {
		h.regamedll_hooker_main_src(CppSourceSet) {
			source {
				srcDirs "hookers"
				include "main.cpp"
			}
		}
	}

	static void regamedll_mp_main_src(def h) {
		h.regamedll_mp_main_src(CppSourceSet) {
			source {
				srcDirs "hookers"
				include "main_mp.cpp"
				exclude "memory.cpp"
			}
		}
	}

	static void regamedll_tests_src(def h) {
		h.regamedll_tests_src(CppSourceSet) {
			source {
				srcDir "unittests"
				include "**/*.cpp"
			}
		}
	}
}


model {
	buildTypes {
		debug
		release
	}

	platforms {
		x86 {
			architecture "x86"
		}
	}

	toolChains {
		visualCpp(VisualCpp) {
		}
		icc(Icc) {
		}
	}

	flavors {
		regamedllNofixes
		regamedllFixes
	}

	components {
		regamedll_hooker_gamedll(NativeLibrarySpec) {
			targetPlatform 'x86'
			baseName 'filesystem_stdio'

			sources {
				RegamedllSrc.regamedll_pch(it)
				RegamedllSrc.regamedll_src(it)
				RegamedllSrc.regamedll_hooker_src(it)
				RegamedllSrc.regamedll_hooker_main_src(it)
			}

			binaries.all { NativeBinarySpec b -> project.setupToolchain(b) }
		}

		regamedll_mp_gamedll(NativeLibrarySpec) {
			targetPlatform 'x86'
			baseName GradleCppUtils.windows ? 'mp' : 'cs'
			sources {
				RegamedllSrc.regamedll_pch(it)
				RegamedllSrc.regamedll_src(it)
				RegamedllSrc.regamedll_mp_main_src(it)
			}
			binaries.all { NativeBinarySpec b -> project.setupToolchain(b) }
		}

		regamedll_hooker_gamedll_tests(NativeExecutableSpec) {
			targetPlatform 'x86'
			sources {
				RegamedllSrc.regamedll_pch(it)
				RegamedllSrc.regamedll_src(it)
				RegamedllSrc.regamedll_tests_src(it)
			}

			binaries.all { NativeBinarySpec b -> project.setupToolchain(b) }
		}

		regamedll_mp_gamedll_tests(NativeExecutableSpec) {
			targetPlatform 'x86'
			sources {
				RegamedllSrc.regamedll_pch(it)
				RegamedllSrc.regamedll_src(it)
				RegamedllSrc.regamedll_tests_src(it)
			}

			binaries.all { NativeBinarySpec b -> project.setupToolchain(b) }
		}
	}
}

task buildRelease {
	dependsOn binaries.withType(SharedLibraryBinarySpec).matching { SharedLibraryBinarySpec blib ->
		blib.buildable && blib.buildType.name == 'release' && !blib.name.contains('Regamedll_hooker_gamedll')
	}
}

task prepareDevEnvTests {
	def regamedllTests = new File(project.projectDir, '_dev/testDemos')

	inputs.files configurations.regamedll_tests.files
	outputs.dir regamedllTests

	doLast {
		regamedllTests.mkdirs()
		configurations.regamedll_tests.files.each { File f ->
			def t = zipTree(f)
			copy {
				into new File(regamedllTests, FilenameUtils.getBaseName(f.absolutePath))
				from t
			}
		}
	}
}

task prepareDevEnvGamedll << {
	['_dev/regamedll', '_dev/regamedll_mp'].each { gamedllDir ->
		def regamedllImage = new File(project.projectDir, gamedllDir)
		regamedllImage.mkdirs()
		def demoRunner = new RegamedllDemoRunner(project.configurations.regamedll_playtest_image.getFiles(), regamedllImage, null)
		demoRunner.prepareEngine()
		//demoRunner.prepareDemo()
	}
}

task prepareDevEnv {
	dependsOn prepareDevEnvGamedll, prepareDevEnvTests
}

tasks.clean.doLast {
	project.file('version/appversion.h').delete()
}

task generateAppVersion {

	RegamedllVersionInfo verInfo = (RegamedllVersionInfo) rootProject.regamedllVersionInfo
	def tplFile = project.file('version/appversion.vm')
	def renderedFile = project.file('version/appversion.h')

	inputs.file tplFile
	inputs.file project.file('gradle.properties')
	outputs.file renderedFile
	inputs.property('version', verInfo.asMavenVersion())
	inputs.property('lastCommitDate', verInfo.lastCommitDate.toString())

	doLast {
		def templateCtx = [
			verInfo: verInfo
		]

		def content = VelocityUtils.renderTemplate(tplFile, templateCtx)

		renderedFile.delete()
		renderedFile.write(content, 'utf-8')
	}
}