/*
 * mod_lua_system.c
 *
 * Copyright (C) 2018-2020 Aerospike, Inc.
 *
 * Portions may be licensed to Aerospike, Inc. under one or more contributor
 * license agreements.
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any
 * later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see http://www.gnu.org/licenses/
 */

#include <aerospike/as_std.h>

// Auto-generated from system lua code in the lua-core repo.

const char as_lua_as[] =
"Map = Map or getmetatable(map())\n"
"List = List or getmetatable(list())\n"
"Bytes = Bytes or getmetatable(bytes())\n"
"function list.clone(l)\n"
"	local ll = {}\n"
"	for v in list.iterator(l) do\n"
"		table.insert(ll, v)\n"
"	end\n"
"	return list(ll)\n"
"end\n"
"function list.merge(l1, l2)\n"
"	local ll = {}\n"
"	for v in list.iterator(l1) do\n"
"		table.insert(ll,v)\n"
"	end\n"
"	for v in list.iterator(l2) do\n"
"		table.insert(ll, v)\n"
"	end\n"
"	return list(ll)\n"
"end\n"
"function map.merge(m1,m2,f)\n"
"	local mm = {}\n"
"	for k,v in map.pairs(m1) do\n"
"		mm[k] = v\n"
"	end\n"
"	for k,v in map.pairs(m2) do\n"
"		mm[k] = (mm[k] and f and type(f) == 'function' and f(m1[k],m2[k])) or v\n"
"	end\n"
"	return map(mm, map.size(m1) + map.size(m2))\n"
"end\n"
"function map.diff(m1,m2)\n"
"	local mm = {}\n"
"	for k,v in map.pairs(m1) do\n"
"		if not m2[k] then\n"
"			mm[k] = v\n"
"		end\n"
"	end\n"
"	for k,v in map.pairs(m2) do\n"
"		if not m1[k] then\n"
"			mm[k] = v\n"
"		end\n"
"	end\n"
"	return map(mm, map.size(m1) + map.size(m2))\n"
"end\n"
"function map.clone(m)\n"
"	local mm = {}\n"
"	for k,v in map.pairs(m) do\n"
"		mm[k] = v\n"
"	end\n"
"	return map(mm, map.size(m))\n"
"end\n"
"function math.sum(a,b) \n"
"	return a + b\n"
"end\n"
"function math.product(a, b)\n"
"	return a * b\n"
"end\n"
;

size_t as_lua_as_size = sizeof(as_lua_as);

