module app;

import bindbc.sdl;

import cimgui.cimgui;

import game_core.job_updater;

import ecs.manager;
import ecs.core;
import ecs.std;

import ecs_utils.gfx.renderer;
import ecs_utils.imgui_bind;
import ecs_utils.imgui_styles;
import ecs_utils.math.vector;
import ecs_utils.utils;

import glad.gl.gl;
import glad.gl.gles2;
import glad.gl.loader;

import gui.manager;

extern (C) :

enum Tool
{
    entity_spawner,
    component_manipulator,
    selector
}

__gshared const (char)*[3] tool_strings = ["Entity spawner", "Component manipulator", "Selector"];

struct Mouse
{
    vec2 position;
    bool left, right, middle;
}

struct Launcher 
{
    ECSJobUpdater* job_updater;
    GUIManager* gui_manager;
    ImGuiContext* context;
    SDL_Window* window;
    SDL_GLContext gl_context;
    EntityManager* manager;
    bool function() loop;
    void function() end;
    void function(SDL_Event*) event;
    void function(vec2, Tool, int) tool;
    ivec2 window_size = ivec2(1024,768);
    Renderer renderer;
    ubyte[] keys;
    uint style = 3;
    uint entities_count;
    bool multithreading;
    ulong timer_freq;
    double delta_time;
    uint fps;

    Tool used_tool;
    int tool_size = 0;
    float tool_repeat = 0;
    float repeat_time = 0;

    bool swap_interval = true;

    float windows_alpha = 0.75;

    const (char)* tips;

    bool show_stat_wnd = true;
    bool show_tips = true;
    bool show_demo_wnd = true;
    bool show_virtual_keys_wnd = false;
    bool show_profile_wnd = true;

    int plot_index;
    PlotStruct[] plot_values;

    Mouse mouse;

    struct PlotStruct
    {
        float delta_time = 0;
        float loop_time = 0;
        float draw_time = 0;
    }

    void switchDemo(void function() start, bool function() loop, void function() end, void function(SDL_Event*) event, void function(vec2, Tool, int) tool, const (char)* tips)
    {
        gui_manager.clear();

        if(this.end)this.end();

        manager.begin();
        manager.update("clean");
        manager.end();

        if(start)start();
        this.loop = loop;
        this.end = end;
        this.event = event;
        this.tips = tips;
        this.tool = tool;
    }

    bool getKeyState(SDL_Scancode key)
    {
        if(keys[key])return true;
        else return false;
    }

    void setStyle(uint index)
    {
        style = index;
        .setStyle(index);
    }

    double getTime()
    {
        ulong local_freq = timer_freq / 1000_000;
        //return cast(double)(SDL_GetPerformanceCounter() & 0x00FFFFFF) / timer_freq;
        if(local_freq == 0)return cast(double)(SDL_GetPerformanceCounter() / timer_freq * 1000);
        else return cast(double)(SDL_GetPerformanceCounter() / local_freq) / 1000;
    }
}

__gshared Launcher launcher;

struct CountSystem
{
    mixin ECS.System!1;

    struct EntitiesData
    {
        uint length;
    }

    bool onBegin()
    {
        launcher.entities_count = 0;
        return true;
    }

    void onUpdate(EntitiesData data)
    {
        launcher.entities_count += data.length;
    }
}

struct CleanSystem
{
    mixin ECS.System!64;

    struct EntitiesData
    {
        uint length;
        Entity[] entities;
    }

    void onUpdate(EntitiesData data)
    {
        foreach(i; 0..data.length)
        {
            launcher.manager.removeEntity(data.entities[i].id);
        }
    }
}

