inline b32 AreEqual(render_handle A, render_handle B)
{
    b32 Result = false;
    if((A.U64[0] == B.U64[0]) && (A.U64[1] == B.U64[1]) &&
       (A.U64[2] == B.U64[2]) && (A.U64[3] == B.U64[3]))
    {
        Result = true;
    }
    return(Result);
}

inline v2_s32 DimFromTexture(render_handle Handle)
{
    v2_s32 Result = V2S32(Handle.U32[2], Handle.U32[3]);
    return(Result);
}

inline render_handle EmptyRenderHandle(void)
{
    render_handle Result = {};
    return(Result);
}

#define PushCommand(Group, type) (type *)PushCommand_(Group, Render_Command_##type, sizeof(type))
static void *PushCommand_(render_group *Group, render_command_type Type, u64 Size)
{
    void *Result = 0;
    
    vn_render_commands *Commands = Group->Commands;
    
    u64 TotalSize = Size + sizeof(render_command_header);
    
    u8 *PushBufferEnd = Commands->PushBufferBase + Commands->MaxPushBufferSize;
    if((Commands->PushBufferAt + TotalSize) <= PushBufferEnd)
    {
        render_command_header *Header = (render_command_header *)Commands->PushBufferAt;
        Header->Type = Type;
        
        Commands->PushBufferAt += sizeof(*Header);
        
        Group->CurrentCommand = Header;
        
        Result = Commands->PushBufferAt;
        Commands->PushBufferAt += Size;
        Fill(Result, 0, Size);
    }
    else
    {
        InvalidCodepath;
    }
    
    return(Result);
}

inline render_group BeginRenderGroup(vn_render_commands *Commands)
{
    render_group Group = {};
    Group.Commands = Commands;
    Group.ClipStack[0].Max = Commands->RenderDim;
    return(Group);
} 

inline void PushClear(render_group *Group, v3 Color)
{
    render_command_clear *Command = PushCommand(Group, render_command_clear);
    Command->Color = Color;
}

inline s32 GetTextureIndexForCommand(render_texture_mapping *Mapping, render_handle Handle)
{
    s32 Result = -1;
    
    for(s32 TextureIndex = 0;
        TextureIndex < MAX_BOUND_TEXTURES;
        ++TextureIndex)
    {
        if(AreEqual(Mapping->Textures[TextureIndex], EmptyRenderHandle()))
        {
            Assert(Mapping->TexturesUsed == TextureIndex);
            
            Mapping->Textures[TextureIndex] = Handle;
            ++Mapping->TexturesUsed;
        }
        
        if(AreEqual(Mapping->Textures[TextureIndex], Handle))
        {
            Result = TextureIndex;
            break;
        }
    }
    
    return(Result);
}

inline u32 PackV4ToU32(v4 V)
{
    u32 Result =
    ((u8)(Clamp01(V.x)*255.0)<<24) |
    ((u8)(Clamp01(V.y)*255.0)<<16) |
    ((u8)(Clamp01(V.z)*255.0)<<8)  |
    ((u8)(Clamp01(V.w)*255.0)<<0);
    return(Result);
}