const char as_lua_stream_ops[] =
"local function check_limit(v)\n"
"	return type(v) == 'number' and v >= 1000\n"
"end\n"
"local function clone_table(t)\n"
"	local out = {}\n"
"	for k,v in pairs(t) do\n"
"		out[k] = v\n"
"	end\n"
"	return out\n"
"end\n"
"local function clone(v)\n"
"	local t = type(v)\n"
"	if t == 'number' then\n"
"		return v\n"
"	elseif t == 'string' then\n"
"		return v\n"
"	elseif t == 'boolean' then\n"
"		return v\n"
"	elseif t == 'table' then\n"
"		return clone_table(v)\n"
"	elseif t == 'userdata' then\n"
"		if getmetatable(v) == Map then\n"
"			return map.clone(v)\n"
"		elseif getmetatable(v) == List then\n"
"			return list.clone(v)\n"
"		end\n"
"		return nil\n"
"	end\n"
"	return v\n"
"end\n"
"function filter( next, p )\n"
"	local done = false\n"
"	return function()\n"
"		if done then return nil end\n"
"		for a in next do\n"
"			if p(a) then\n"
"				return a\n"
"			end\n"
"		end\n"
"		done = true\n"
"		return nil\n"
"	end\n"
"end\n"
"function transform( next, f )\n"
"	local done = false\n"
"	return function()\n"
"		if done then return nil end\n"
"		local a = next()\n"
"		if a ~= nil then\n"
"			return f(a)\n"
"		end\n"
"		done = true;\n"
"		return nil\n"
"	end\n"
"end\n"
"function reduce( next, f )\n"
"	local done = false\n"
"	return function()\n"
"		if done then return nil end\n"
"		local a = next()\n"
"		if a ~= nil then\n"
"			for b in next do\n"
"				a = f(a,b)\n"
"			end\n"
"		end\n"
"		done = true\n"
"		return a\n"
"	end\n"
"end\n"
"function aggregate( next, init, f )\n"
"	local done = false\n"
"	return function()\n"
"		if done then return nil end\n"
"		local a = clone(init)\n"
"		for b in next do\n"
"			a = f(a,b)\n"
"			if check_limit(a) then\n"
"				return a\n"
"			end\n"
"		end\n"
"		done = true\n"
"		return a\n"
"	end\n"
"end\n"
"function stream_iterator(s)\n"
"	local done = false\n"
"	return function()\n"
"		if done then return nil end\n"
"		local v = stream.read(s)\n"
"		if v == nil then\n"
"			done = true\n"
"		end\n"
"		return v;\n"
"	end\n"
"end\n"
"StreamOps = {}\n"
"StreamOps_mt = { __index = StreamOps }\n"
"local SCOPE_SERVER = 1\n"
"local SCOPE_CLIENT = 2\n"
"local SCOPE_EITHER = 3\n"
"local SCOPE_BOTH = 4\n"
"function StreamOps_create()\n"
"	local self = {}\n"
"	setmetatable(self, StreamOps_mt)\n"
"	self.ops = {}\n"
"	return self\n"
"end\n"
"function StreamOps_apply(stream, ops, i, n)\n"
"	i = i or 1\n"
"	n = n or #ops\n"
"	if i > n then return stream end\n"
"	local op = ops[i]\n"
"	local s = op.func(stream, unpack(op.args)) or stream\n"
"	return StreamOps_apply(s, ops, i + 1, n)\n"
"end\n"
"function StreamOps_select(stream_ops, scope)\n"
"	local server_ops = {}\n"
"	local client_ops = {}\n"
"	local phase = SCOPE_SERVER\n"
"	for i,op in ipairs(stream_ops) do\n"
"		if phase == SCOPE_SERVER then\n"
"			if op.scope == SCOPE_SERVER then\n"
"				table.insert(server_ops, op)\n"
"			elseif op.scope == SCOPE_EITHER then\n"
"				table.insert(server_ops, op)\n"
"			elseif op.scope == SCOPE_BOTH then\n"
"				table.insert(server_ops, op)\n"
"				table.insert(client_ops, op)\n"
"				phase = SCOPE_CLIENT\n"
"			end\n"
"		elseif phase == SCOPE_CLIENT then\n"
"			table.insert(client_ops, op)\n"
"		end \n"
"	end\n"
"	if scope == SCOPE_CLIENT then\n"
"		return client_ops\n"
"	else\n"
"		return server_ops\n"
"	end\n"
"end\n"
"function StreamOps:aggregate(...)\n"
"	table.insert(self.ops, { scope = SCOPE_SERVER, name = \"aggregate\", func = aggregate, args = {...}})\n"
"	return self\n"
"end\n"
"function StreamOps:reduce(...)\n"
"	table.insert(self.ops, { scope = SCOPE_BOTH, name = \"reduce\", func = reduce, args = {...}})\n"
"	return self\n"
"end\n"
"function StreamOps:map(...)\n"
"	table.insert(self.ops, { scope = SCOPE_EITHER, name = \"map\", func = transform, args = {...}})\n"
"	return self\n"
"end\n"
"function StreamOps:filter(...)\n"
"	table.insert(self.ops, { scope = SCOPE_EITHER, name = \"filter\", func = filter, args = {...}})\n"
"	return self\n"
"end\n"
"function StreamOps:groupby(f)\n"
"	local function _aggregate(m, v)\n"
"		local k = f and f(v) or nil;\n"
"		local l = m[k] or list()\n"
"		list.append(l, v)\n"
"		m[k] = l;\n"
"		return m;\n"
"	end\n"
"	local function _merge(l1, l2)\n"
"		local l = list.clone(l1)\n"
"		for v in list.iterator(l2) do\n"
"			list.append(l, v)\n"
"		end\n"
"		return l\n"
"	end\n"
"	function _reduce(m1, m2)\n"
"		return map.merge(m1, m2, _merge)\n"
"	end\n"
"	return self : aggregate(map(), _aggregate) : reduce(_reduce)\n"
"end\n"
;

size_t as_lua_stream_ops_size = sizeof(as_lua_stream_ops);

const char as_lua_aerospike[] =
"function trace(m, ...)\n"
"	return aerospike:log(4, string.format(m, ...))\n"
"end\n"
"function debug(m, ...)\n"
"	return aerospike:log(3, string.format(m, ...))\n"
"end\n"
"function info(m, ...)\n"
"	return aerospike:log(2, string.format(m, ...))\n"
"end\n"
"function warn(m, ...)\n"
"	return aerospike:log(1, string.format(m, ...))\n"
"end\n"
"function apply_record(f, r, ...)\n"
"	if f == nil then\n"
"		error(\"function not found\", 2)\n"
"	end\n"
"	success, result = pcall(f, r, ...)\n"
"	if success then\n"
"		return result\n"
"	else\n"
"		error(result, 2)\n"
"		return nil\n"
"	end\n"
"end\n"
"function apply_stream(f, scope, istream, ostream, ...)\n"
"	if f == nil then\n"
"		error(\"function not found\", 2)\n"
"		return 2\n"
"	end\n"
"	local stream_ops = StreamOps_create();\n"
"	success, result = pcall(f, stream_ops, ...)\n"
"	if success then\n"
"		local ops = StreamOps_select(result.ops, scope);\n"
"		local values = StreamOps_apply(stream_iterator(istream), ops);\n"
"		for value in values do\n"
"			if stream.write(ostream, value) ~= 0 then\n"
"				break\n"
"			end\n"
"		end\n"
"		stream.write(ostream, nil)\n"
"		return 0\n"
"	else\n"
"		error(result, 2)\n"
"		return 2\n"
"	end\n"
"end\n"
;

size_t as_lua_aerospike_size = sizeof(as_lua_aerospike);