void mainLoop(void* arg)
{
    __gshared double time = 0;
    launcher.delta_time = launcher.getTime() - time;
    time = launcher.getTime();

    if(launcher.delta_time > 1000)launcher.delta_time = 1000;

    __gshared uint temp_fps = 0;
    __gshared double fps_time = 0;
    temp_fps++;
    fps_time += launcher.delta_time;
    while(fps_time > 1000)
    {
        fps_time -= 1000;
        launcher.fps = temp_fps;
        temp_fps = 0;
    }


    SDL_Event event;
    while (SDL_PollEvent(&event)) 
    {
        version(WebAssembly)ImGui_ImplSDL2_ProcessEvent(&event);
        else ImGui_ImplSDL2_ProcessEvent(&event);
        if(launcher.event)launcher.event(&event);
        if (event.type == SDL_QUIT || (event.type == SDL_KEYDOWN && event.key.keysym.scancode == SDL_SCANCODE_ESCAPE)) {
            quit();
            *cast(bool*)arg = false;
            return;
        }
        else if(event.type == SDL_WINDOWEVENT)
        {
            switch(event.window.event)
            {
                case SDL_WINDOWEVENT_RESIZED:
                    //launcher.renderer.resize(ivec2(event.window.data1,event.window.data2));
                    launcher.window_size = ivec2(event.window.data1,event.window.data2);
                    break;
                case SDL_WINDOWEVENT_SIZE_CHANGED:
                    //launcher.renderer.resize(ivec2(event.window.data1,event.window.data2));
                    launcher.window_size = ivec2(event.window.data1,event.window.data2);
                    break;
                default:break;
            }
        }
        else if(event.type == SDL_MOUSEBUTTONDOWN)
        {
            switch(event.button.button)
            {
                case SDL_BUTTON_LEFT:launcher.mouse.left = true;break;
                case SDL_BUTTON_RIGHT:launcher.mouse.right = true;break;
                case SDL_BUTTON_MIDDLE:launcher.mouse.middle = true;break;
                default:break;
            }
            if(launcher.tool && event.button.button == SDL_BUTTON_LEFT && launcher.tool_repeat == 0 && !igIsWindowHovered(ImGuiHoveredFlags_AnyWindow))
            {
                launcher.tool(vec2(event.button.x, launcher.window_size.y - event.button.y), launcher.used_tool, launcher.tool_size);
            }
        }
        else if(event.type == SDL_MOUSEBUTTONUP)
        {
            switch(event.button.button)
            {
                case SDL_BUTTON_LEFT:launcher.mouse.left = false;break;
                case SDL_BUTTON_RIGHT:launcher.mouse.right = false;break;
                case SDL_BUTTON_MIDDLE:launcher.mouse.middle = false;break;
                default:break;
            }
        }
        else if(event.type == SDL_MOUSEMOTION)
        {
            launcher.mouse.position = vec2(event.motion.x, launcher.window_size.y - event.motion.y);
        }
    }

    if(launcher.tool && launcher.tool_repeat != 0 && launcher.mouse.left && !igIsWindowHovered(ImGuiHoveredFlags_AnyWindow) && !igIsWindowFocused(ImGuiFocusedFlags_AnyWindow))
    {
        float range = 500.0 / cast(float)launcher.tool_repeat;
        launcher.repeat_time += launcher.delta_time;
        while(launcher.repeat_time > range)
        {
            launcher.repeat_time -= range;
            launcher.tool(launcher.mouse.position, launcher.used_tool, launcher.tool_size);
        }
        
    }

    version(WebAssembly)
    {
        ImGui_ImplOpenGL3_NewFrame();
        ImGui_ImplSDL2_NewFrame(launcher.window);
    }
    else
    {
        ImGuiImplOpenGL2NewFrame();
        ImGuiImplSDL2NewFrame(launcher.window);
    }

    igNewFrame();

    if(igBeginMainMenuBar())
    {
        if(igBeginMenu("File",true))
        {
            if(igMenuItemBool("Close","esc",false,true))
            {
                quit();
                *cast(bool*)arg = false;
                return;
            }
            igEndMenu();
        }
        if(igBeginMenu("Demos",true))
        {
            if(igMenuItemBool("Simpe",null,false,true))
            {
                import demos.simple;
                launcher.switchDemo(&simpleStart,&simpleLoop,&simpleEnd,&simpleEvent,&simpleTool,Simple.tips);
            }
            if(igMenuItemBool("Snake",null,false,true))
            {
                import demos.snake;
                launcher.switchDemo(&snakeStart,&snakeLoop,&snakeEnd,&snakeEvent,&snakeTool,Snake.tips);
            } 
            if(igMenuItemBool("Space invaders",null,false,true))
            {
                import demos.space_invaders;
                launcher.switchDemo(&spaceInvadersStart,&spaceInvadersLoop,&spaceInvadersEnd,&spaceInvadersEvent,&spaceInvadersTool,SpaceInvaders.tips);
            }
            igEndMenu();
        }
        if(igBeginMenu("Options",true))
        {
            if(igMenuItemBool("VSync", null, launcher.swap_interval, true))
            {
                launcher.swap_interval = !launcher.swap_interval;
                SDL_GL_SetSwapInterval(launcher.swap_interval);
            }
            if(igMenuItemBool("Multithreading", null, launcher.multithreading, true))
            {
                launcher.multithreading = !launcher.multithreading;
            }
            if(igBeginMenu("Show",true))
            {
                if(igMenuItemBool("Statistics",null,launcher.show_stat_wnd,true))
                {
                    launcher.show_stat_wnd = !launcher.show_stat_wnd;
                }
                else if(igMenuItemBool("Demo",null,launcher.show_demo_wnd,true))
                {
                    launcher.show_demo_wnd = !launcher.show_demo_wnd;
                }
                else if(igMenuItemBool("Tips",null,launcher.show_tips,true))
                {
                    launcher.show_tips = !launcher.show_tips;
                }
                else if(igMenuItemBool("Virual keys",null,launcher.show_virtual_keys_wnd,true))
                {
                    launcher.show_virtual_keys_wnd = !launcher.show_virtual_keys_wnd;
                }
                igEndMenu();
            }
            if(igBeginMenu("Style",true))
            {
                if(igMenuItemBool("Classic",null,launcher.style == 0,true))
                {
                    launcher.setStyle(0);
                }
                else if(igMenuItemBool("Dark",null,launcher.style == 1,true))
                {
                    launcher.setStyle(1);
                }
                else if(igMenuItemBool("Light",null,launcher.style == 2,true))
                {
                    launcher.setStyle(2);
                }
                else if(igMenuItemBool("Default",null,launcher.style == 3,true))
                {
                    launcher.setStyle(3);
                }
                igEndMenu();
            }
            igEndMenu();
        }
        igEndMainMenuBar();
    }

    if(launcher.show_virtual_keys_wnd)
    {
        igSetNextWindowPos(ImVec2(launcher.window_size.x - 400, launcher.window_size.y - 200), ImGuiCond_Once, ImVec2(0,0));
        igSetNextWindowSize(ImVec2(64*4+128, 168), ImGuiCond_Once);
        igSetNextWindowBgAlpha(launcher.windows_alpha);
        ImGuiStyle * style = igGetStyle();
        style.Colors[ImGuiCol_Button].w = 0.8;
        if(igBegin("Virtual keys",&launcher.show_virtual_keys_wnd, 0))
        {
            igColumns(2,null,0);
            igSetColumnWidth(0,64*3+16);
            igIndent(64+4);
            if(igButton("W",ImVec2(64,64)) || igIsItemDeactivated())
            {
                launcher.keys[SDL_SCANCODE_W] = false;
            }
            else if(igIsItemActive())
            {
                launcher.keys[SDL_SCANCODE_W] = true;
            }
            igUnindent(64+4);
            if(igButton("A",ImVec2(64,64)) || igIsItemDeactivated())
            {
                launcher.keys[SDL_SCANCODE_A] = false;
            }
            else if(igIsItemActive())
            {
                launcher.keys[SDL_SCANCODE_A] = true;
            }
            igSameLine(0,4);
            if(igButton("S",ImVec2(64,64)) || igIsItemDeactivated())
            {
                launcher.keys[SDL_SCANCODE_S] = false;
            }
            else if(igIsItemActive())
            {
                launcher.keys[SDL_SCANCODE_S] = true;
            }
            igSameLine(0,4);
            if(igButton("D",ImVec2(64,64)) || igIsItemDeactivated())
            {
                launcher.keys[SDL_SCANCODE_D] = false;
            }
            else if(igIsItemActive())
            {
                launcher.keys[SDL_SCANCODE_D] = true;
            }
            igNextColumn();
            if(igButton("Space",ImVec2(-1,128)) || igIsItemDeactivated())
            {
                launcher.keys[SDL_SCANCODE_SPACE] = false;
            }
            else if(igIsItemActive())
            {
                launcher.keys[SDL_SCANCODE_SPACE] = true;
            }
            igColumns(1,null,0);
        }
        igEnd();
        style.Colors[ImGuiCol_Button].w = 1.0;
    }

    if(launcher.show_tips)
    {
        igSetNextWindowPos(ImVec2(launcher.window_size.x - 550, 80), ImGuiCond_Once, ImVec2(0,0));
        igSetNextWindowSize(ImVec2(300, 0), ImGuiCond_Once);
        igSetNextWindowBgAlpha(launcher.windows_alpha);
        if(igBegin("Tips",&launcher.show_tips,ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings))
        {
            igTextWrapped(launcher.tips);
        }
        igEnd();
    }

    if(launcher.show_demo_wnd)
    {
        igSetNextWindowPos(ImVec2(launcher.window_size.x - 260, 30), ImGuiCond_Once, ImVec2(0,0));
        igSetNextWindowSize(ImVec2(250, 250), ImGuiCond_Once);
        if(igBegin("Demo",&launcher.show_demo_wnd,0))
        {
            ImDrawList* draw_list = igGetWindowDrawList();
            //igBeginGroup();
            launcher.gui_manager.gui();
            //igEndGroup();
            //ImDrawList_AddRect(draw_list, igGetItemRectMin(), igGetItemRectMax(), igColorConvertFloat4ToU32(ImVec4(0.4,0.4,0.4,0.4)), -1, 0, 1);
            //igBeginChildFrame(1,ImVec2(0,-1),ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_ChildWindow);
            //igBeginChild("Tool frame",ImVec2(-1,-1),true,0);
            if(igCollapsingHeader("Tool##ToolHeader", ImGuiTreeNodeFlags_SpanAvailWidth))
            {
                igIndent(8);
                igBeginGroup();
                if(igBeginCombo("Tool",tool_strings[launcher.used_tool],0))
                {
                    if(igSelectable("Entity spawner",false,0,ImVec2(0,0)))
                    {
                        launcher.used_tool = Tool.entity_spawner;
                    }
                    if(igSelectable("Component manipulator",false,0,ImVec2(0,0)))
                    {
                        launcher.used_tool = Tool.component_manipulator;
                    }
                    if(igSelectable("Selector",false,0,ImVec2(0,0)))
                    {
                        launcher.used_tool = Tool.selector;
                    }
                    igEndCombo();
                }

                igSliderInt("Tool size", &launcher.tool_size, 0, 256, null);
                igSliderFloat("Tool repeat", &launcher.tool_repeat, 0, 1024, null, 4);
                launcher.gui_manager.toolGui();
                igEndGroup();
                ImDrawList_AddRect(draw_list, igGetItemRectMin(), igGetItemRectMax(), igColorConvertFloat4ToU32(ImVec4(0.4,0.4,0.4,0.4)), 4, ImDrawCornerFlags_All, 1);
                igUnindent(8);
            }

            //igEndChild();
            //igEndChildFrame();
            if(igButton("Clear",ImVec2(-1,0)))
            {
                launcher.manager.begin();
                launcher.manager.update("clean");
                launcher.manager.end();
            }
        }
        igEnd();
    }

    if(launcher.show_profile_wnd)
    {
        igSetNextWindowPos(ImVec2(launcher.window_size.x - 260, launcher.window_size.y - 280), ImGuiCond_Once, ImVec2(0,0));
        igSetNextWindowSize(ImVec2(250, 250), ImGuiCond_Once);
        if(igBegin("Profile",&launcher.show_profile_wnd,0))
        {
            if(igBeginTabBar("ProfileTabBar",ImGuiTabBarFlags_FittingPolicyScroll))
            {
                
                    if(igBeginTabItem("Plots", null, 0))
                    {
                        if(igBeginChild("ProfileChild",ImVec2(-1,-1),0,0))
                        {
                            igPlotLines("Delta time",&launcher.plot_values[0].delta_time,100,launcher.plot_index,"Delta time",0,float.max,ImVec2(0,64),Launcher.PlotStruct.sizeof);
                            igPlotLines("Loop time",&launcher.plot_values[0].loop_time,100,launcher.plot_index,"Loop time",0,float.max,ImVec2(0,64),Launcher.PlotStruct.sizeof);
                            igPlotLines("Draw time",&launcher.plot_values[0].draw_time,100,launcher.plot_index,"Draw time",0,float.max,ImVec2(0,64),Launcher.PlotStruct.sizeof);  
                        }
                        igEndChild();
                        igEndTabItem();
                    }
                    if(igBeginTabItem("Multithreading graph", null, 0))
                    {
                        if(igBeginChild("ProfileChild",ImVec2(-1,-1),0,0))
                        {
                            igText("Work in proggress");
                        }
                        igEndChild();
                        igEndTabItem();
                    }
                
                igEndTabBar();
            }
        }
        igEnd();
    }   

    launcher.renderer.resize(launcher.window_size);
    launcher.renderer.view(vec2(0,0),vec2(launcher.window_size.x,launcher.window_size.y));
    //glClear(GL_COLOR_BUFFER_BIT);
    launcher.renderer.clear();

    double loop_time = launcher.getTime();
    if(launcher.loop && !launcher.loop())
    {   
        quit();
        *cast(bool*)arg = false;
    }
    loop_time = launcher.getTime() - loop_time;

    double draw_time = launcher.getTime();
    launcher.renderer.present();
    draw_time = launcher.getTime() - draw_time;

    __gshared float plot_time = 0;
    __gshared uint plot_samples = 0;
    plot_time += launcher.delta_time;
    plot_samples++;
    launcher.plot_values[100].delta_time += launcher.delta_time;
    launcher.plot_values[100].loop_time += loop_time;
    launcher.plot_values[100].draw_time += draw_time;
    if(plot_time > 100)
    {
        launcher.plot_values[launcher.plot_index].delta_time = launcher.plot_values[100].delta_time / cast(float)plot_samples;
        launcher.plot_values[launcher.plot_index].loop_time = launcher.plot_values[100].loop_time / cast(float)plot_samples;
        launcher.plot_values[launcher.plot_index].draw_time = launcher.plot_values[100].draw_time / cast(float)plot_samples;
        launcher.plot_values[100].delta_time = 0;
        launcher.plot_values[100].loop_time = 0;
        launcher.plot_values[100].draw_time = 0;

        plot_samples = 0;
        plot_time -= 100;
        launcher.plot_index++;
        if(launcher.plot_index >= 100)launcher.plot_index = 0;
    }

    if(launcher.show_stat_wnd)
    {
        igSetNextWindowPos(ImVec2(10, 30), ImGuiCond_Appearing, ImVec2(0,0));
        igSetNextWindowContentSize(ImVec2(150, 0.0f));
        igSetNextWindowBgAlpha(launcher.windows_alpha);
        if(igBegin("Statistics", &launcher.show_stat_wnd, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize))
        {
            igColumns(2,null,0);
            igSetColumnWidth(0,100);
            igText("FPS: ");
            igText("Entities: ");
            igText("Loop time: ");
            igText("Draw time: ");
            igText("Delta time: ");
            igNextColumn();
            igText("%u",launcher.fps);
            igText("%u",launcher.entities_count);
            igText("%2.2fms",loop_time);
            igText("%2.2fms",draw_time);
            igText("%2.2fms",launcher.delta_time);
            igColumns(1,null,0);
        }
        igEnd();
    }

    glUseProgram(0);
    import ecs_utils.gfx.buffer;
    Buffer.unbind(Buffer.BindTarget.array);
    Buffer.unbind(Buffer.BindTarget.element_array);

    igRender();
    version(WebAssembly)ImGui_ImplOpenGL3_RenderDrawData(igGetDrawData());
    else ImGuiImplOpenGL2RenderDrawData(igGetDrawData());
    
    //launcher.renderer.clear();
    //launcher.renderer.present();

    SDL_GL_SwapWindow(launcher.window);

}

