Revision 1 (by (no author), 2006/09/28 22:53:53) Initial import
/// \file core.cpp
/// Core classes

#include <string.h>
#include <sstream>
#include "core.h"

extern "C" {
#include <lauxlib.h>
}

using namespace std;

#include <iostream>

namespace Board
{

template <class C> static int Trampoline(lua_State* state)
{
	if(!lua_isuserdata(state, 1))
	{
		lua_pushstring(state, "native method called without object");
		return lua_error(state);
	}
	C* obj = *(C**) lua_touserdata(state, 1);
	
	int (C::*fn)(lua_State*) = *(int (C::**)(lua_State*)) lua_touserdata(state, lua_upvalueindex(1));
	
	return (obj->*fn)(state);
}

static int RegisterWeakRef(lua_State* state)
{
	lua_getfield(state, LUA_REGISTRYINDEX, "weakrefs");
	if(lua_isnil(state, -1))
	{
		lua_pop(state, 1);
		lua_createtable(state, 1, 0);
		lua_createtable(state, 0, 1);
		lua_pushstring(state, "kv");
		lua_setfield(state, -2, "__mode");
		lua_setmetatable(state, -2);
		lua_pushvalue(state, -1);
		lua_setfield(state, LUA_REGISTRYINDEX, "weakrefs");
	}
	lua_pushvalue(state, -2);
	int refnum = luaL_ref(state, -2);
	lua_pop(state, 1);
	return refnum;
}

static void GetWeakRef(lua_State* state, int ref)
{
	lua_getfield(state, LUA_REGISTRYINDEX, "weakrefs");
	if(lua_isnil(state, -1))
		return;
	lua_pushinteger(state, ref);
	lua_gettable(state, -2);
	lua_remove(state, -2);
}

static void UnregisterWeakRef(lua_State* state, int ref)
{
	lua_getfield(state, LUA_REGISTRYINDEX, "weakrefs");
	if(lua_isnil(state, -1))
		return;
	luaL_unref(state, -1, ref);
}

void RegisterAll(lua_State* state)
{
	Card::Register(state);
	Pawn::Register(state);
	Board::Register(state);
}

void Card::Register(lua_State* state)
{
	int (Card::*func)(lua_State* state);
	void* data;
	
	lua_createtable(state, 0, 1);
	
	lua_pushcfunction(state, &Card::New);
	lua_setfield(state, -2, "new");
	
	lua_setglobal(state, "Card");
}

int Card::New(lua_State* state)
{
	int (Card::*func)(lua_State* state);
	void* data;
	
	if(lua_gettop(state) != 4)
	{
		lua_pushstring(state, "wrong number of arguments");
		return lua_error(state);
	}
	
	// Get parameters
	string name = lua_tostring(state, 1);
	int graphic = lua_tointeger(state, 2);
	int level = lua_tointeger(state, 3);
	
	// Create userdata
	Card** c = (Card**) lua_newuserdata(state, sizeof(Card*));
	*c = new Card(name, graphic, level);
	
	// Set metatable
	lua_createtable(state, 0, 2);
	
	// __gc
	func = &Card::Destroy;
	data = lua_newuserdata(state, sizeof(func));
	memcpy(data, &func, sizeof(func));
	lua_pushcclosure(state, Trampoline<Card>, 1);
	lua_setfield(state, -2, "__gc");
	
	// __index
	lua_getglobal(state, "Card");
	lua_setfield(state, -2, "__index");
	
	// Apply metatable
	lua_setmetatable(state, -2);
	
	// Self reference
	lua_pushvalue(state, -1);
	(*c)->self = RegisterWeakRef(state);
	
	return 1;
}

Card::Card(const string& name, int graphic, int level): name(name), graphic(graphic), level(level)
{
}

Card::~Card()
{
}

int Card::Destroy(lua_State* state)
{
	UnregisterWeakRef(state, self);
	delete this;
	return 0;
}

void Pawn::Register(lua_State* state)
{
	int (Pawn::*func)(lua_State* state);
	void* data;
	
	lua_createtable(state, 0, 1);
	
	lua_pushcfunction(state, &Pawn::New);
	lua_setfield(state, -2, "new");
	
	func = &Pawn::SetHand;
	data = lua_newuserdata(state, sizeof(func));
	memcpy(data, &func, sizeof(func));
	lua_pushcclosure(state, Trampoline<Pawn>, 1);
	lua_setfield(state, -2, "SetHand");
	
	func = &Pawn::GetDataTable;
	data = lua_newuserdata(state, sizeof(func));
	memcpy(data, &func, sizeof(func));
	lua_pushcclosure(state, Trampoline<Pawn>, 1);
	lua_setfield(state, -2, "GetDataTable");
	
	lua_setglobal(state, "Pawn");
}

int Pawn::New(lua_State* state)
{
	int (Pawn::*func)(lua_State* state);
	void* data;
	
	// Create userdata
	Pawn** p = (Pawn**) lua_newuserdata(state, sizeof(Pawn*));
	*p = new Pawn();
	
	// Set metatable
	lua_createtable(state, 0, 2);
	
	// __gc
	func = &Pawn::Destroy;
	data = lua_newuserdata(state, sizeof(func));
	memcpy(data, &func, sizeof(func));
	lua_pushcclosure(state, Trampoline<Pawn>, 1);
	lua_setfield(state, -2, "__gc");
	
	// __index
	if(lua_getmetatable(state, 1) == 0)
		lua_pushvalue(state, 1);
	lua_setfield(state, -2, "__index");
	
	// Apply metatable
	lua_setmetatable(state, -2);
	
	// Data member table
	lua_pushvalue(state, -1);
	lua_createtable(state, 0, 1);
	
	lua_createtable(state, 2, 0);
	lua_setfield(state, -2, "hand");
	lua_createtable(state, 2, 0);
	lua_setfield(state, -2, "data");
	
	lua_settable(state, LUA_REGISTRYINDEX);
	
	// Self reference
	lua_pushvalue(state, -1);
	(*p)->self = RegisterWeakRef(state);
	
	return 1;
}

Pawn::Pawn()
{
}

int Pawn::SetHand(lua_State* state)
{
	luaL_argcheck(state, lua_isuserdata(state, 1), 1, "SetHand");
	luaL_argcheck(state, lua_istable(state, 2), 2, "SetHand");
	
	lua_pushvalue(state, 1);
	lua_gettable(state, LUA_REGISTRYINDEX);
	
	lua_pushvalue(state, 2);
	lua_setfield(state, -2, "hand");
	
	lua_pop(state, 1);
	
	hand.clear();
	int i = 1;
	while(true)
	{
		lua_pushinteger(state, i);
		lua_gettable(state, 2);
		if(!lua_isuserdata(state, -1))
			break;
		Card* c = *(Card**) lua_touserdata(state, -1);
		hand.push_back(c);
		i++;
	}
	
	return 0;
}

int Pawn::GetDataTable(lua_State* state)
{
	luaL_argcheck(state, lua_isuserdata(state, 1), 1, "GetDataTable");
	lua_gettable(state, LUA_REGISTRYINDEX);
	lua_getfield(state, 1, "data");
	return 1;
}

int Pawn::Destroy(lua_State* state)
{
	UnregisterWeakRef(state, self);
	delete this;
	return 0;
}

void Board::Register(lua_State* state)
{
	int (Board::*func)(lua_State* state);
	void* data;
	
	lua_createtable(state, 0, 1);
	
	func = &Board::SetPlayers;
	data = lua_newuserdata(state, sizeof(func));
	memcpy(data, &func, sizeof(func));
	lua_pushcclosure(state, Trampoline<Board>, 1);
	lua_setfield(state, -2, "SetPlayers");
	
	func = &Board::RegisterCard;
	data = lua_newuserdata(state, sizeof(func));
	memcpy(data, &func, sizeof(func));
	lua_pushcclosure(state, Trampoline<Board>, 1);
	lua_setfield(state, -2, "RegisterCard");
	
	func = &Board::GetPlayers;
	data = lua_newuserdata(state, sizeof(func));
	memcpy(data, &func, sizeof(func));
	lua_pushcclosure(state, Trampoline<Board>, 1);
	lua_setfield(state, -2, "GetPlayers");
	
	func = &Board::GetRandomCard;
	data = lua_newuserdata(state, sizeof(func));
	memcpy(data, &func, sizeof(func));
	lua_pushcclosure(state, Trampoline<Board>, 1);
	lua_setfield(state, -2, "GetRandomCard");
	
	lua_setglobal(state, "Board");
}

Board* Board::CreateInstance(lua_State* state)
{
	int (Board::*func)(lua_State* state);
	void* data;
	
	// Create userdata
	Board** b = (Board**) lua_newuserdata(state, sizeof(Board*));
	*b = new Board();
	
	// Data member table
	lua_pushvalue(state, -1);
	lua_createtable(state, 0, 1);
	
	// Create player list
	lua_createtable(state, 2, 0);
	lua_setfield(state, -2, "objects");
	lua_settable(state, LUA_REGISTRYINDEX);
	
	// Set metatable
	lua_createtable(state, 0, 2);
	
	// __gc
	func = &Board::Destroy;
	data = lua_newuserdata(state, sizeof(func));
	memcpy(data, &func, sizeof(func));
	lua_pushcclosure(state, Trampoline<Board>, 1);
	lua_setfield(state, -2, "__gc");
	
	// __index
	lua_getglobal(state, "Board");
	lua_setfield(state, -2, "__index");
	
	// Apply metatable
	lua_setmetatable(state, -2);
	
	// Self reference
	lua_pushvalue(state, -1);
	(*b)->self = RegisterWeakRef(state);
	
	// Register global
	lua_setglobal(state, "board");
	
	return *b;
}

Board::~Board()
{
}

void Board::Write(const string& message)
{
	log.push_back(message);
	if(log.size() > 16)
		log.pop_front();
}

int Board::SetPlayers(lua_State* state)
{
	luaL_argcheck(state, lua_isuserdata(state, 1), 1, "SetPlayers");
	luaL_argcheck(state, lua_istable(state, 2), 2, "SetPlayers");
	
	objects.clear();
	
	lua_pushvalue(state, 1);
	lua_gettable(state, LUA_REGISTRYINDEX);
	lua_getfield(state, -1, "objects");
	
	lua_pushinteger(state, 1);
	lua_pushinteger(state, 1);
	lua_gettable(state, 2);
	if(!lua_isuserdata(state, -1))
	{
		lua_pushstring(state, "Passed non-userdata as player");
		return lua_error(state);
	}
	objects.push_back(*(Pawn**) lua_touserdata(state, -1));
	lua_settable(state, -3);
	
	lua_pushinteger(state, 2);
	lua_pushinteger(state, 2);
	lua_gettable(state, 2);
	if(!lua_isuserdata(state, -1))
	{
		lua_pushstring(state, "Passed non-userdata as player");
		return lua_error(state);
	}
	objects.push_back(*(Pawn**) lua_touserdata(state, -1));
	lua_settable(state, -3);
	
	return 0;
}

int Board::RegisterCard(lua_State* state)
{
	if(lua_gettop(state) != 2)
	{
		lua_pushstring(state, "wrong number of arguments to native function");
		return lua_error(state);
	}
	
	Card* c = *(Card**) lua_touserdata(state, 2);
	lua_pop(state, 1);
	
	cards.push_back(c);
	
	return 0;
}

int Board::GetPlayers(lua_State* state)
{
	if(lua_gettop(state) != 1)
	{
		lua_pushstring(state, "wrong number of arguments to native function");
		return lua_error(state);
	}
	
	if(objects.size() < 2)
		return 0;
	
	GetWeakRef(state, objects[0]->self);
	GetWeakRef(state, objects[1]->self);
	
	return 2;
}

int Board::GetRandomCard(lua_State* state)
{
	if(lua_gettop(state) != 2)
	{
		lua_pushstring(state, "wrong number of arguments to native function");
		return lua_error(state);
	}
	
	if(cards.size() == 0)
	{
		lua_pushnil(state);
		return 1;
	}
	
	int level = lua_tointeger(state, 2);
	int firstCard = rand() % cards.size();
	
	for(int i = firstCard; i < cards.size(); i++)
	{
		if(cards[i]->level == level)
		{
			GetWeakRef(state, cards[i]->self);
			return 1;
		}
	}
	for(int i = 0; i < firstCard; i++)
	{
		if(cards[i]->level == level)
		{
			GetWeakRef(state, cards[i]->self);
			return 1;
		}
	}
	
	lua_pushnil(state);
	
	return 1;
}

int Board::Destroy(lua_State* state)
{
	UnregisterWeakRef(state, self);
	delete this;
	return 0;
}

}

// The end