/*
 *  Copyright (c) 2000-2022 Inria
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  * Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation
 *  and/or other materials provided with the distribution.
 *  * Neither the name of the ALICE Project-Team nor the names of its
 *  contributors may be used to endorse or promote products derived from this
 *  software without specific prior written permission.
 * 
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *
 *  Contact: Bruno Levy
 *
 *     https://www.inria.fr/fr/bruno-levy
 *
 *     Inria,
 *     Domaine de Voluceau,
 *     78150 Le Chesnay - Rocquencourt
 *     FRANCE
 *
 */

#include <geogram_gfx/lua/lua_imgui.h>
#include <geogram_gfx/imgui_ext/imgui_ext.h>
#include <geogram_gfx/imgui_ext/icon_font.h>
#include <geogram/lua/lua_wrap.h>
#include <geogram/basic/string.h>
#include <geogram/basic/logger.h>
#include <map>

extern void LoadImguiBindings();
extern lua_State* lState;

namespace {
    using namespace GEO;

    
    int wrapper_TextInput(lua_State* L) {
	
	if(
	    lua_gettop(L) != 2 &&
	    lua_gettop(L) != 3
	) {
	    return luaL_error(
		L, "'imgui.TextInput()' invalid number of arguments"
	    );
	}
	
	if(!lua_isstring(L,1)) {
	    return luaL_error(
		L, "'imgui.TextInput()' argument 1 should be a string"
	    );
	}
	
	if(!lua_isstring(L,2)) {
	    return luaL_error(
		L, "'imgui.TextInput()' argument 2 should be a string"
	    );
	}

	ImGuiInputTextFlags flags = 0;
	
	if(lua_gettop(L) == 3) {
	    if(!lua_isnumber(L,3)) {
		return luaL_error(
		    L, "'imgui.TextInput()' argument 3 should be a number"
		);
	    }
	    flags = ImGuiInputTextFlags(lua_tonumber(L,3));
	}
	
	const char* label  = lua_tostring(L,1);
	const char* str = lua_tostring(L,2);	
	static char buff[geo_imgui_string_length];
	strcpy(buff,str);
	bool result = ImGui::InputText(
	    label, buff, geo_imgui_string_length, flags
	);
	lua_pushboolean(L,result);
	lua_pushstring(L,buff);
	
	
	return 2;
    }


    int wrapper_Combo(lua_State* L) {
	if(lua_gettop(L) != 3) {
	    return luaL_error(
		L, "'imgui.Combo()' invalid number of arguments"
	    );
	}
	
	if(!lua_isstring(L,1)) {
	    return luaL_error(
		L, "'imgui.Combo()' argument should be a string"
	    );
	}
	
	if(!lua_isstring(L,2)) {
	    return luaL_error(
		L, "'imgui.Combo()' argument should be a string"
	    );
	}

	if(!lua_isstring(L,3)) {
	    return luaL_error(
		L, "'imgui.Combo()' argument should be a string"
	    );
	}
	
	const char* label = lua_tostring(L,1);
	const char* current_item = lua_tostring(L,2);
	const char* items = lua_tostring(L,3);		

	char* lua_items = (char*)alloca(strlen(items)+2);
	strcpy(lua_items,items);
	{
	    size_t n = strlen(lua_items);
	    lua_items[n] = ';';
	    lua_items[n+1] = '\0';
	}
	
	int lua_current_item=0;
	
	const char* prev_item = lua_items;
	int nb_items = 0;

	char* p = lua_items;
	while(*p != '\0') {
	    if(*p == ';') {
		*p = '\0';
		if(!strcmp(prev_item, current_item)) {
		    lua_current_item = nb_items;
		}
		prev_item = p+1;
		++nb_items;
	    }
	    ++p;
	}
	*p = '\0'; // Double '\0' to indicate end of item list to lua.

	bool result = ImGui::Combo(label, &lua_current_item, lua_items);

	current_item = lua_items;
	while(lua_current_item > 0) {
	    while(*current_item) {
		++current_item;
	    }
	    ++current_item;
	    --lua_current_item;
	}

	lua_pushboolean(L, result);
	lua_pushstring(L, current_item);

	return 2;
    }