void quit()
{
    launcher.gui_manager.clear();
    Mallocator.dispose(launcher.gui_manager);

    launcher.manager.destroy();
    launcher.manager = null;

    version(WebAssembly)emscripten_cancel_main_loop();
}

int main(int argc, char** argv)
{

    if (SDL_Init(SDL_INIT_VIDEO) < 0)
    {
        printf("SDL could not initialize! SDL_Error: %s", SDL_GetError());
        return -1;
    }

    SDL_version sdl_version;
    SDL_GetVersion(&sdl_version);
    printf("SDL version: %u.%u.%u\n",cast(uint)sdl_version.major,cast(uint)sdl_version.minor,cast(uint)sdl_version.patch);

    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
    launcher.window = SDL_CreateWindow("Simple", SDL_WINDOWPOS_CENTERED,
                SDL_WINDOWPOS_CENTERED, launcher.window_size.x, launcher.window_size.y, SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);

    launcher.gl_context = SDL_GL_CreateContext(launcher.window);
    launcher.context = igCreateContext(null);
    launcher.timer_freq = SDL_GetPerformanceFrequency();

    launcher.plot_values = Mallocator.makeArray!(Launcher.PlotStruct)(101);

    SDL_GetWindowSize(launcher.window, &launcher.window_size.x, &launcher.window_size.y);

    version(WebAssembly)
    {
        gladLoadGLES2(x => SDL_GL_GetProcAddress(x));
        if(!ImGui_ImplSDL2_InitForOpenGL(launcher.window,launcher.gl_context))
        {
            printf("ImGui initialization failed!");
            return -2;
        }
        if(!ImGui_ImplOpenGL3_Init())
        {
            printf("ImGui OpenGL initialization failed!");
            return -3;
        }
    }
    else 
    {
        gladLoadGL();
        if(!ImGuiImplSDL2InitForOpenGL(launcher.window,launcher.gl_context))
        {
            printf("ImGui initialization failed!");
            return -2;
        }
        if(!ImGuiImplOpenGL2Init())
        {
            printf("ImGui OpenGL initialization failed!");
            return -3;
        }
    }

    ImFontConfig* config = ImFontConfig_ImFontConfig();
    ImGuiIO* io = igGetIO();
    const ushort* font_ranges = ImFontAtlas_GetGlyphRangesDefault(io.Fonts);
    ImFontAtlas_AddFontFromFileTTF(io.Fonts,"assets/fonts/Ruda-Bold.ttf", 15.0f, config, font_ranges);

    setStyle(3);

    launcher.job_updater = Mallocator.make!ECSJobUpdater(12);
    //launcher.job_updater.onCreate();

    EntityManager.initialize(12);
    launcher.manager = EntityManager.instance;

    //launcher.manager.m_thread_id_func = &launcher.job_updater.getThreadID;
    //launcher.manager.setJobDispachFunc(&launcher.job_updater.dispatch);
    launcher.manager.setMultithreadingCallbacks(&launcher.job_updater.dispatch, &launcher.job_updater.getThreadID);

    launcher.manager.beginRegister();

    launcher.manager.registerPass("clean");

    launcher.manager.registerSystem!CountSystem(10000);
    launcher.manager.registerSystem!CleanSystem(0,"clean");
    
    launcher.manager.endRegister();

    loadGFX();

    launcher.renderer.initialize();

    launcher.gui_manager = Mallocator.make!GUIManager();

    {
        import demos.simple;
        launcher.switchDemo(&simpleStart,&simpleLoop,&simpleEnd,&simpleEvent,&simpleTool,Simple.tips);
    }

    int key_num;
    ubyte* keys = SDL_GetKeyboardState(&key_num);
    launcher.keys = keys[0..key_num];

    version(WebAssembly)
    {
        /*EmscriptenFullscreenStrategy strategy;
        strategy.scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH;
        strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF;
        strategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_NEAREST;*/

        //int result = emscripten_request_fullscreen_strategy(null, true, &strategy);
        //emscripten_enter_soft_fullscreen(null, &strategy);
        //printf("Fullscreen request: %s.\n", emscripten_result_to_string(result));
        emscripten_set_main_loop_arg(&mainLoop, null, 0, 1);
    }
    else
    {
        bool arg = true;
        while(arg == true)
        {
            mainLoop(&arg);
        }
    }

    return 0;
}

