

plapi := import("plapi")
fmt := import("fmt")
log := import("log")
text := import("text")




print := func(txt, ...args) {
	plapi.print(txt, args...)
}






panic := func(msg, ...args) {
	txt := fmt.sprintf(msg, args...)
	plapi.print(txt)


	plapi.setTemplateError(txt)
}




assert := func(condition, msg, ...args) {
	if !condition {
		panic("assertion error: condition failed: "+msg, args...)
	}
}







isStrict := func(obj) {
	return plapi.isStrictMap(obj)
}







isMap := func(obj) {
	return is_map(obj) || is_immutable_map(obj) || isStrict(obj)
}








fromStrict := func(r) {
	if is_map(r) {
		return r
	}

	if !isStrict(r) {
		panic("not a map", r)
	}

	return plapi.strictToMap(r)
}







ensureNonStrict := func(r) {
	if isStrict(r) {
		return fromStrict(r)
	}

	return r
}










toStrict := func(v, ...recursive) {
	if len(recursive) > 0 && recursive[0] {
		if isMap(v) {
			for k, vv in v {
				v[k] = toStrict(vv)
			}
		} else if is_array(v) {
			for i, vv in v {
				if isMap(vv) {
					v[i] = toStrict(vv)
				}
			}
		}

		if !isMap(v) {
			return v // for recursive transformation, accept the top-level value to be of any type (i.e. array of maps)
		}
	}

	if isStrict(v) {
		return v
	}

	if(!isMap(v)) {
		panic("toStrict(<v>): <v> is not a map", v)
	}
	return plapi.mapToStrict(v)
}









methodExists := func(module, methodName) {
	if isStrict(module) {
		if !is_callable(plapi.mapHasKey) {
			module = fromStrict(module)

		} else {
			if !plapi.mapHasKey(module, methodName) {
				return false
			}
		}
	}

	method := module[methodName]

	if is_undefined(method) {
		return false
	}

	return true
}




apiVersion := func() {
	if !methodExists(plapi, "apiVersion") {
		return 0 // before we introduced apiVersion
	}

	return plapi.apiVersion
}




fieldId := func(resourceId, fieldName) {
	return plapi.newFieldID(resourceId, fieldName)
}




isFieldId := func(val) {
	if !isStrict(val) {
		return false
	}
	v := fromStrict(val)
	return v["ResourceID"] != undefined && v["Name"] != undefined
}




idToString := func(referenceID) {
	if isFieldId(referenceID) {
		return fmt.sprintf("0x%X/%s", referenceID.ResourceID, referenceID.Name)
	}
	if is_int(referenceID) {
		return fmt.sprintf("0x%X", referenceID)
	}
	panic("ll.idToString: <referenceID> cannot be used as resource or field ID")
}




getCurrentTemplateRenderer := func() {
	return plapi.getTemplate()
}




isInitializing := func() {
	return plapi.isInit
}






getExecutorsList := func() {
	return plapi.getExecutors()
}






filepathJoin := func(...elems) {
	return plapi.filepathJoin(elems...)
}







filepathRegexpJoin := func(...elems) {
	escapedSeparator := plapi.regexpQuoteMeta(plapi.osSeparator)
	return text.join(elems, escapedSeparator)
}








filepathCanonize := func(...elem) {
	return plapi.filepathCanonize(elem...)
}






pathSeparator := plapi.osSeparator


























parseUrl := func(url) {
	return plapi.urlParse(url)
}






getCtx := func() {
	if methodExists(plapi, "getPersistentCtx") {
		return plapi.getPersistentCtx()
	}


	return plapi.getCtx()
}





getPersistentCtx := func() {
	if methodExists(plapi, "getPersistentCtx") {
		return plapi.getPersistentCtx()
	}


	return plapi.getCtx()
}











getExecutionCtx := func() {
	assert(methodExists(plapi, "getExecutionCtx"), "ll.getExecutionCtx: method does not exists in this backend instance.")
	return plapi.getExecutionCtx()
}







moduleVars := func(moduleName) {
	assert(is_string(moduleName), "ll.moduleVars: moduleName must be a string")

	name := "module/" + moduleName

	ctx := getExecutionCtx()
	if is_undefined(ctx[name]) {
		ctx[name] = {}
	}

	return {
		get: func(k) {
			return ctx[name][k]
		},

		set: func(k, v) {
			ctx[name][k] = v
		}
	}
}




sha256Encode := func(b) {
	assert(methodExists(plapi, "sha256Encode"), "ll.sha256Encode: method does not exists in this backend instance.")
	return plapi.sha256Encode(b)
}




gzipEncode := func(b) {
	assert(methodExists(plapi, "gzipEncode"), "ll.gzipEncode: method does not exists in this backend instance.")
	return plapi.gzipEncode(b)
}




gzipDecode := func(b) {
	assert(methodExists(plapi, "gzipDecode"), "ll.gzipDecode: method does not exists in this backend instance.")
	return plapi.gzipDecode(b)
}




base32Encode := func(b) {
	assert(methodExists(plapi, "base32Encode"), "ll.base32Encode: method does not exists in this backend instance.")
	return plapi.base32Encode(b)
}








base32HexEncode := func(b) {
	assert(methodExists(plapi, "base32HexEncode"), "ll.base32HexEncode: method does not exists in this backend instance.")
	return plapi.base32HexEncode(b)
}




base32Decode := func(s) {
	assert(methodExists(plapi, "base32Decode"), "ll.base32Decode: method does not exists in this backend instance.")
	return plapi.base32Decode(s)
}








base32HexDecode := func(s) {
	assert(methodExists(plapi, "base32HexDecode"), "ll.base32HexDecode: method does not exists in this backend instance.")
	return plapi.base32HexDecode(s)
}




getBlobSize := func(resourceID) {
	assert(methodExists(plapi, "getBlobSize"), "ll.getBlobSize: method does not exists in this backend instance.")
	return plapi.getBlobSize(resourceID)
}

export toStrict({
	apiVersion: apiVersion,
	print: print,
	panic: panic,
	assert: assert,
	methodExists: methodExists,
	isStrict: isStrict,
	isMap: isMap,
	fromStrict: fromStrict,
	ensureNonStrict: ensureNonStrict,
	toStrict: toStrict,
	fieldId: fieldId,
	isFieldId: isFieldId,
	idToString: idToString,
	getCurrentTemplateRenderer: getCurrentTemplateRenderer,
	isInitializing: isInitializing,
	getExecutorsList: getExecutorsList,
	filepathJoin: filepathJoin,
	filepathRegexpJoin: filepathRegexpJoin,
	pathSeparator: pathSeparator,
	filepathCanonize: filepathCanonize,
	parseUrl: parseUrl,
	getCtx: getCtx,
	getPersistentCtx: getPersistentCtx,
	getExecutionCtx: getExecutionCtx,
	moduleVars: moduleVars,
	sha256Encode: sha256Encode,
	gzipEncode: gzipEncode,
	gzipDecode: gzipDecode,
	base32Encode: base32Encode,
	base32HexEncode: base32HexEncode,
	base32Decode: base32Decode,
	base32HexDecode: base32HexDecode,
	getBlobSize: getBlobSize
})