    int wrapper_ColorEdit3WithPalette(
	lua_State* L	
    ) {
	if(lua_gettop(L) != 4) {
	    return luaL_error(
		L, "'imgui.ColorEdit3WithPalette()' invalid number of arguments"
	    );
	}
	
	if(!lua_isstring(L,1)) {
	    return luaL_error(
		L, "'imgui.ColorEdit3WithPalette()' argument 1 should be a string"
	    );
	}

	if(!lua_isnumber(L,2)) {
	    return luaL_error(
		L, "'imgui.ColorEdit3WithPalette()' argument 2 should be a number"
	    );
	}

	if(!lua_isnumber(L,3)) {
	    return luaL_error(
		L, "'imgui.ColorEdit3WithPalette()' argument 3 should be a number"
	    );
	}

	if(!lua_isnumber(L,4)) {
	    return luaL_error(
		L, "'imgui.ColorEdit3WithPalette()' argument 4 should be a number"
	    );
	}

	const char* label = lua_tostring(L,1);
	
	float rgb[3];
	rgb[0] = float(lua_tonumber(L,2));
	rgb[1] = float(lua_tonumber(L,3));
	rgb[2] = float(lua_tonumber(L,4));	

	bool sel = ImGui::ColorEdit3WithPalette(
	    label, rgb
	);

	lua_pushboolean(L,sel);
	lua_pushnumber(L,double(rgb[0]));
	lua_pushnumber(L,double(rgb[1]));
	lua_pushnumber(L,double(rgb[2]));

	return 4;
    }

    int wrapper_ColorEdit4WithPalette(
	lua_State* L	
    ) {
	if(lua_gettop(L) != 5) {
	    return luaL_error(
		L, "'imgui.ColorEdit3WithPalette()' invalid number of arguments"
	    );
	}
	
	if(!lua_isstring(L,1)) {
	    return luaL_error(
		L, "'imgui.ColorEdit3WithPalette()' argument 1 should be a string"
	    );
	}

	if(!lua_isnumber(L,2)) {
	    return luaL_error(
		L, "'imgui.ColorEdit3WithPalette()' argument 2 should be a number"
	    );
	}

	if(!lua_isnumber(L,3)) {
	    return luaL_error(
		L, "'imgui.ColorEdit3WithPalette()' argument 3 should be a number"
	    );
	}

	if(!lua_isnumber(L,4)) {
	    return luaL_error(
		L, "'imgui.ColorEdit3WithPalette()' argument 4 should be a number"
	    );
	}

	if(!lua_isnumber(L,5)) {
	    return luaL_error(
		L, "'imgui.ColorEdit3WithPalette()' argument 5 should be a number"
	    );
	}
	
	const char* label = lua_tostring(L,1);
	
	float rgb[4];
	rgb[0] = float(lua_tonumber(L,2));
	rgb[1] = float(lua_tonumber(L,3));
	rgb[2] = float(lua_tonumber(L,4));
	rgb[3] = float(lua_tonumber(L,5));		

	bool sel = ImGui::ColorEdit4WithPalette(
	    label, rgb
	);

	lua_pushboolean(L,sel);
	lua_pushnumber(L,double(rgb[0]));
	lua_pushnumber(L,double(rgb[1]));
	lua_pushnumber(L,double(rgb[2]));
	lua_pushnumber(L,double(rgb[3]));	

	return 5;
    }

    
    int wrapper_OpenFileDialog(
	lua_State* L
    ) {
	if(lua_gettop(L) != 4) {
	    return luaL_error(
		L, "'imgui.OpenFileDialog()' invalid number of arguments"
	    );
	}

	if(!lua_isstring(L,1)) {
	    return luaL_error(
		L, "'imgui.OpenFileDialog()' argument 1 should be a string"
	    );
	}

	if(!lua_isstring(L,2)) {
	    return luaL_error(
		L, "'imgui.OpenFileDialog()' argument 2 should be a string"
	    );
	}

	if(!lua_isstring(L,3)) {
	    return luaL_error(
		L, "'imgui.OpenFileDialog()' argument 3 should be a string"
	    );
	}

	if(!lua_isnumber(L,4)) {
	    return luaL_error(
		L, "'imgui.OpenFileDialog()' argument 4 should be a number"
	    );
	}

	const char* label      = lua_tostring(L,1);
	const char* extensions = lua_tostring(L,2);
	const char* filename   = lua_tostring(L,3);
	ImGuiExtFileDialogFlags flags =
	    ImGuiExtFileDialogFlags(lua_tonumber(L,4));

	ImGui::OpenFileDialog(label, extensions, filename, flags);
	
	return 0;
    }

