module game_core.rendering;

import bubel.ecs.attributes;
import bubel.ecs.core;
import bubel.ecs.std;

import ecs_utils.gfx.texture;
import ecs_utils.math.vector;

import game_core.basic;

void registerRenderingModule(EntityManager* manager)
{
    manager.registerComponent!CLocation;
    manager.registerComponent!CScale;
    manager.registerComponent!CRotation;
    manager.registerComponent!CDepth;
    manager.registerComponent!CColor;
    manager.registerComponent!CSelected;
    manager.registerComponent!CTexCoords;
    manager.registerComponent!CTexCoordsIndex;
    manager.registerComponent!CMaterialIndex;
    manager.registerComponent!CDrawDefault;

    manager.registerSystem!DrawSystem(100);
}

struct CTexCoords
{
    mixin ECS.Component;

    alias value this;///use component as it value

    vec4 value;
}

struct CTexCoordsIndex
{
    mixin ECS.Component; 

    alias value this;

    ushort value;
}

struct CMaterialIndex
{
    mixin ECS.Component;

    alias value this;

    ushort value;
}

struct CDrawDefault
{
    mixin ECS.Component;
}

struct TexCoordsManager
{
    import bubel.ecs.vector;
    import bubel.ecs.hash_map;

    __gshared TexCoordsManager* instance = null;

    static void initialize()
    {
        if(instance is null)instance = Mallocator.make!TexCoordsManager;
    }

    static void destroy()
    {
        if(instance)Mallocator.dispose(instance);
        instance = null;
    }

    vec4 get(ushort index)
    {
        if(index > coords.length)return vec4(0,0,1,1);
        else return coords[index];
    }

    ushort getCoordIndex(vec4 coords)
    {
        ushort ret = coords_map.get(coords, ushort.max);
        if(ret != ushort.max)
        {
            return ret;
        }
        this.coords.add(coords);
        coords_map.add(coords, cast(ushort)(this.coords.length - 1));
        return cast(ushort)(this.coords.length - 1);
    }

    Vector!vec4 coords;
    HashMap!(vec4,ushort) coords_map;
}

struct DrawSystem
{
    mixin ECS.System!32;

    import ecs_utils.gfx.renderer : Renderer;

    struct EntitiesData
    {
        uint length;
        //uint thread_id;
        uint job_id;
        const(Entity)[] entity;
        @readonly CLocation[] locations;
        @readonly @optional CScale[] scale;
        // @readonly CTexCoords[] texcoord;
        @readonly @optional CTexCoords[] texcoord;
        @readonly @optional CTexCoordsIndex[] texcoord_index;
        @readonly @optional CRotation[] rotation;
        @readonly @optional CDepth[] depth;
        @readonly @optional CColor[] color;
        @readonly @optional CMaterialIndex[] material;
        @readonly @optional CDrawDefault[] draw_default;
    }

    Renderer.DrawData default_data;
    float color_time = 0;
    uint select_color = 0;

    bool onBegin()
    {
        import app : launcher;
        color_time += launcher.delta_time * 0.001;
        color_time = color_time - cast(int)(color_time*0.5)*2;
        float ratio = color_time - cast(int)color_time;
        if(color_time > 1)ratio = 1 - ratio;
        uint multipler = cast(uint)(0x60 * ratio);
        select_color = 0xA0A0A0A0 + cast(uint)(0x01010101 * multipler);
        return true;
    }