void loadGFX()
{
    import ecs_utils.gfx.texture;
    import ecs_utils.gfx.vertex;
    import ecs_utils.gfx.shader;
    import ecs_utils.gfx.material;
    import ecs_utils.gfx.config;
    import ecs_utils.gfx.mesh;

    Texture.__loadBackend();
    Renderer.__loadBackend();

    GfxConfig.materials = Mallocator.makeArray!Material(1);
    GfxConfig.meshes = Mallocator.makeArray!Mesh(1);

    float[16] vertices = [-0.5,-0.5, 0,0, -0.5,0.5, 0,1, 0.5,-0.5, 1,0, 0.5,0.5, 1,1];
    GfxConfig.meshes[0].vertices = Mallocator.makeArray(vertices);
    ushort[6] indices = [0,1,2,1,2,3];
    GfxConfig.meshes[0].indices = Mallocator.makeArray(indices);
    GfxConfig.meshes[0].uploadData();

    Shader vsh;
    vsh.create();
    vsh.load("assets/shaders/base.vp");
    vsh.compile();

    Shader fsh;
    fsh.create();
    fsh.load("assets/shaders/base.fp");
    fsh.compile();

    GfxConfig.materials[0].create();
    GfxConfig.materials[0].data.blend_mode = Material.BlendMode.opaque;
    GfxConfig.materials[0].data.mode = Material.TransformMode.position;
    Material.ShaderModule[1] modules = [Material.ShaderModule(vsh,fsh)];
    GfxConfig.materials[0].attachModules(modules);
    //GfxConfig.materials[0].
    //GfxConfig.materials[0].load(load_data.materials[i].str);
    GfxConfig.materials[0].compile();
    GfxConfig.materials[0].bindAttribLocation("positions",0);
    GfxConfig.materials[0].bindAttribLocation("tex_coords",1);
    GfxConfig.materials[0].link();
    
    GfxConfig.materials[0].data.uniforms = Mallocator.makeArray!(Material.Uniform)(3);
    GfxConfig.materials[0].data.uniforms[0] = Material.Uniform(Material.Type.float4, GfxConfig.materials[0].getLocation("matrix_1"), 0);
    GfxConfig.materials[0].data.uniforms[1] = Material.Uniform(Material.Type.float4, GfxConfig.materials[0].getLocation("matrix_2"), 16);
    GfxConfig.materials[0].data.uniforms[2] = Material.Uniform(Material.Type.float4, GfxConfig.materials[0].getLocation("uv_transform"), 32);
    GfxConfig.materials[0].data.bindings = Mallocator.makeArray!(int)(1);
    GfxConfig.materials[0].data.bindings[0] = GfxConfig.materials[0].getLocation("tex");
    
    
    /*glUseProgram(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);*/
}