#if VN_USE_INSTANCING
static void PushTexturedQuad(render_group *Group,
                             range2_r32 Dest,
                             range2_r32 Source,
                             v4 Color00, v4 Color10, v4 Color01, v4 Color11,
                             r32 CornerRadius, r32 EdgeSoftness, r32 BorderThickness,
                             render_handle Texture)
{
    vn_render_commands *Commands = Group->Commands;
    
    render_command_instanced_quads *Command = (render_command_instanced_quads *)(Group->CurrentCommand + 1);
    s32 TextureIndex;
    if(!(Group->CurrentCommand && Group->CurrentCommand->Type == Render_Command_render_command_instanced_quads &&
         (TextureIndex = GetTextureIndexForCommand(&Command->Mapping, Texture)) != -1))
    {
        Command = PushCommand(Group, render_command_instanced_quads);
        TextureIndex = GetTextureIndexForCommand(&Command->Mapping, Texture);
        Command->QuadBufferIndex = Commands->InstancedQuadCount;
    }
    
    //if(InRange(Clip, Dest.Min) || InRange(Clip, Dest.Max))
    {
        v2_s32 TextureSize = DimFromTexture(Texture);
        v2_r32 TextureSizeReal = ConvertV2ToR32(TextureSize);
        
        Source.Min /= TextureSizeReal;
        Source.Max /= TextureSizeReal;
        
#if 0        
        Dest.Min.x = Round(Dest.Min.x);
        Dest.Min.y = Round(Dest.Min.y);
#endif
        
        instanced_quad *Quad = Commands->InstancedQuadBase + Commands->InstancedQuadCount++;
        Quad->Dest = Dest;
        Quad->Source = Source;
        Quad->TextureIndex = TextureIndex;
        Quad->Color[0] = PackV4ToU32(Color00);
        Quad->Color[1] = PackV4ToU32(Color01);
        Quad->Color[2] = PackV4ToU32(Color10);
        Quad->Color[3] = PackV4ToU32(Color11);
        Quad->CornerRadius = CornerRadius;
        Quad->EdgeSoftness = EdgeSoftness;
        Quad->BorderThickness = BorderThickness;
        
        Command->QuadCount += 1;
    }
}
#else
static void PushTexturedQuad(render_group *Group,
                             range2_r32 Dest,
                             range2_r32 Source,
                             v4 Color00, v4 Color10, v4 Color01, v4 Color11,
                             r32 CornerRadius, r32 EdgeSoftness, r32 BorderThickness,
                             render_handle Texture)
{
    vn_render_commands *Commands = Group->Commands;
    
    render_command_quads *Command = (render_command_quads *)(Group->CurrentCommand + 1);
    s32 TextureIndex;
    if(!(Group->CurrentCommand && Group->CurrentCommand->Type == Render_Command_render_command_quads &&
         (TextureIndex = GetTextureIndexForCommand(&Command->Mapping, Texture)) != -1))
    {
        Command = PushCommand(Group, render_command_quads);
        Command->QuadBufferIndex = Commands->QuadIndexCount;
        TextureIndex = GetTextureIndexForCommand(&Command->Mapping, Texture);
    }
    
    range2_r32 Clip = Group->ClipStack[Group->ClipStackUsed];
    
    v2 DestMin = Max(Dest.Min, Clip.Min);
    v2 DestMax = Min(Dest.Max, Clip.Max);
    
    // sixten(TODO): Proper aabb overlap testing here!
    if(InRange(Clip, Dest.Min) || InRange(Clip, Dest.Max))
    {
        v2 HalfSize = DimOfRange(Dest)*0.5;
        
        v2 P00 = V2(DestMin.x, DestMin.y);
        v2 P01 = V2(DestMin.x, DestMax.y);
        v2 P10 = V2(DestMax.x, DestMin.y);
        v2 P11 = V2(DestMax.x, DestMax.y);
        v2 Center = (DestMin + DestMax)*0.5;
        
        v2 TextureSize = V2(Texture.U32[2], Texture.U32[3]);
        
        Source.Min /= TextureSize;
        Source.Max /= TextureSize;
        
        v2 Source00 = Source.Min;
        v2 Source01 = V2(Source.Min.x, Source.Max.y);
        v2 Source10 = V2(Source.Max.x, Source.Min.y);
        v2 Source11 = Source.Max;;
        
        s32 BaseVertex = Commands->QuadVertexCount;
        
        quad_vertex *Vertex = Commands->QuadVertexBase + Commands->QuadVertexCount++;
        Vertex->P = P00;
        Vertex->SourceP = Source00;
        Vertex->TextureIndex = TextureIndex;
        Vertex->Color = PackV4ToU32(Color00);
        Vertex->ToCenter = Center - P00;
        Vertex->HalfSize = HalfSize;
        Vertex->CornerRadius = CornerRadius;
        Vertex->EdgeSoftness = EdgeSoftness;
        Vertex->BorderThickness = BorderThickness;
        
        Vertex = Commands->QuadVertexBase + Commands->QuadVertexCount++;
        Vertex->P = P10;
        Vertex->SourceP = Source10;
        Vertex->TextureIndex = TextureIndex;
        Vertex->Color = PackV4ToU32(Color10);
        Vertex->ToCenter = Center - P10;
        Vertex->HalfSize = HalfSize;
        Vertex->CornerRadius = CornerRadius;
        Vertex->EdgeSoftness = EdgeSoftness;
        Vertex->BorderThickness = BorderThickness;
        
        Vertex = Commands->QuadVertexBase + Commands->QuadVertexCount++;
        Vertex->P = P01;
        Vertex->SourceP = Source01;
        Vertex->TextureIndex = TextureIndex;
        Vertex->Color = PackV4ToU32(Color01);
        Vertex->ToCenter = Center - P01;
        Vertex->HalfSize = HalfSize;
        Vertex->CornerRadius = CornerRadius;
        Vertex->EdgeSoftness = EdgeSoftness;
        Vertex->BorderThickness = BorderThickness;
        
        Vertex = Commands->QuadVertexBase + Commands->QuadVertexCount++;
        Vertex->P = P11;
        Vertex->SourceP = Source11;
        Vertex->TextureIndex = TextureIndex;
        Vertex->Color = PackV4ToU32(Color11);
        Vertex->ToCenter = Center - P11;
        Vertex->HalfSize = HalfSize;
        Vertex->CornerRadius = CornerRadius;
        Vertex->EdgeSoftness = EdgeSoftness;
        Vertex->BorderThickness = BorderThickness;
        
        Commands->QuadIndexBase[Commands->QuadIndexCount++] = BaseVertex + 0;
        Commands->QuadIndexBase[Commands->QuadIndexCount++] = BaseVertex + 3;
        Commands->QuadIndexBase[Commands->QuadIndexCount++] = BaseVertex + 1;
        Commands->QuadIndexBase[Commands->QuadIndexCount++] = BaseVertex + 0;
        Commands->QuadIndexBase[Commands->QuadIndexCount++] = BaseVertex + 2;
        Commands->QuadIndexBase[Commands->QuadIndexCount++] = BaseVertex + 3;
        
        ++Command->QuadCount;
    }
}
#endif