    int wrapper_FileDialog(
	lua_State* L
    ) {
	if(lua_gettop(L) != 2) {
	    return luaL_error(
		L, "'imgui.FileDialog()' invalid number of arguments"
	    );
	}

	if(!lua_isstring(L,1)) {
	    return luaL_error(
		L, "'imgui.OpenFileDialog()' argument 1 should be a string"
	    );
	}

	if(!lua_isstring(L,2)) {
	    return luaL_error(
		L, "'imgui.OpenFileDialog()' argument 2 should be a string"
	    );
	}

	const char* label      = lua_tostring(L,1);
	char filename[geo_imgui_string_length];

	const char* filename_in = lua_tostring(L,2);
	if(filename_in != nullptr) {
	    if(strlen(filename_in) > geo_imgui_string_length + 1) {
		Logger::err("ImGui") << "Max file name length exceeded"
				     << std::endl;
		return false;
	    }
	    strcpy(filename, filename_in);
	} else {
	    filename[0] = '\0';
	}
	
	bool result =
	    ImGui::FileDialog(label, filename, geo_imgui_string_length);

	lua_pushboolean(L,result);
	lua_pushstring(L, result ? filename : filename_in);
	
	return 2;
    }

    int wrapper_SetNextWindowPos(lua_State* L) {
	if(
	    lua_gettop(L) != 2 &&
	    lua_gettop(L) != 3
	) {
	    return luaL_error(
		L, "'imgui.SetNextWindowPos()' invalid number of arguments"
	    );
	}

	if(!lua_isnumber(L,1)) {
	    return luaL_error(
		L, "'imgui.SetNextWindowPos()' argument 1 should be a number"
	    );
	}

	if(!lua_isnumber(L,2)) {
	    return luaL_error(
		L, "'imgui.SetNextWindowPos()' argument 2 should be a number"
	    );
	}

	ImGuiCond cond = 0;
	if(lua_gettop(L) == 3) {
	    if(!lua_isnumber(L,3)) {
		return luaL_error(
		    L,
		    "'imgui.SetNextWindowPos()' argument 3 should be a number"
		);
	    }
	    cond = ImGuiCond(lua_tonumber(L,3));
	}

	
	ImGui::SetNextWindowPos(
	    ImVec2(float(lua_tonumber(L,1)), float(lua_tonumber(L,2))),
	    cond
	);

	return 0;
    }

    int wrapper_SetNextWindowSize(lua_State* L) {
	if(
	    lua_gettop(L) != 2 &&
	    lua_gettop(L) != 3
	) {
	    return luaL_error(
		L, "'imgui.SetNextWindowSize()' invalid number of arguments"
	    );
	}

	if(!lua_isnumber(L,1)) {
	    return luaL_error(
		L, "'imgui.SetNextWindowSize()' argument 1 should be a number"
	    );
	}

	if(!lua_isnumber(L,2)) {
	    return luaL_error(
		L, "'imgui.SetNextWindowSize()' argument 2 should be a number"
	    );
	}

	ImGuiCond cond = 0;
	if(lua_gettop(L) == 3) {
	    if(!lua_isnumber(L,3)) {
		return luaL_error(
		    L,
		    "'imgui.SetNextWindowSize()' argument 3 should be a number"
		);
	    }
	    cond = ImGuiCond(lua_tonumber(L,3));
	}

	
	ImGui::SetNextWindowSize(
	    ImVec2(float(lua_tonumber(L,1)), float(lua_tonumber(L,2))),
	    cond
	);

	return 0;
    }


    int wrapper_IsItemHovered(lua_State* L) {
	if(lua_gettop(L) != 0) {
	    return luaL_error(
		L, "'imgui.IsItemHovered()' invalid number of arguments"
	    );
	}
	lua_pushboolean(L,ImGui::IsItemHovered());
	return 1;
    }

    int wrapper_Text(lua_State* L) {
	if(lua_gettop(L) < 1) {
	    return luaL_error(
		L, "'imgui.Text()' invalid number of arguments"
	    );
	}
	const char* str = lua_tostring(L,1);
	ImGui::Text("%s",str);
	return 0;
    }

    int wrapper_SetTooltip(lua_State* L) {
	if(lua_gettop(L) != 1) {
	    return luaL_error(
		L, "'imgui.SetTooltip()' invalid number of arguments"
	    );
	}
	const char* str = lua_tostring(L,1);
	ImGui::SetTooltip("%s",str);
	return 0;
    }

