/*
 * 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);