inline void PushQuad(render_group *Group, range2_r32 Dest,
                     v4 Color00, v4 Color01, v4 Color10, v4 Color11,
                     r32 CornerRadius, r32 EdgeSoftness, r32 BorderThickness)
{
    PushTexturedQuad(Group, Dest, Range2R32(V2(0, 0), V2(0, 0)), Color00, Color01, Color10, Color11,
                     CornerRadius, EdgeSoftness, BorderThickness, Group->Commands->WhiteTexture);
}

inline void PushQuad(render_group *Group, range2_r32 Dest,
                     v4 Color,
                     r32 CornerRadius, r32 EdgeSoftness, r32 BorderThickness)
{
    PushTexturedQuad(Group, Dest, Range2R32(V2(0, 0), V2(0, 0)), Color, Color, Color, Color,
                     CornerRadius, EdgeSoftness, BorderThickness, Group->Commands->WhiteTexture);
}

inline void PushClip(render_group *Group, range2_r32 Clip)
{
    Assert(Group->ClipStackUsed + 1 < ArrayCount(Group->ClipStack));
    
#if VN_USE_INSTANCING
    render_command_clip *Command = PushCommand(Group, render_command_clip);
    Command->ClipRect = Group->ClipStack[++Group->ClipStackUsed] = Clip;
#else
    Group->ClipStack[++Group->ClipStackUsed] = Clip;
#endif
}

inline void PopClip(render_group *Group)
{
    Assert(Group->ClipStackUsed > 0);
#if VN_USE_INSTANCING
    render_command_clip *Command = PushCommand(Group, render_command_clip);
    Command->ClipRect = Group->ClipStack[--Group->ClipStackUsed];
#else
    --Group->ClipStackUsed;
#endif
}