    int wrapper_ShowStyleEditor(lua_State* L) {
	if(lua_gettop(L) != 0) {
	    return luaL_error(
		L, "'imgui.ShowStyleEditor()' invalid number of arguments"
	    );
	}
	ImGui::ShowStyleEditor(nullptr);
	return 0;
    }

    int wrapper_PushFont(lua_State* L) {
	if(lua_gettop(L) != 1) {
	    return luaL_error(
		L, "'imgui.PushFont()' invalid number of arguments"
	    );
	}
	if(!lua_isinteger(L,1)) {
	    return luaL_error(
		L, "'imgui.PushFont()' argument is not an integer"
	    );
	}
	int idx = int(lua_tointeger(L,1));
	if(idx < 0 || idx >= ImGui::GetIO().Fonts->Fonts.size()) {
	    return luaL_error(
		L, "'imgui.PushFont()' invalid font index"
	    );
	}
	ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[idx]);
	return 0;
    }

    int wrapper_font_icon(lua_State* L) {
	if(lua_gettop(L) != 1) {
	    return luaL_error(
		L, "'imgui.font_icon()' invalid number of arguments"
	    );
	}
	if(!lua_isstring(L,1)) {
	    return luaL_error(
		L, "'imgui.font_icon()' argument is not a string"
	    );
	}
	const char* K = lua_tostring(L,1);
	wchar_t result[2];
	result[0] = icon_wchar(K);
	result[1] = '\0';	
	std::string result_str = String::wchar_to_UTF8(result);
	lua_pushstring(L, result_str.c_str());
	return 1;
    }

    int wrapper_BeginTabBar(lua_State* L) {
	if(lua_gettop(L) != 1) {
	    return luaL_error(
		L, "'imgui.BeginTabBar()' invalid number of arguments"
	    );
	}
	if(!lua_isstring(L,1)) {
	    return luaL_error(
		L, "'imgui.BeinTabBar()' argument is not a string"
	    );
	}
	const char* K = lua_tostring(L,1);
	ImGui::BeginTabBar(K);
	return 0;
    }

    int wrapper_BeginTabItem(lua_State* L) {
	if(lua_gettop(L) != 1) {
	    return luaL_error(
		L, "'imgui.BeginTabItem()' invalid number of arguments"
	    );
	}
	if(!lua_isstring(L,1)) {
	    return luaL_error(
		L, "'imgui.BeginTabItem()' argument is not a string"
	    );
	}
	const char* K = lua_tostring(L,1);
	lua_pushboolean(L,ImGui::BeginTabItem(K));
	return 1;
    }

    int wrapper_SimpleButton(lua_State* L) {
	if(lua_gettop(L) != 1) {
	    return luaL_error(
		L, "'imgui.SimpleButton()' invalid number of arguments"
	    );
	}
	if(!lua_isstring(L,1)) {
	    return luaL_error(
		L, "'imgui.SimpleButton()' argument is not a string"
	    );
	}
	const char* K = lua_tostring(L,1);
	lua_pushboolean(L,ImGui::SimpleButton(K));
	return 1;
    }

    int wrapper_GetMousePos(lua_State* L) {
	if(lua_gettop(L) != 0) {
	    return luaL_error(
		L, "'imgui.GetMousePos()' invalid number of arguments"
	    );
	}
	lua_pushnumber(L,double(ImGui::GetIO().MousePos.x));
	lua_pushnumber(L,double(ImGui::GetIO().MousePos.y));	
	return 2;
    }
    
}

namespace GEO {
    
    /**
     * \brief Specialization of lua_push() for ImDrawList*
     */
    template<> inline void lua_push(
	lua_State* L, ImDrawList* x
    ) {
	lua_pushlightuserdata(L,(void*)x);
    }

    /**
     * \brief lua_to specialization for ImDrawList*
     */
    template<> class lua_to<ImDrawList*> {
      public:
	lua_to(lua_State* L, int idx) {
	    x_ = (ImDrawList*)(lua_touserdata(L,idx));
	}
	static bool can_convert(lua_State* L, int idx) {
	    return lua_check_type(L, idx, my_lua_islightuserdata);
	}
	operator ImDrawList*() const {
	    return x_;
	}
      private:
	ImDrawList* x_;
    };
    
}

// WIP: export all ImGuiDrawList functions to LUA

namespace ImGuiDrawAdapters {

    // Needed by lua_bindwrapper because ImGui::GetBackgroundDrawList()
    // takes an ImViewport (default = nullptr) as an argunemt.
    static ImDrawList* GetBackgroundDrawList() {
	return ImGui::GetBackgroundDrawList();
    }

