/******************************************************************************
Project: 	MicroMacro
Author: 	SolarStrike Software
URL:		www.solarstrike.net
License:	Modified BSD (see license.txt)
******************************************************************************/

#include "map_lua.h"
#include "error.h"
#include <iostream>       // std::cout
#include <thread>         // std::thread
#include <mutex>          // std::mutex
#include <map> 
#include <string.h>

std::mutex mtx;           // mutex for critical section
std::map<std::string,std::string> map_string;
std::map<std::string,int> map_int;
std::map<std::string,double> map_double;
std::map<std::string,const void*> map_pointer;
extern "C"
{
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
}

int Map_lua::regmod(lua_State *L)
{
	static const luaL_Reg _funcs[] = {
		{"get", Map_lua::get},
		{"set", Map_lua::set},
		{NULL, NULL}
	};

	luaL_newlib(L, _funcs);
	lua_setglobal(L, MAP_MODULE_NAME);

	return MicroMacro::ERR_OK;
}

/*	map.get(string key)
Returns:	string or int value


*/
int Map_lua::get(lua_State *L)
{
	mtx.lock();
	if( lua_gettop(L) != 1 )
		wrongArgs(L);
	checkType(L, LT_STRING, 1);

	size_t strlen;
	const char *str = lua_tolstring(L, 1, &strlen);

	std::string key = std::string(str);
	if(map_string.count(key) > 0){
		std::string value = map_string.at(key);
		lua_pushstring(L, value.c_str());
	}
	else
	{
		if(map_int.count(key) > 0){
			int value = map_int.at(key);
			lua_pushnumber(L, value);
		}
		else
		{
			if(map_double.count(key) > 0){
				double value = map_double.at(key);
				lua_pushnumber(L, value);
			}
			else
			{
				if(map_pointer.count(key) > 0){
					const void* value = map_pointer.at(key);
					//void* v2 = value;
					lua_pushlightuserdata(L,  const_cast<void*>(value));
				}
				lua_pushnil(L); 
			}
		}
	}
	mtx.unlock();
	return 1;
}

/*	map.set(string key, string value)
Returns:	nil

Returns 
*/
int Map_lua::set(lua_State *L)
{
	mtx.lock();

	if( lua_gettop(L) != 2 )
		wrongArgs(L);
	checkType(L, LT_STRING, 1);
	//checkType(L, LT_STRING, 2);
	if(lua_type (L, 2)== LT_NUMBER){
		std::string key = (char *)lua_tostring(L, 1);
		double value = lua_tonumber(L, -1);
		if (value == (int)value) {
			if(map_int.count(key) > 0){
				map_int.erase(key);
			}
			map_int[key] = (int)value;
		} 
		else
		{
			if(map_double.count(key) > 0){
				map_double.erase(key);
			}
			map_double[key] = value;
		}

	}
	else
	{
		if(lua_type (L, 2)== LT_STRING){
			//size_t strlen;
			std::string key = (char *)lua_tostring(L, 1);
			std::string value = (char *)lua_tostring(L, 2);
			if(map_string.count(key) > 0){
				map_string.erase(key);
			}
			map_string[key] = value;
		}
		else
		{
			int ty = lua_type (L, 2);

			if(ty == LT_FUNCTION || ty == LT_TABLE || ty == LT_USERDATA ){
				std::string key = (char *)lua_tostring(L, 1);
				const void* value = lua_topointer(L, 2);
				if(map_pointer.count(key) > 0){
					map_pointer.erase(key);
				}
				map_pointer[key] = value;
			}
			else
			{
				wrongArgs(L);
			}
		}
	}

	mtx.unlock();

	return 0;
}
int Map_lua::clear(lua_State *L)
{
	if( lua_gettop(L) != 0 )
		wrongArgs(L);

	map_double.clear();
	map_int.clear();
	map_string.clear();
	map_pointer.clear();

	return 0;
}

