/** 
@author Graham Rosser <grosser@cs.brown.edu>
*/

#include "ui/GameUI.h"



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



#define luaGlue extern "C" int



/*

 * C++ functions that can be called from within a Lua environment

 */

luaGlue _addText(lua_State *lua);

luaGlue _addButton(lua_State *lua);

luaGlue _addSprite(lua_State *lua);

luaGlue _addBackground(lua_State *lua);



int crapout(lua_State *l)

{

	std::cerr << "Died from within Lua.  Ending program." << std::endl;

	debugAssert(0);

	return 0;

}



static luaL_reg g_glue[] = {

	{"AddText",_addText},

	{"AddButton",_addButton},

	{"AddSprite",_addSprite},

	{"AddBackground",_addBackground},

	{NULL,NULL}

};



/*

 * Destructors.  All empty.

 */

GameUI::UIBackground::~UIBackground(){}

GameUI::UISprite::~UISprite(){}

GameUI::UIText::~UIText(){}

GameUI::UIButton::~UIButton(){}



GameUI::UIButton::UIButton(const Rect2D &r, TextureRef n, TextureRef h, TextureRef d) : UIItem(),

rect(r), neutral(n),

hover(h), down(d), pressed(false), mouseover(false)

{

}

/*

 * draw the button differently based on its pressed/mouseover states

 */

void GameUI::UIButton::draw(RenderDevice *rd)

{

    if(pressed)

    {

	if(mouseover)

		rd->setTexture(0,down);

	else

		rd->setTexture(0,neutral);

    }

    else

    {

	if(mouseover)

		rd->setTexture(0,hover);

	else

		rd->setTexture(0,neutral);

    }



	Draw::rect2D(rect,rd);

}



/*

 * the button updates like this.

 */

void GameUI::UIButton::update(const Vector2 &mousepos,RealTime dt)

{

	if(rect.contains(mousepos))

	    mouseover = true;

	else

	    mouseover = false;

}

void GameUI::UIButton::setPos(const Vector2 &p){ rect = Rect2D::xywh(p,rect.wh()); }

void GameUI::UIButton::setRect(const Rect2D &r){ rect = r; }



/*

 * Sets some simple state in the button

 */

bool GameUI::UIButton::reactClick(const Vector2 &click)

{

	if(rect.contains(click))

	{

	    pressed = !pressed;

		return true;

	}

	return false;

}



GameUI::UIText::UIText(GFontRef f, double s, const Vector2 &p, std::string initialText, 

					   const Color3 &color, const Color4 &outline) : UIItem(),

					   font(f),size(s),

					   pos(p),toSay(initialText),

					   textColor(color),outlineColor(outline)

{

	xalign = GFont::XALIGN_LEFT;

	yalign = GFont::YALIGN_TOP;

}



void GameUI::UIText::draw(RenderDevice *rd)

{

	font->draw2D(rd,toSay,pos,size,textColor,outlineColor,xalign,yalign);

}

void GameUI::UIText::setPos(const Vector2 &p){ pos = p; }

void GameUI::UIText::setText(const std::string &text){ toSay = text; }



GameUI::UISprite::UISprite(TextureRef i, const Rect2D &r) : UIItem(), image(i), rect(r)

{

}



void GameUI::UISprite::draw(RenderDevice *rd)

{

	rd->setTexture(0,image);

	Draw::rect2D(rect,rd);

}

void GameUI::UISprite::setPos(const Vector2 &p){ rect = Rect2D::xywh(p,rect.wh()); }

void GameUI::UISprite::setRect(const Rect2D &r){ rect = r; }



GameUI::UIBackground::UIBackground(TextureRef i) : UISprite(i,Rect2D()) {}



/*

 * backgrounds draw to the whole screen

 */

void GameUI::UIBackground::draw(RenderDevice *rd)

{

	rd->setTexture(0,image);

	Draw::rect2D(rd->window()->dimensions(),rd);

}



GameUI::GameUI(DApp *a)

{

	app = a;

}

