#!/usr/bin/env ascript

ascriptDir = path.dirname(sys.executable)
extraDir = path.join(ascriptDir, 'extra')

if (sys.build == `msc) {
	//-------------------------------------------------------------------------
	outDir = 'msc'
	cflags = [
		'/nologo', '/DASCRIPT_MODULE_SEPARATED'
		'/EHsc'
		'/I' + path.join(ascriptDir, 'include')
		'/I' + path.join(ascriptDir, 'extra', 'include')
	]
	ldflags = [
		'/nologo', '/LD', '/MT'
	]
	libs = [
		path.join(ascriptDir, 'lib', 'libascript.lib')
	]
	objname(outDir:string, src:string):map = {
		path.join(outDir, path.basename(src).replace('.cpp', '.obj').replace('.c', '.obj'))
	}
	compile(outDir:string, src:string, msc:dict, bcc:dict, gnuc:dict,
								verboseFlag:boolean, debugFlag:boolean):map = {
		obj = objname(outDir, src)
		cflagsAdd = []
		if (msc.haskey(`cflags)) {
			cflagsAdd = msc[`cflags]
		}
		if (msc.haskey(`incDirs)) {
			cflagsAdd.append('-I' + msc[`incDirs])
		}
		if (debugFlag) {
			cflagsAdd.append('/ZI')
		}
		result = true
		if (!path.exists(obj) || path.stat(obj).mtime < path.stat(src).mtime) {
			buff = b''
			os.stdout = os.stderr = buff.stream()
			verboseFlag && println('compile: ', src)
			result = (os.exec('cl', cflagsAdd*, cflags*, '/Fo' + obj, '/c', src) == 0)
			if (!result) {
				sys.stdout.write(buff)
				return(false)
			}
		}
		result
	}
	link(outDir:string, target:string, objs[]:string, msc:dict, bcc:dict, gnuc:dict,
								verboseFlag:boolean, debugFlag:boolean) = {
		target = path.join(outDir, target)
		ldflagsAdd = []
		libsAdd = []
		libsExp = []
		if (msc.haskey(`libs)) {
			libsAdd = msc[`libs]
		}
		if (msc.haskey(`dlls)) {
			dlls = msc[`dlls]
			libsExp = makeLibPath(dlls::replace('.dll', '.lib'):icase)
		}
		if (debugFlag) {
			ldflagsAdd.append(['/Zi'])
		}
		libsAll = list(libs, libsExp, libsAdd)
		libsStd = [
			'kernel32.lib', 'user32.lib', 'gdi32.lib', 'winspool.lib', 'comdlg32.lib',
			'advapi32.lib', 'shell32.lib', 'ole32.lib', 'oleaut32.lib', 'uuid.lib',
			'odbc32.lib', 'odbccp32.lib', 'ws2_32.lib', 'opengl32.lib', 'glu32.lib',
		]
		if (!path.exists(target) || (path.stat(target).mtime < path.stat(objs):*mtime).or() || \
						(path.stat(target).mtime < path.stat(libsAll):*mtime).or()) {
			buff = b''
			os.stdout = os.stderr = buff.stream()
			result = (os.exec('cl', ldflags*, ldflagsAdd*,
								'/Fe' + target, objs*, libsAll*, libsStd*) == 0)
			if (!result) {
				sys.stdout.write(buff)
				return(false)
			}
			println(target, ' was created')
		}
		true
	}
	clean(outDir:string, target:string, srcs[]:string, verboseFlag:boolean) = {
		for (fileName in path.dir(outDir, target, '*.obj', '*.exp', '*.lib')) {
			fs.remove(fileName)
			verboseFlag && println('deleted: ', fileName)
		}
	}
	makeLibPath(lib:string):map = {
		if (lib.find('/') || lib.find('\\')) {
			libPath = lib
		} else {
			candList = path.join(os.getenv('PATH').split(';'), lib)
			candList = candList.filter(path.exists(candList)):list
			if (candList.isempty()) {
				sys.stderr.println(lib, ' has not been found in search paths\n')
				sys.exit(1)
			}
			libPath = candList.first()
		}
		libPath
	}
} elsif (sys.build == `bcc) {
	//-------------------------------------------------------------------------
	outDir = 'bcc'
	candList = os.getenv('PATH').split(';')
	candList = candList.filter(path.exists(path.join(candList, 'bcc32.exe'))):list
	if (candList.isempty()) {
		sys.stderr.println(
			'bcc32.exe has not been found. Please install Borland-C and set PATH environment\n'
			'to include a search path for it.\n')
		sys.exit(1)
	}
	bccDir = path.join(candList.first(), '..')
	cflags = [
		'-w-8004', '-w-8057', '-DASCRIPT_MODULE_SEPARATED', '-q'
		'-I' + path.join(bccDir, 'Include')
		'-I' + path.join(ascriptDir, 'include')
		'-I' + path.join(ascriptDir, 'extra', 'include')
	]
	ldflags = [
		'-tWD', '-tWM', '-M', '-q'
		'-L' + path.join(bccDir, 'Lib')
		'-L' + path.join(bccDir, 'Lib', 'psdk')
	]
	libs = [
		path.join(ascriptDir, 'lib', 'libascript.bcc.lib')
	]
	objname(outDir:string, src:string):map = {
		path.join(outDir, path.basename(src).replace('.cpp', '.obj').replace('.c', '.obj'))
	}
	compile(outDir:string, src:string, msc:dict, bcc:dict, gnuc:dict,
								verboseFlag:boolean, debugFlag:boolean):map = {
		obj = objname(outDir, src)
		cflagsAdd = []
		if (bcc.haskey(`cflags)) {
			cflagsAdd = bcc[`cflags]
		}
		if (bcc.haskey(`incDirs)) {
			cflagsAdd.append('-I' + bcc[`incDirs])
		}
		if (debugFlag) {
			cflagsAdd.append('-v')
		}
		result = true
		if (!path.exists(obj) || path.stat(obj).mtime < path.stat(src).mtime) {
			buff = b''
			os.stdout = buff.stream()
			verboseFlag && println('compile: ', src)
			result = (os.exec('bcc32', cflagsAdd*, cflags*, '-o' + obj, '-c', src) == 0)
			if (!result) {
				sys.stdout.write(buff)
				return(false)
			}
		}
		result
	}
	link(outDir:string, target:string, objs[]:string, msc:dict, bcc:dict, gnuc:dict,
								verboseFlag:boolean, debugFlag:boolean) = {
		target = path.join(outDir, target)
		ldflagsAdd = []
		libsAdd = []
		libsExp = []
		if (debugFlag) {
			ldflagsAdd.append('-v')
		}
		addUnderscoreFlag = !bcc.haskey(`addUnderscoreFlag) || bcc[`addUnderscoreFlag]
		if (bcc.haskey(`libs)) {
			libsAdd = bcc[`libs]
		}
		if (bcc.haskey(`dlls)) {
			dlls = bcc[`dlls]
			libsExp = path.basename(dlls::replace('.dll', '.bcc.lib'):icase)
			libsExp = path.join(outDir, libsExp)
			!implib(outDir, libsExp, dlls, addUnderscoreFlag, verboseFlag).and() && return(false)
		}
		libsAll = list(libs, libsExp, libsAdd)
		if (!path.exists(target) || (path.stat(target).mtime < path.stat(objs):*mtime).or() || \
						(path.stat(target).mtime < path.stat(libsAll):*mtime).or()) {
			buff = b''
			os.stdout = buff.stream()
			result = (os.exec('bcc32', ldflags*, ldflagsAdd*,
										'-e' + target, objs*, libsAll*) == 0)
			if (!result) {
				sys.stdout.write(buff)
				return(false)
			}
			println(target, ' was created')
		}
		true
	}
	clean(outDir:string, target:string, srcs[]:string, verboseFlag:boolean) = {
		for (fileName in path.dir(outDir, target,
								'*.obj', '*.map', '*.tds', '*.bcc.lib')) {
			fs.remove(fileName)
			verboseFlag && println('deleted: ', fileName)
		}
	}
	implib(outDir:string, lib:string, dll:string,
					addUnderscoreFlag:boolean, verboseFlag:boolean):map = {
		if (dll.find('/') || dll.find('\\')) {
			dllPath = dll
		} else {
			candList = path.join(os.getenv('PATH').split(';'), dll)
			candList = candList.filter(path.exists(candList)):list
			if (candList.isempty()) {
				sys.stderr.println(dll, ' has not been found in search paths\n')
				sys.exit(1)
			}
			dllPath = candList.first()
		}
		result = true
		if (!path.exists(lib) || path.stat(lib).mtime < path.stat(dllPath).mtime) {
			verboseFlag && println('implib: ', dllPath)
			os.stdout = nil
			if (addUnderscoreFlag) {
				result = (os.exec('implib', '-a', lib, dllPath) == 0)
			} else {
				result = (os.exec('implib', lib, dllPath) == 0)
			}
		}
		result
	}
} elsif (sys.build == `gnuc) {
	//-------------------------------------------------------------------------
	outDir = 'gnuc'
	cflags = [
		'-DASCRIPT_MODULE_SEPARATED'
		'-fPIC'
	]
	ldflags = [
		'-shared'
	]
	libs = [
	]
	objname(outDir:string, src:string):map = {
		path.join(outDir, path.basename(src).replace('.cpp', '.o').replace('.c', '.o'))
	}
	compile(outDir:string, src:string, msc:dict, bcc:dict, gnuc:dict,
								verboseFlag:boolean, debugFlag:boolean):map = {
		obj = objname(outDir, src)
		cflagsAdd = []
		if (gnuc.haskey(`cflags)) {
			cflagsAdd = gnuc[`cflags]
		}
		if (gnuc.haskey(`incDirs)) {
			cflagsAdd.append('-I' + gnuc[`incDirs])
		}
		if (debugFlag) {
			cflagsAdd.append('-g')
		}
		result = true
		if (!path.exists(obj) || path.stat(obj).mtime < path.stat(src).mtime) {
			buff = b''
			os.stdout = os.stderr = buff.stream()
			verboseFlag && println('compile: ', src)
			result = (os.exec('gcc', cflagsAdd*, cflags*, '-o', obj, '-c', src) == 0)
			if (!result) {
				if (verboseFlag) {
					sys.stdout.write(buff)
				} else {
				    println('error while compiling ', src)
				}
				gnuc.haskey(`hint) && print(gnuc[`hint])
				return(false)
			}
		}
		result
	}
	link(outDir:string, target:string, objs[]:string, msc:dict, bcc:dict, gnuc:dict,
								verboseFlag:boolean, debugFlag:boolean) = {
		target = path.join(outDir, target)
		ldflagsAdd = []
		if (debugFlag) {
			ldflagsAdd.append('-g')
		}
		libsAdd = []
		if (gnuc.haskey(`libs)) {
			libsAdd = gnuc[`libs]
		}
		libsAll = '-l' + list(libs, libsAdd)
		if (!path.exists(target) || (path.stat(target).mtime < path.stat(objs):*mtime).or()) {
			buff = b''
			os.stdout = os.stderr = buff.stream()
			result = (os.exec('gcc', ldflags*, ldflagsAdd*, '-o', target, objs*, libsAll*) == 0)
			if (!result) {
				if (verboseFlag) {
					sys.stdout.write(buff)
				} else {
				    println('error while linking ', target)
				}
				gnuc.haskey(`hint) && print(gnuc[`hint])
				return(false)
			}
			println(target, ' was created')
		}
		true
	}
	clean(outDir:string, target:string, srcs[]:string, verboseFlag:boolean) = {
		for (fileName in path.dir(outDir, target, '*.o')) {
			fs.remove(fileName)
			verboseFlag && println('deleted: ', fileName)
		}
	}
}

install(outDir:string, target:string, targetAlias:string, siteFlag:boolean) = {
	if (siteFlag) {
		targetPath = path.join(sys.libdir, 'site', targetAlias)
	} else {
		targetPath = path.join(sys.libdir, targetAlias)
	}
	dirName = path.dirname(targetPath)
	!path.exists(dirName) && fs.mkdir(dirName)
	copy(path.join(outDir, target), open(targetPath, 'w'))
}

build(target:string, srcs[]:string, targetAlias?:string) {`block?} = {
	msc = %{}
	bcc = %{}
	gnuc = %{}
	block && eval(block)
	if ((sys.argv == '--here').or()) {
		outDir = '.'
	}
	verboseFlag = (sys.argv == '--verbose').or()
	debugFlag = (sys.argv == '--debug').or()
	if ((sys.argv == 'clean').or()) {
		path.exists(outDir) && clean(outDir, target, srcs, verboseFlag)
		return
	}
	if ((sys.argv == '--rebuild').or()) {
		path.exists(outDir) && clean(outDir, target, srcs, verboseFlag)
	}
	!path.exists(outDir) && fs.mkdir(outDir)
	!compile(outDir, srcs, msc, bcc, gnuc, verboseFlag, debugFlag).and() && return(false)
	objs = objname(outDir, srcs)
	!link(outDir, target, objs, msc, bcc, gnuc, verboseFlag, debugFlag) && return(false)
	if ((sys.argv == 'install').or()) {
		install(outDir, target, targetAlias || target, true)
	}
	if ((sys.argv == 'install-root').or()) {
		install(outDir, target, targetAlias || target, false)
	}
	true
}