    // Needed by lua_bindwrapper because ImGui::GetForegroundDrawList()
    // takes an ImViewport (default = nullptr) as an argunemt.
    static ImDrawList* GetForegroundDrawList() {
	return ImGui::GetForegroundDrawList();
    }

    static void PushClipRect(
	ImDrawList* list,
	float xmin, float ymin,
	float xmax, float ymax,
	bool intersect
    ) {
	list->PushClipRect(
	    ImVec2(xmin, ymin),
	    ImVec2(xmax, ymax),
	    intersect
	);
    }

    static void PushClipRectFullScreen(ImDrawList* list) {
	list->PushClipRectFullScreen();
    }

    static void PopClipRect(ImDrawList* list) {
	list->PopClipRect();
    }

    static void PushTextureID(ImDrawList* list, index_t id) {
	union {
	    ImTextureID im_texture_id;
	    index_t gl_texture_id;
	};
	gl_texture_id = id;
	list->PushTextureID(im_texture_id);
    }
    
    static void PopTextureID(ImDrawList* list) {
	list->PopClipRect();
    }
    
    static void AddLine(
	ImDrawList* list, float x1, float y1, float x2, float y2,
	Numeric::uint32 color, float thickness
    ) {
	list->AddLine(
	    ImVec2(x1,y1), ImVec2(x2,y2),
	    color, thickness
	);
    }

    static void AddRect(
	ImDrawList* list,
	float x1, float y1, float x2, float y2,
	Numeric::uint32 color, float rounding, int rounding_corners,
	float thickness
    ) {
	list->AddRect(
	    ImVec2(x1,y1), ImVec2(x2,y2), color, rounding,
	    ImDrawFlags(rounding_corners), thickness
	);
    }

    static void AddRectFilled(
	ImDrawList* list,
	float x1, float y1, float x2, float y2,
	Numeric::uint32 color, float rounding, int rounding_corners
    ) {
	list->AddRectFilled(
	    ImVec2(x1,y1), ImVec2(x2,y2), color, rounding,
	    ImDrawFlags(rounding_corners)
	);
    }

    static void AddRectFilledMultiColor(
	ImDrawList* list,
	float x1, float y1, float x2, float y2,
	Numeric::uint32 color1,	Numeric::uint32 color2,
	Numeric::uint32 color3,	Numeric::uint32 color4
    ) {
	list->AddRectFilledMultiColor(
	    ImVec2(x1,y1), ImVec2(x2,y2),	    
	    color1, color2, color3, color4
	);
    }

    static void AddQuad(
	ImDrawList* list,
	float x1, float y1, float x2, float y2,
	float x3, float y3, float x4, float y4,	
	Numeric::uint32 color,
	float thickness
    ) {
	list->AddQuad(
	    ImVec2(x1,y1), ImVec2(x2,y2), ImVec2(x3,y3), ImVec2(x4,y4),	    
	    color, thickness
	);
    }

    static void AddQuadFilled(
	ImDrawList* list, float x1, float y1, float x2, float y2,
	float x3, float y3, float x4, float y4,	Numeric::uint32 color
    ) {
	list->AddQuad(
	    ImVec2(x1,y1), ImVec2(x2,y2), ImVec2(x3,y3), ImVec2(x4,y4), color
	);
    }

    static void AddTriangle(
	ImDrawList* list, float x1, float y1, float x2, float y2,
	float x3, float y3, Numeric::uint32 color, float thickness
    ) {
	list->AddTriangle(
	    ImVec2(x1,y1), ImVec2(x2,y2), ImVec2(x3,y3), color, thickness
	);
    }

    static void AddTriangleFilled(
	ImDrawList* list, float x1, float y1, float x2, float y2,
	float x3, float y3, Numeric::uint32 color
    ) {
	list->AddTriangleFilled(ImVec2(x1,y1), ImVec2(x2,y2), ImVec2(x3,y3), color);
    }

    static void AddCircle(
	ImDrawList* list, float x, float y, float radius, Numeric::uint32 color,
	int num_segments, float thickness
    ) {
	list->AddCircle(ImVec2(x,y), radius, color, num_segments, thickness);
    }

    static void AddCircleFilled(
	ImDrawList* list, float x, float y, float radius, Numeric::uint32 color,
	int num_segments
    ) {
	list->AddCircleFilled(ImVec2(x,y), radius, color, num_segments);
    }

    static void AddText(
	ImDrawList* list, float x, float y, index_t color, const char* text
    ) {
	list->AddText(ImVec2(x,y), color, text);
    }