GameUI::GameUI(DApp *a, const std::string &luafile)

{

	app = a;

	doLuaFile(luafile);

}



GameUI::~GameUI()

{

}



void

GameUI::update(const Vector2 &mousepos, RealTime dt)

{

    //update all elements

	int len = elements.size();

	for(int i = 0; i < len; ++i)

		elements[i]->update(mousepos,dt);

}



void GameUI::doLuaFile(const std::string &luafile)

{

	lua_State *l = lua_open();

	//open the correct libraries for the lua_State

	luaopen_base(l);

	luaopen_io(l);



	lua_atpanic(l,crapout);



	//register lua-c functions here

	for(int i = 0; g_glue[i].name; ++i)

		lua_register(l,g_glue[i].name,g_glue[i].func);



	//push 'this' as a global value called G_UI into the scripting environment

	//so Lua can access this object

	lua_pushstring(l,"G_UI");

	lua_pushlightuserdata(l,this);

	lua_settable(l,LUA_GLOBALSINDEX);



	//the lua file will now configure the UI

	lua_dofile(l,luafile.c_str());



	lua_close(l);

}



void GameUI::doLuaFile(const std::string &luafile, Array<UIHandle> &handles)

{

	doLuaFile(luafile);

	for(int i = 0; i < elements.size(); ++i)

		handles.append(i);

}



//assumes we're in a rd->push2D() state,

//and that we have alpha blending active

void GameUI::doUIGraphics(RenderDevice *rd)

{

	int len = elements.length();

	for(int i = 0; i < len; ++i)

		elements[i]->draw(rd);

}



/*

 * if a button reacts to the click, store its handle and 

 * return true

 */

bool GameUI::doClick(const Vector2 &click, UIHandle &reactor)

{

	ButtonTable::Iterator b = buttons.end();

	for(ButtonTable::Iterator a = buttons.begin();

		a != b; ++a)

	{

		UIButton *button = a->value;

		if(button->reactClick(click))

		{

			reactor = button->handle;

			return true;

		}

	}

	return false;

}



UIHandle GameUI::addBackground(TextureRef image)

{

	UIBackground *b = new UIBackground(image);

	elements.append(b);

	UIHandle h = elements.size() - 1;

	b->handle = h;

	sprites.set(h,b);

	return h;

}



UIHandle GameUI::addButton(Rect2D pos, TextureRef neutral, TextureRef hover, TextureRef down)

{

	UIButton *b = new UIButton(pos,neutral,hover,down);

	elements.append(b);

	UIHandle h = elements.size() - 1;

	b->handle = h;

	buttons.set(h,b);

	return h;

}



UIHandle GameUI::addSprite(TextureRef image, const Rect2D &rect)

{

	UISprite *s = new UISprite(image,rect);

	elements.append(s);

	UIHandle h = elements.size() - 1;

	s->handle = h;

	sprites.set(h,s);

	return h;

}



UIHandle GameUI::addText(GFontRef font, double size, const Vector2 &pos, std::string initialText, const Color3 &color,

						 const Color4 &outline)

{

	UIText *t = new UIText(font,size,pos,initialText, color, outline);

	elements.append(t);

	UIHandle h = elements.size() - 1;

	t->handle = h;

	texts.set(h,t);

	return h;

}



void GameUI::setText(UIHandle h, const std::string &text)

{

	((UIText*)elements[h])->setText(text);

}



void GameUI::setPos(UIHandle h, const Vector2 &pos)

{

	elements[h]->setPos(pos);

}



GameUI::UIItem* GameUI::getItem(UIHandle h)

{

	return elements[h];

}



/*

 * lua stuff.  Only play with this if you're 

 * relatively comfortable with the LUA-C API.

 */

luaGlue _addText(lua_State *lua)

