import org.doomedsociety.gradlecpp.GradleCppUtils
import org.doomedsociety.gradlecpp.toolchain.icc.IccCompilerPlugin
import org.doomedsociety.gradlecpp.toolchain.icc.Icc
import org.doomedsociety.gradlecpp.cfg.ToolchainConfig
import org.doomedsociety.gradlecpp.msvc.MsvcToolchainConfig
import org.doomedsociety.gradlecpp.gcc.GccToolchainConfig
import org.doomedsociety.gradlecpp.cfg.ToolchainConfigUtils
import org.gradle.language.cpp.CppSourceSet
import org.gradle.language.rc.tasks.WindowsResourceCompile
import org.gradle.nativeplatform.NativeBinarySpec
import versioning.MetamodVersionInfo
import gradlecpp.VelocityUtils

apply plugin: 'cpp'
apply plugin: 'windows-resources'
apply plugin: IccCompilerPlugin

List<Task> getRcCompileTasks(NativeBinarySpec binary) {
	def linkTask = GradleCppUtils.getLinkTask(binary)

	def res = linkTask.taskDependencies.getDependencies(linkTask).findAll { Task t -> t instanceof WindowsResourceCompile }
	return res as List
}

void postEvaluate(NativeBinarySpec b) {
	if (GradleCppUtils.windows) {
		getRcCompileTasks(b).each { Task t ->
			t.dependsOn project.generateAppVersion
		}
	} else {
		// attach generateAppVersion task to all 'compile source' tasks
		GradleCppUtils.getCompileTasks(b).each { Task t ->
			t.dependsOn project.generateAppVersion
		}
	}
}

void setupToolchain(NativeBinarySpec b)
{
	ToolchainConfig cfg = rootProject.createToolchainConfig(b)
	cfg.projectInclude(project, '', '/src', '/include/common', '/include/dlls', '/include/engine', '/include/game_shared', '/include/pm_shared', '/include/public')
	cfg.singleDefines 'METAMOD_CORE';

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

		cfg.extraLibs 'psapi.lib', 'user32.lib'
		cfg.singleDefines('_CRT_SECURE_NO_WARNINGS')
	} else if (cfg instanceof GccToolchainConfig) {
		cfg.compilerOptions.pchConfig = new GccToolchainConfig.PrecompilerHeaderOptions(
			enabled: true,
			pchSourceSet: 'rmod_pch'
		)
		cfg.compilerOptions.languageStandard = 'c++0x'
		cfg.defines([
			'_stricmp': 'strcasecmp',
			'_strnicmp': 'strncasecmp',
			'_strdup': 'strdup',
			'_unlink': 'unlink',
			'_write' : 'write',
			'_close' : 'close',
			'_getcwd' : 'getcwd',
			'_vsnprintf': 'vsnprintf',
			'_vsnwprintf': 'vswprintf',
			'_snprintf': 'snprintf'
		])

		cfg.compilerOptions.args '-Qoption,cpp,--treat_func_as_string_literal_cpp', '-msse2', '-fomit-frame-pointer', '-inline-forceinline', '-fvisibility=default', '-fvisibility-inlines-hidden', '-fno-rtti', '-g0', '-s', '-fno-exceptions'
	}

	ToolchainConfigUtils.apply(project, cfg, b)

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

model {
	buildTypes {
		debug
		release
	}

	platforms {
		x86 {
			architecture "x86"
		}
	}

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

	components {
		metamod(NativeLibrarySpec) {
			targetPlatform 'x86'
			baseName GradleCppUtils.windows ? 'metamod' : 'metamod_i386'

			sources {
				rmod_pch(CppSourceSet) {
					source {
						srcDirs "src"
						include "precompiled.cpp"
					}

					exportedHeaders {
						srcDirs "include"
					}
				}
				rmod_src(CppSourceSet) {
					source {
						srcDir "src"
						srcDir "version"
						include "**/*.cpp"

						exclude "precompiled.cpp"
					}

					exportedHeaders {
						srcDirs "include"
					}
				}
				rc {
					source {
						srcDirs "msvc"
						include "metamod.rc"
					}
					exportedHeaders {
						srcDirs "msvc"
					}
				}
			}
			binaries.all {
				NativeBinarySpec b -> project.setupToolchain(b)
			}
		}
	}
}

afterEvaluate {
	project.binaries.all {
		NativeBinarySpec binary ->
		Tool linker = binary.linker

		if (GradleCppUtils.windows) {
			linker.args "/DEF:${projectDir}\\msvc\\metamod.def"
		}
	}
}

task buildFinalize << {
	if (GradleCppUtils.windows) {
		return;
	}

	binaries.withType(SharedLibraryBinarySpec) {
		def sharedBinary = it.getSharedLibraryFile();
		if (sharedBinary.exists()) {
			sharedBinary.renameTo(new File(sharedBinary.getParent() + "/" + sharedBinary.getName().replaceFirst("^lib", "")));
		}
	}
}

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

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

task generateAppVersion {

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

	// check to up-to-date
	inputs.file tplFile
	inputs.file project.file('gradle.properties')
	outputs.file renderedFile

	// this will ensure that this task is redone when the versions change
	inputs.property('version', rootProject.version)
	inputs.property('commitDate', verInfo.asCommitDate())

	println "##teamcity[buildNumber '" + verInfo.asMavenVersion(false) + "']";

	doLast {
		def templateCtx = [
			verInfo: verInfo
		]

		def content = VelocityUtils.renderTemplate(tplFile, templateCtx)
		renderedFile.delete()
		renderedFile.write(content, 'utf-8')

		println 'The current Metamod maven version is ' + rootProject.version + ', url: (' + verInfo.commitURL + '' + verInfo.commitSHA + ')';
	}
}