#include <dbd/common.h>

const char *dbd_strlower(char *in) {
	char *s = in;

	while(*s) {
		*s= (*s <= 'Z' && *s >= 'A') ? (*s - 'A') + 'a' : *s;
		s++;
	}

	return in;
}

/*
 * replace '?' placeholders with {native_prefix}\d+ placeholders
 * to be compatible with native API
 */
char *dbd_replace_placeholders(lua_State *L, char native_prefix, const char *sql) {
	size_t len = strlen(sql);
	int num_placeholders = 0;
	int extra_space = 0;
	size_t i;
	char *newsql;
	int newpos = 1;
	int ph_num = 1;
	int in_quote = 0;
	char format_str[4];

	format_str[0] = native_prefix;
	format_str[1] = '%';
	format_str[2] = 'u';
	format_str[3] = '\0';

	/*
	 * dumb count of all '?'
	 * this will match more placeholders than necessesary
	 * but it's safer to allocate more placeholders at the
	 * cost of a few bytes than risk a buffer overflow
	 */
	for (i = 1; i < len; i++) {
		if (sql[i] == '?') {
			num_placeholders++;
		}
	}

	/*
	 * this is MAX_PLACEHOLDER_SIZE-1 because the '?' is
	 * replaced with '{native_prefix}'
	 */
	extra_space = num_placeholders * (MAX_PLACEHOLDER_SIZE-1);

	/*
	 * allocate a new string for the converted SQL statement
	 */
	newsql = calloc(len+extra_space+1, sizeof(char));
	if(!newsql) {
		lua_pushliteral(L, "out of memory");
		/* lua_error does not return. */
		lua_error(L);
	}

	/*
	 * copy first char. In valid SQL this cannot be a placeholder
	 */
	newsql[0] = sql[0];

	/*
	 * only replace '?' not in a single quoted string
	 */
	for (i = 1; i < len; i++) {
		/*
		 * don't change the quote flag if the ''' is preceded
		 * by a '\' to account for escaping
		 */
		if (sql[i] == '\'' && sql[i-1] != '\\') {
			in_quote = !in_quote;
		}

		if (sql[i] == '?' && !in_quote) {
			size_t n;

			if (ph_num > MAX_PLACEHOLDERS) {
				luaL_error(L, "Sorry, you are using more than %d placeholders. Use %c{num} format instead", MAX_PLACEHOLDERS, native_prefix);
			}

			n = snprintf(&newsql[newpos], MAX_PLACEHOLDER_SIZE, format_str, ph_num++);

			newpos += n;
		} else {
			newsql[newpos] = sql[i];
			newpos++;
		}
	}

	/*
	 * terminate string on the last position
	 */
	newsql[newpos] = '\0';

	/* fprintf(stderr, "[%s]\n", newsql); */
	return newsql;
}

void dbd_register(lua_State *L, const char *name,
                  const luaL_Reg *methods, const luaL_Reg *class_methods,
                  lua_CFunction gc, lua_CFunction tostring, lua_CFunction close)
{
	/* Create a new metatable with the given name and then assign the methods
	 * to it.  Set the __index, __gc and __tostring fields appropriately.
	 */
	luaL_newmetatable(L, name);
#if LUA_VERSION_NUM < 502
	luaL_register(L, 0, methods);
#else
	luaL_setfuncs(L, methods, 0);
#endif
	lua_pushvalue(L, -1);
	lua_setfield(L, -2, "__index");

	lua_pushcfunction(L, gc);
	lua_setfield(L, -2, "__gc");

	lua_pushcfunction(L, tostring);
	lua_setfield(L, -2, "__tostring");

	lua_pushcfunction(L, close);
	lua_setfield(L, -2, "__close");

	/* Create a new table and register the class methods with it */
	lua_newtable(L);
#if LUA_VERSION_NUM < 502
	luaL_register(L, 0, class_methods);
#else
	luaL_setfuncs(L, class_methods, 0);
#endif
}