    static void AddText2(
	ImDrawList* list, index_t font, float font_size, float x, float y,
	index_t color, const char* text
    ) {
	if(int(font) >= ImGui::GetIO().Fonts->Fonts.size()) {
	    return;
	}
	ImFont* imfont = ImGui::GetIO().Fonts->Fonts[int(font)];
	list->AddText(imfont, font_size, ImVec2(x,y), color, text);
    }

    static void AddBezierCubic(
	ImDrawList* list, float x1, float y1, float x2, float y2,
	float x3, float y3, float x4, float y4, index_t color,
	float thickness, int num_segments
    ) {
	list->AddBezierCubic(
	    ImVec2(x1,y1), ImVec2(x2,y2), ImVec2(x3,y3), ImVec2(x4,y4),
	    color, thickness, num_segments
	);
    }

    static void AddImage(
	ImDrawList* list, index_t id,
	float x1, float y1, float x2, float y2,
	float u1, float v1, float u2, float v2,
	index_t color
    ) {
	union {
	    ImTextureID im_texture_id;
	    index_t gl_texture_id;
	};
	gl_texture_id = id;
	list->AddImage(
	    im_texture_id,
	    ImVec2(x1,y1), ImVec2(x2,y2),
	    ImVec2(u1,v1), ImVec2(u2,v2),
	    color
	);
    }

    static void AddImageRounded(
	ImDrawList* list, index_t id,
	float x1, float y1, float x2, float y2,
	float u1, float v1, float u2, float v2,
	index_t color, float rounding, int rounding_corners
    ) {
	union {
	    ImTextureID im_texture_id;
	    index_t gl_texture_id;
	};
	gl_texture_id = id;
	list->AddImageRounded(
	    im_texture_id,
	    ImVec2(x1,y1), ImVec2(x2,y2),
	    ImVec2(u1,v1), ImVec2(u2,v2),
	    color, rounding, ImDrawFlags(rounding_corners)
	);
    }

    static void PathClear(ImDrawList* list) {
	list->PathClear();
    }

    static void PathLineTo(ImDrawList* list, float x, float y) {
	list->PathLineTo(ImVec2(x,y));
    }

    static void PathLineToMergeDuplicate(ImDrawList* list, float x, float y) {
	list->PathLineToMergeDuplicate(ImVec2(x,y));
    }

    static void PathFillConvex(ImDrawList* list, index_t color) {
	list->PathFillConvex(color);
    }

    static void PathStroke(
	ImDrawList* list, index_t color, bool closed, float thickness
    ) {
	list->PathStroke(color, closed, thickness);
    }

    static void PathArcTo(
	ImDrawList* list, float cx, float cy, float radius, float a1, float a2,
	int num_segments
    ) {
	list->PathArcTo(ImVec2(cx,cy), radius, a1, a2, num_segments);
    }

    static void PathArcToFast(
	ImDrawList* list, float cx, float cy, float radius, int a1, int a2
    ) {
	list->PathArcToFast(ImVec2(cx,cy), radius, a1, a2);
    }

    static void PathBezierCubicCurveTo(
	ImDrawList* list,
	float x1, float y1, float x2, float y2, float x3, float y3,
	int num_segments
    ) {
	list->PathBezierCubicCurveTo(
	    ImVec2(x1,y1), ImVec2(x2,y2), ImVec2(x3,y3), num_segments
	);
    }

    static void PathRect(
	ImDrawList* list,
	float x1, float y1, float x2, float y2,
	float rounding, int rounding_corners	
    ) {
	list->PathRect(
	    ImVec2(x1,y1), ImVec2(x2,y2),
	    rounding, ImDrawFlags(rounding_corners)
	);
    }
}