{

	int nargs = lua_gettop(lua);

	GameUI *ui = (GameUI*)lua_touserdata(lua,1);



	lua_pushstring(lua,"font");

	lua_gettable(lua,2);



	GFontRef f = GFont::fromFile(NULL,app->dataDir + lua_tostring(lua,-1));

	lua_pop(lua,1);

	lua_pushstring(lua,"size");

	lua_gettable(lua,2);

	double size = lua_tonumber(lua,-1);

	lua_pop(lua,1);

	lua_pushstring(lua,"x");

	lua_gettable(lua,2);

	double x = lua_tonumber(lua,-1);

	lua_pop(lua,1);

	lua_pushstring(lua,"y");

	lua_gettable(lua,2);

	double y = lua_tonumber(lua,-1);

	lua_pop(lua,1);



	Color3 color(1,1,1);

	lua_pushstring(lua,"color");

	lua_gettable(lua,2);

	if(!lua_isnil(lua,-1))

	{

		lua_pushnumber(lua,1);

		lua_gettable(lua,-2);

		color.r = lua_tonumber(lua,-1);

		lua_pop(lua,1);



		lua_pushnumber(lua,2);

		lua_gettable(lua,-2);

		color.g = lua_tonumber(lua,-1);

		lua_pop(lua,1);



		lua_pushnumber(lua,3);

		lua_gettable(lua,-2);

		color.b = lua_tonumber(lua,-1);

		lua_pop(lua,1);

	}

	lua_pop(lua,1);



	Color4 outline;

	lua_pushstring(lua,"outline");

	lua_gettable(lua,2);

	if(!lua_isnil(lua,-1))

	{

		lua_pushnumber(lua,1);

		lua_gettable(lua,-2);

		outline.r = lua_tonumber(lua,-1);

		lua_pop(lua,1);



		lua_pushnumber(lua,2);

		lua_gettable(lua,-2);

		outline.g = lua_tonumber(lua,-1);

		lua_pop(lua,1);



		lua_pushnumber(lua,3);

		lua_gettable(lua,-2);

		outline.b = lua_tonumber(lua,-1);

		lua_pop(lua,1);



		lua_pushnumber(lua,4);

		lua_gettable(lua,-2);

		outline.a = lua_tonumber(lua,-1);

		lua_pop(lua,1);

	}



	std::string str = "";

	lua_pushstring(lua,"text");

	lua_gettable(lua,2);

	if(!lua_isnil(lua,-1))

	{

		str = lua_tostring(lua,-1);

	}

	lua_pop(lua,1);



	ui->addText(f,size,Vector2(x,y),str,color,outline);



	return 0;

}

luaGlue _addButton(lua_State *lua)

{

	GameUI *ui = (GameUI*)lua_touserdata(lua,1);

	const char *n,*ho,*d;

	double x,y,w,h;

	x = lua_tonumber(lua,2);

	y = lua_tonumber(lua,3);

	w = lua_tonumber(lua,4);

	h = lua_tonumber(lua,5);



	n = lua_tostring(lua,6);

	ho = lua_tostring(lua,7);

	d = lua_tostring(lua,8);



	//buttons may be transparent.  Save the alpha channel.

	ui->addButton(Rect2D::xywh(x,y,w,h),

		Texture::fromFile(n,TextureFormat::RGBA8),

		Texture::fromFile(ho,TextureFormat::RGBA8),

		Texture::fromFile(d,TextureFormat::RGBA8));



	return 0;

}



luaGlue _addSprite(lua_State *lua)

{

	GameUI *ui = (GameUI*)lua_touserdata(lua,1);

	const char *str = lua_tostring(lua,2);

	double x = lua_tonumber(lua,3);

	double y = lua_tonumber(lua,4);

	double w = lua_tonumber(lua,5);

	double h = lua_tonumber(lua,6);



	//sprites may be transparent.  Save the alpha channel.

	ui->addSprite(Texture::fromFile(str,TextureFormat::RGBA8),Rect2D::xywh(x,y,w,h));



	return 0;

}

luaGlue _addBackground(lua_State *lua)

{

	GameUI *ui = (GameUI*)lua_touserdata(lua,1);

	const char *str = lua_tostring(lua,2);

	ui->addBackground(Texture::fromFile(str));



	return 0;

}