    void onUpdate(EntitiesData data)
    {
        import app : launcher;

        if(launcher.renderer.prepared_items >= launcher.renderer.MaxObjects)return;//simple leave loop if max visible objects count was reached
        Renderer.DrawData draw_data = default_data;
        draw_data.thread_id = data.job_id;
        
        if(launcher.show_filtered && launcher.filterEntity(data.entity[0]))
        {
            draw_data.color = select_color;
            data.color = null;
        }
        //import std.stdio;
        //writeln(data.draw_default);
        //if(data.draw_default is null && data.texcoord is null && data.texcoord_index is null && !data.entity[0].hasComponent(CDrawDefault.component_id))return;
        
        if(data.texcoord is null && data.texcoord_index is null && data.draw_default is null)return;


        foreach(i; 0..data.length)
        {
            draw_data.position = data.locations[i];
            if(data.color)draw_data.color = data.color[i];
            if(data.depth)draw_data.depth = data.depth[i];
            if(data.rotation)draw_data.angle = data.rotation[i];
            if(data.scale)draw_data.size = data.scale[i];
            if(data.texcoord)draw_data.coords = data.texcoord[i];
            else if(data.texcoord_index)draw_data.coords = TexCoordsManager.instance.get(data.texcoord_index[i]);
            if(data.material)draw_data.material_id = data.material[i];
            launcher.renderer.draw(draw_data);
        }//*/

        /*
        ubyte mode;
        if(data.scale)mode |= 0x01;
        if(data.texcoord)mode |= 0x02;
        if(data.texcoord_index)mode |= 0x04;
        if(data.rotation)mode |= 0x08;
        if(data.depth)mode |= 0x10;
        if(data.color)mode |= 0x20;

        if(launcher.show_filtered && launcher.filterEntity(data.entity[0]))
        {
            draw_data.color = select_color;
            mode &= ~0x20;
            //goto draw_nocolor;
        }
        
        switch(mode)
        {
            case 0:
                foreach(i; 0..data.length)
                {
                    draw_data.position = data.locations[i];
                    launcher.renderer.draw(draw_data);
                }
                break;

            case 0b000001:
                foreach(i; 0..data.length)
                {
                    draw_data.position = data.locations[i];
                    draw_data.size = data.scale[i];
                    launcher.renderer.draw(draw_data);
                }
                break;

            case 0b000010:
                foreach(i; 0..data.length)
                {
                    draw_data.position = data.locations[i];
                    draw_data.coords = data.texcoord[i];
                    launcher.renderer.draw(draw_data);
                }
                break;
            case 0b000011:
                foreach(i; 0..data.length)
                {
                    draw_data.position = data.locations[i];
                    draw_data.size = data.scale[i];
                    draw_data.coords = data.texcoord[i];
                    launcher.renderer.draw(draw_data);
                }
                break;

            case 0b001000:
                foreach(i; 0..data.length)
                {
                    draw_data.position = data.locations[i];
                    draw_data.angle = data.rotation[i];
                    launcher.renderer.draw(draw_data);
                }
                break;
            case 0b001001:
                foreach(i; 0..data.length)
                {
                    draw_data.position = data.locations[i];
                    draw_data.angle = data.rotation[i];
                    draw_data.size = data.scale[i];
                    launcher.renderer.draw(draw_data);
                }
                break;
            case 0b001010:
                foreach(i; 0..data.length)
                {
                    draw_data.position = data.locations[i];
                    draw_data.angle = data.rotation[i];
                    draw_data.coords = data.texcoord[i];
                    launcher.renderer.draw(draw_data);
                }
                break;
            case 0b001011:
                foreach(i; 0..data.length)
                {
                    draw_data.position = data.locations[i];
                    draw_data.angle = data.rotation[i];
                    draw_data.size = data.scale[i];
                    draw_data.coords = data.texcoord[i];
                    launcher.renderer.draw(draw_data);
                }
                break;

            
            case 0b010000:
                foreach(i; 0..data.length)
                {
                    draw_data.position = data.locations[i];
                    draw_data.depth = data.depth[i];
                    launcher.renderer.draw(draw_data);
                }
                break;
            case 0b010001:
                foreach(i; 0..data.length)
                {
                    draw_data.position = data.locations[i];
                    draw_data.depth = data.depth[i];
                    draw_data.size = data.scale[i];
                    launcher.renderer.draw(draw_data);
                }
                break;
            case 0b010010:
                foreach(i; 0..data.length)
                {
                    draw_data.position = data.locations[i];
                    draw_data.depth = data.depth[i];
                    draw_data.coords = data.texcoord[i];
                    launcher.renderer.draw(draw_data);
                }
                break;
            case 0b010011:
                foreach(i; 0..data.length)
                {
                    draw_data.position = data.locations[i];
                    draw_data.depth = data.depth[i];
                    draw_data.size = data.scale[i];
                    draw_data.coords = data.texcoord[i];
                    launcher.renderer.draw(draw_data);
                }
                break;
            case 0b011000:
                foreach(i; 0..data.length)
                {
                    draw_data.position = data.locations[i];
                    draw_data.depth = data.depth[i];
                    draw_data.angle = data.rotation[i];
                    launcher.renderer.draw(draw_data);
                }
                break;
            case 0b011001:
                foreach(i; 0..data.length)
                {
                    draw_data.position = data.locations[i];
                    draw_data.depth = data.depth[i];
                    draw_data.angle = data.rotation[i];
                    draw_data.size = data.scale[i];
                    launcher.renderer.draw(draw_data);
                }
                break;
            case 0b011010:
                foreach(i; 0..data.length)
                {
                    draw_data.position = data.locations[i];
                    draw_data.depth = data.depth[i];
                    draw_data.angle = data.rotation[i];
                    draw_data.coords = data.texcoord[i];
                    launcher.renderer.draw(draw_data);
                }
                break;
            case 0b011011:
                foreach(i; 0..data.length)
                {
                    draw_data.position = data.locations[i];
                    draw_data.depth = data.depth[i];
                    draw_data.angle = data.rotation[i];
                    draw_data.size = data.scale[i];
                    draw_data.coords = data.texcoord[i];
                    launcher.renderer.draw(draw_data);
                }
                break;

            
            case 0b100000:
                foreach(i; 0..data.length)
                {
                    draw_data.position = data.locations[i];
                    draw_data.color = data.color[i];
                    launcher.renderer.draw(draw_data);
                }
                break;
            case 0b100001:
                foreach(i; 0..data.length)
                {
                    draw_data.position = data.locations[i];
                    draw_data.color = data.color[i];
                    draw_data.size = data.scale[i];
                    launcher.renderer.draw(draw_data);
                }
                break;
            case 0b100010:
                foreach(i; 0..data.length)
                {
                    draw_data.position = data.locations[i];
                    draw_data.color = data.color[i];
                    draw_data.coords = data.texcoord[i];
                    launcher.renderer.draw(draw_data);
                }
                break;
            case 0b100011:
                foreach(i; 0..data.length)
                {
                    draw_data.position = data.locations[i];
                    draw_data.color = data.color[i];
                    draw_data.size = data.scale[i];
                    draw_data.coords = data.texcoord[i];
                    launcher.renderer.draw(draw_data);
                }
                break;
            case 0b101000:
                foreach(i; 0..data.length)
                {
                    draw_data.position = data.locations[i];
                    draw_data.color = data.color[i];
                    draw_data.angle = data.rotation[i];
                    launcher.renderer.draw(draw_data);
                }
                break;
            case 0b101001:
                foreach(i; 0..data.length)
                {
                    draw_data.position = data.locations[i];
                    draw_data.color = data.color[i];
                    draw_data.angle = data.rotation[i];
                    draw_data.size = data.scale[i];
                    launcher.renderer.draw(draw_data);
                }
                break;
            case 0b101010:
                foreach(i; 0..data.length)
                {
                    draw_data.position = data.locations[i];
                    draw_data.color = data.color[i];
                    draw_data.angle = data.rotation[i];
                    draw_data.coords = data.texcoord[i];
                    launcher.renderer.draw(draw_data);
                }
                break;
            case 0b101011:
                foreach(i; 0..data.length)
                {
                    draw_data.position = data.locations[i];
                    draw_data.color = data.color[i];
                    draw_data.angle = data.rotation[i];
                    draw_data.size = data.scale[i];
                    draw_data.coords = data.texcoord[i];
                    launcher.renderer.draw(draw_data);
                }
                break;
            case 0b110000:
                foreach(i; 0..data.length)
                {
                    draw_data.position = data.locations[i];
                    draw_data.color = data.color[i];
                    draw_data.depth = data.depth[i];
                    launcher.renderer.draw(draw_data);
                }
                break;
            case 0b110001:
                foreach(i; 0..data.length)
                {
                    draw_data.position = data.locations[i];
                    draw_data.color = data.color[i];
                    draw_data.depth = data.depth[i];
                    draw_data.size = data.scale[i];
                    launcher.renderer.draw(draw_data);
                }
                break;
            case 0b110010:
                foreach(i; 0..data.length)
                {
                    draw_data.position = data.locations[i];
                    draw_data.color = data.color[i];
                    draw_data.depth = data.depth[i];
                    draw_data.coords = data.texcoord[i];
                    launcher.renderer.draw(draw_data);
                }
                break;
            case 0b110011:
                foreach(i; 0..data.length)
                {
                    draw_data.position = data.locations[i];
                    draw_data.color = data.color[i];
                    draw_data.depth = data.depth[i];
                    draw_data.size = data.scale[i];
                    draw_data.coords = data.texcoord[i];
                    launcher.renderer.draw(draw_data);
                }
                break;
            case 0b111000:
                foreach(i; 0..data.length)
                {
                    draw_data.position = data.locations[i];
                    draw_data.color = data.color[i];
                    draw_data.depth = data.depth[i];
                    draw_data.angle = data.rotation[i];
                    launcher.renderer.draw(draw_data);
                }
                break;
            case 0b111001:
                foreach(i; 0..data.length)
                {
                    draw_data.position = data.locations[i];
                    draw_data.color = data.color[i];
                    draw_data.depth = data.depth[i];
                    draw_data.angle = data.rotation[i];
                    draw_data.size = data.scale[i];
                    launcher.renderer.draw(draw_data);
                }
                break;
            case 0b111010:
                foreach(i; 0..data.length)
                {
                    draw_data.position = data.locations[i];
                    draw_data.color = data.color[i];
                    draw_data.depth = data.depth[i];
                    draw_data.angle = data.rotation[i];
                    draw_data.coords = data.texcoord[i];
                    launcher.renderer.draw(draw_data);
                }
                break;
            case 0b111011:
                foreach(i; 0..data.length)
                {
                    draw_data.position = data.locations[i];
                    draw_data.color = data.color[i];
                    draw_data.depth = data.depth[i];
                    draw_data.angle = data.rotation[i];
                    draw_data.size = data.scale[i];
                    draw_data.coords = data.texcoord[i];
                    launcher.renderer.draw(draw_data);
                }
                break;
            default:break;
        }//*/
/*
        if(!data.color)
        {
        draw_nocolor:
            if(!data.depth)
            {
                if(!data.rotation)
                {
                    foreach(i; 0..data.length)
                    {
                        draw_data.coords = data.texcoord[i];
                        draw_data.size = data.scale[i];
                        draw_data.position = data.locations[i];
                        launcher.renderer.draw(draw_data);
                    }
                }
                else
                {
                    foreach(i; 0..data.length)
                    {
                        draw_data.angle = data.rotation[i];
                        draw_data.coords = data.texcoord[i];
                        draw_data.size = data.scale[i];
                        draw_data.position = data.locations[i];
                        launcher.renderer.draw(draw_data);
                    }
                }
            }
            else
            {
                if(!data.rotation)
                {
                    foreach(i; 0..data.length)
                    {
                        draw_data.depth = data.depth[i];
                        draw_data.coords = data.texcoord[i];
                        draw_data.size = data.scale[i];
                        draw_data.position = data.locations[i];
                        launcher.renderer.draw(draw_data);
                    }
                }
                else
                {
                    foreach(i; 0..data.length)
                    {
                        draw_data.depth = data.depth[i];
                        draw_data.angle = data.rotation[i];
                        draw_data.coords = data.texcoord[i];
                        draw_data.size = data.scale[i];
                        draw_data.position = data.locations[i];
                        launcher.renderer.draw(draw_data);
                    }
                }
            }
        }
        else
        {
            if(!data.depth)
            {
                if(!data.rotation)
                {
                    foreach(i; 0..data.length)
                    {
                        draw_data.color = data.color[i];
                        draw_data.coords = data.texcoord[i];
                        draw_data.size = data.scale[i];
                        draw_data.position = data.locations[i];
                        launcher.renderer.draw(draw_data);
                    }
                }
                else
                {
                    foreach(i; 0..data.length)
                    {
                        draw_data.color = data.color[i];
                        draw_data.angle = data.rotation[i];
                        draw_data.coords = data.texcoord[i];
                        draw_data.size = data.scale[i];
                        draw_data.position = data.locations[i];
                        launcher.renderer.draw(draw_data);
                    }
                }
            }
            else
            {
                if(!data.rotation)
                {
                    foreach(i; 0..data.length)
                    {
                        draw_data.depth = data.depth[i];
                        draw_data.color = data.color[i];
                        draw_data.coords = data.texcoord[i];
                        draw_data.size = data.scale[i];
                        draw_data.position = data.locations[i];
                        launcher.renderer.draw(draw_data);
                    }
                }
                else
                {
                    foreach(i; 0..data.length)
                    {
                        draw_data.depth = data.depth[i];
                        draw_data.color = data.color[i];
                        draw_data.coords = data.texcoord[i];
                        draw_data.size = data.scale[i];
                        draw_data.position = data.locations[i];
                        launcher.renderer.draw(draw_data);
                    }
                }
            }
        }*/
    }
}