#define DECLARE_IMGUI_CONSTANT(C) \
	lua_pushinteger(L,C);     \
	lua_setglobal(L,#C)


void init_lua_imgui(lua_State* L) {
    lState = L;
    LoadImguiBindings();

    DECLARE_IMGUI_CONSTANT(ImGuiExtFileDialogFlags_Load);
    DECLARE_IMGUI_CONSTANT(ImGuiExtFileDialogFlags_Save);
    DECLARE_IMGUI_CONSTANT(ImGuiCond_Always);
    DECLARE_IMGUI_CONSTANT(ImGuiCond_Once);
    DECLARE_IMGUI_CONSTANT(ImGuiCond_FirstUseEver);
    DECLARE_IMGUI_CONSTANT(ImGuiCond_Appearing);
    DECLARE_IMGUI_CONSTANT(ImGuiSelectableFlags_AllowDoubleClick);

    DECLARE_IMGUI_CONSTANT(ImGuiCol_Text);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_TextDisabled);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_WindowBg);              
    DECLARE_IMGUI_CONSTANT(ImGuiCol_ChildBg);               
    DECLARE_IMGUI_CONSTANT(ImGuiCol_PopupBg);               
    DECLARE_IMGUI_CONSTANT(ImGuiCol_Border);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_BorderShadow);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_FrameBg);               
    DECLARE_IMGUI_CONSTANT(ImGuiCol_FrameBgHovered);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_FrameBgActive);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_TitleBg);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_TitleBgActive);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_TitleBgCollapsed);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_MenuBarBg);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_ScrollbarBg);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_ScrollbarGrab);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_ScrollbarGrabHovered);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_ScrollbarGrabActive);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_CheckMark);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_SliderGrab);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_SliderGrabActive);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_Button);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_ButtonHovered);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_ButtonActive);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_Header);                
    DECLARE_IMGUI_CONSTANT(ImGuiCol_HeaderHovered);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_HeaderActive);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_Separator);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_SeparatorHovered);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_SeparatorActive);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_ResizeGrip);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_ResizeGripHovered);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_ResizeGripActive);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_Tab);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_TabHovered);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_TabActive);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_TabUnfocused);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_TabUnfocusedActive);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_DockingPreview);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_DockingEmptyBg);        
    DECLARE_IMGUI_CONSTANT(ImGuiCol_PlotLines);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_PlotLinesHovered);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_PlotHistogram);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_PlotHistogramHovered);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_TextSelectedBg);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_DragDropTarget);
    DECLARE_IMGUI_CONSTANT(ImGuiCol_NavHighlight);          
    DECLARE_IMGUI_CONSTANT(ImGuiCol_NavWindowingHighlight); 
    DECLARE_IMGUI_CONSTANT(ImGuiCol_NavWindowingDimBg);     
    DECLARE_IMGUI_CONSTANT(ImGuiCol_ModalWindowDimBg);      
    
    lua_getglobal(L, "imgui");

    lua_pushliteral(L,"TextInput");
    lua_pushcfunction(L,wrapper_TextInput); 
    lua_settable(L,-3);

    lua_pushliteral(L,"Combo");
    lua_pushcfunction(L,wrapper_Combo); 
    lua_settable(L,-3);

    lua_pushliteral(L,"ColorEdit3WithPalette");
    lua_pushcfunction(L,wrapper_ColorEdit3WithPalette); 
    lua_settable(L,-3);

    lua_pushliteral(L,"ColorEdit4WithPalette");
    lua_pushcfunction(L,wrapper_ColorEdit4WithPalette); 
    lua_settable(L,-3);
    
    lua_pushliteral(L,"OpenFileDialog");
    lua_pushcfunction(L,wrapper_OpenFileDialog);
    lua_settable(L,-3);

    lua_pushliteral(L,"FileDialog");
    lua_pushcfunction(L,wrapper_FileDialog);
    lua_settable(L,-3);

    lua_pushliteral(L,"SetNextWindowPos");
    lua_pushcfunction(L,wrapper_SetNextWindowPos);
    lua_settable(L,-3);

    lua_pushliteral(L,"SetNextWindowSize");
    lua_pushcfunction(L,wrapper_SetNextWindowSize);
    lua_settable(L,-3);
    
    lua_pushliteral(L,"IsItemHovered");
    lua_pushcfunction(L,wrapper_IsItemHovered);
    lua_settable(L,-3);

    lua_pushliteral(L,"Text");
    lua_pushcfunction(L,wrapper_Text);
    lua_settable(L,-3);

    lua_pushliteral(L,"SetTooltip");
    lua_pushcfunction(L,wrapper_SetTooltip);
    lua_settable(L,-3);

    lua_pushliteral(L,"ShowStyleEditor");
    lua_pushcfunction(L,wrapper_ShowStyleEditor);
    lua_settable(L,-3);

    lua_pushliteral(L,"PushFont");
    lua_pushcfunction(L,wrapper_PushFont);
    lua_settable(L,-3);

    lua_pushliteral(L,"font_icon");
    lua_pushcfunction(L,wrapper_font_icon);
    lua_settable(L,-3);

    lua_pushliteral(L,"BeginTabBar");
    lua_pushcfunction(L,wrapper_BeginTabBar);
    lua_settable(L,-3);

    lua_pushliteral(L,"BeginTabItem");
    lua_pushcfunction(L,wrapper_BeginTabItem);
    lua_settable(L,-3);

    lua_pushliteral(L,"SimpleButton");
    lua_pushcfunction(L,wrapper_SimpleButton);
    lua_settable(L,-3);

    lua_pushliteral(L,"GetMousePos");
    lua_pushcfunction(L,wrapper_GetMousePos);
    lua_settable(L,-3);
    
    /*****************************************************************/
    
    lua_bindwrapper(L,ImGui::GetWindowDrawList);
    lua_bindwrapper(L,ImGuiDrawAdapters::GetBackgroundDrawList);
    lua_bindwrapper(L,ImGuiDrawAdapters::GetForegroundDrawList);
    
    lua_bindwrapper(L,ImGuiDrawAdapters::PushClipRect);
    lua_bindwrapper(L,ImGuiDrawAdapters::PushClipRectFullScreen);    
    lua_bindwrapper(L,ImGuiDrawAdapters::PopClipRect);
    
    lua_bindwrapper(L,ImGuiDrawAdapters::PushTextureID);
    lua_bindwrapper(L,ImGuiDrawAdapters::PopTextureID);
    
    lua_bindwrapper(L,ImGuiDrawAdapters::AddLine);
    lua_bindwrapper(L,ImGuiDrawAdapters::AddRect);
    lua_bindwrapper(L,ImGuiDrawAdapters::AddRectFilled);
    lua_bindwrapper(L,ImGuiDrawAdapters::AddRectFilledMultiColor);            
    lua_bindwrapper(L,ImGuiDrawAdapters::AddQuad);
    lua_bindwrapper(L,ImGuiDrawAdapters::AddQuadFilled);
    lua_bindwrapper(L,ImGuiDrawAdapters::AddTriangle);
    lua_bindwrapper(L,ImGuiDrawAdapters::AddTriangleFilled);
    lua_bindwrapper(L,ImGuiDrawAdapters::AddCircle);
    lua_bindwrapper(L,ImGuiDrawAdapters::AddCircleFilled);
    lua_bindwrapper(L,ImGuiDrawAdapters::AddText);
    lua_bindwrapper(L,ImGuiDrawAdapters::AddText2);
    lua_bindwrapper(L,ImGuiDrawAdapters::AddBezierCubic);
    lua_bindwrapper(L,ImGuiDrawAdapters::AddImage);
    lua_bindwrapper(L,ImGuiDrawAdapters::AddImageRounded);         
    lua_bindwrapper(L,ImGuiDrawAdapters::PathClear);
    lua_bindwrapper(L,ImGuiDrawAdapters::PathLineTo);
    lua_bindwrapper(L,ImGuiDrawAdapters::PathLineToMergeDuplicate);
    lua_bindwrapper(L,ImGuiDrawAdapters::PathFillConvex);
    lua_bindwrapper(L,ImGuiDrawAdapters::PathStroke);
    lua_bindwrapper(L,ImGuiDrawAdapters::PathArcTo);
    lua_bindwrapper(L,ImGuiDrawAdapters::PathArcToFast);        
    lua_bindwrapper(L,ImGuiDrawAdapters::PathBezierCubicCurveTo);
    lua_bindwrapper(L,ImGuiDrawAdapters::PathRect);                
    
    lua_pop(L,1);

    DECLARE_IMGUI_CONSTANT(ImDrawFlags_RoundCornersNone);
    DECLARE_IMGUI_CONSTANT(ImDrawFlags_RoundCornersTopLeft);
    DECLARE_IMGUI_CONSTANT(ImDrawFlags_RoundCornersTopRight);
    DECLARE_IMGUI_CONSTANT(ImDrawFlags_RoundCornersBottomLeft);
    DECLARE_IMGUI_CONSTANT(ImDrawFlags_RoundCornersBottomRight);
    DECLARE_IMGUI_CONSTANT(ImDrawFlags_RoundCornersTop);
    DECLARE_IMGUI_CONSTANT(ImDrawFlags_RoundCornersBottom);
    DECLARE_IMGUI_CONSTANT(ImDrawFlags_RoundCornersLeft);
    DECLARE_IMGUI_CONSTANT(ImDrawFlags_RoundCornersRight);
    DECLARE_IMGUI_CONSTANT(ImDrawFlags_RoundCornersAll);
    
}

