ftlib : FT_Library;

ATLAS_SIZE :: 4096;

window_proj : Matrix4;

vertex_p2_st2_col4 :: struct {
    pos : Vector2;
    st  : Vector2;
    col : Vector4;
}

Glyph :: struct {
    utf32 : u32;
    index : u32;

    x, y : s16;
    width, height : s16;
    rwidth, rheight : s16;

    bearing_x, bearing_y : s16;

    y_max, y_min : s16;

    ascent : s16;
    descent : s16;
    advance : s16;

    st0, st1 : Vector2;

    bitmap : *u8;
}

Font :: struct {
    face : FT_Face;

    glyphs : Table(u32, Glyph);

    kb : kbts_font;

    texture : Texture;
}

init_font :: (using font: *Font, filename: string, size: s32) {

    pixel_size := size * 96.0 / 72.0;

    font_data, success := read_entire_file(filename);

    if !success {
        log("Could not read the font file.");
    }

    
    error := FT_New_Memory_Face(ftlib, font_data.data, xx font_data.count, 0, *face);
    if error {
        log("%", get_working_directory());
        log("FT_New_Face failed with error: %", to_string(FT_Error_String(error)));
        return;
    }

    error = FT_Set_Char_Size(face, 0, size * 64, 0, 96);
    if error {
        log("%", to_string(FT_Error_String(error)));
    }

    rects : [..]stbrp_rect;
    defer array_free(rects);

    nodes : [ATLAS_SIZE] stbrp_node;

    stbrpcontext : stbrp_context;
    stbrp_init_target(*stbrpcontext, ATLAS_SIZE, ATLAS_SIZE, nodes.data, nodes.count);

    atlas : *u8 = alloc(ATLAS_SIZE * ATLAS_SIZE);
    defer free(atlas);

    charcode : FT_ULong;
    gindex   : FT_UInt;

    i : int;

    charcode = FT_Get_First_Char(face, *gindex);
    while gindex != 0 {
        error = FT_Load_Glyph(face, gindex, FT_LOAD_RENDER);
        if error
            log("%", to_string(FT_Error_String(error)));

        array_add(*rects, stbrp_rect.{cast(s32) face.glyph.glyph_index, xx (face.glyph.bitmap.width + 2),
                    xx (face.glyph.bitmap.rows + 2), 0, 0, 0});

        glyph : Glyph;
        glyph.utf32 = xx charcode;
        glyph.index = gindex;
        glyph.bearing_x = cast,trunc(s16) face.glyph.bitmap_left;
        glyph.bearing_y = cast,trunc(s16) face.glyph.bitmap_top;
        glyph.width = xx face.glyph.bitmap.width;
        glyph.height = xx face.glyph.bitmap.rows;
        glyph.rwidth = xx face.glyph.metrics.width >> 6;
        glyph.rheight = xx face.glyph.metrics.height >> 6;
        glyph.descent = xx (cast(float) face.glyph.bitmap.rows - cast(float) face.glyph.bitmap_top);
        glyph.ascent  = xx (cast(float) face.glyph.bitmap.rows - glyph.descent);
        glyph.advance = cast(s16) face.glyph.advance.x >> 6;
        glyph.bitmap = alloc(glyph.height * glyph.width);
        memcpy(glyph.bitmap, face.glyph.bitmap.buffer, glyph.height * glyph.width);

        table_set(*font.glyphs, glyph.index, glyph);

        charcode = FT_Get_Next_Char(face, charcode, *gindex);
        i += 1;
    }

    stbrp_pack_rects(*stbrpcontext, rects.data, cast(s32) rects.count);


    copybitmaptoatlas :: (bitmap : *u8, width: s32, height: s32, atlas: *u8, destx: s32, desty: s32) {
        if (destx >= ATLAS_SIZE)
            destx = ATLAS_SIZE - 1;

        if (desty >= ATLAS_SIZE)
            desty = ATLAS_SIZE - 1;

        for j: 0..height - 1 {
            for i: 0..width - 1 {
                atlas[(desty + j) * ATLAS_SIZE + (destx + i)] = bitmap[j * width + i];
            }
        }
    }

    flip :: (atlas: *u8) {
        for j : 0..ATLAS_SIZE / 2 - 1 {
            for i : 0..ATLAS_SIZE - 1 {
                byte := atlas[j * ATLAS_SIZE + i];
                atlas[j * ATLAS_SIZE + i] = atlas[(ATLAS_SIZE - j - 1) * ATLAS_SIZE + i];
                atlas[(ATLAS_SIZE - j - 1) * ATLAS_SIZE + i] = byte;
                
            }
        }
    }

    for rect : rects {
        glyph : *Glyph = table_find_pointer(*font.glyphs, cast(u32) rect.id);
        if (glyph.bitmap) {
            glyph.x = cast,trunc(s16, rect.x);
            glyph.y = cast,trunc(s16, rect.y) + xx glyph.height;
            glyph.st0 = .{(cast(float, glyph.x + 1)) / cast(float, ATLAS_SIZE), cast(float, glyph.y + 1) / cast(float, ATLAS_SIZE)};
            glyph.st1 = .{(cast(float, glyph.x + glyph.width + 1)) / cast(float, ATLAS_SIZE), (cast(float, glyph.y - glyph.height + 1)) / cast(float, ATLAS_SIZE)};
            copybitmaptoatlas(glyph.bitmap, cast(s32) glyph.width, cast(s32) glyph.height, atlas, xx (rect.x + 1), xx (rect.y + 1));
        }
    }

    flip(atlas);

    texture = make_texture_from_data(atlas, ATLAS_SIZE, ATLAS_SIZE);

    ScratchSize : u64 = kbts_ReadFontHeader(*font.kb, font_data.data, xx font_data.count);
    PermanentMemorySize : u64 = kbts_ReadFontData(*font.kb, alloc(xx ScratchSize), ScratchSize);
    kbts_PostReadFontInitialize(*font.kb, alloc(xx PermanentMemorySize), PermanentMemorySize);
    if !kbts_FontIsValid(*font.kb) {
        log("[Error] [kb_text_shape] failed to load the font.");
    }
}

render_text :: (font: *Font, text: []u32, pos: Vector2, size: Vector2, window_space: bool = true, colour: Vector4 = .{1.0, 1.0, 1.0, 1.0},
                  background: Vector4 = .{0.0, 0.0, 0.0, 0.0}, nice_background := false, count_descent: bool = false) {

    if !text
        return;

    // Backup GL state
    // opengl_state : OpenGLState;
    // save_opengl_state(*opengl_state);

    glEnable(GL_BLEND);
    glDisable(GL_MULTISAMPLE);

    if nice_background {
        render_text(font, text, pos + .{3.0, -3.0}, size, window_space, v4(v3(0.0), 1.0), v4(0.0), false);
    }

    vertices : [..]vertex_p2_st2_col4;
    defer array_free(vertices);

    render_pos := pos;

    Cursor : kbts_cursor;
    Direction : kbts_direction = .KBTS_DIRECTION_NONE;
    Script : kbts_script = .KBTS_SCRIPT_DONT_KNOW;

    Glyphs : *kbts_glyph = cast(*kbts_glyph, alloc(size_of(kbts_glyph) * text.count));

    CodepointIndex : s32;
    while CodepointIndex < xx text.count {
        Glyphs[CodepointIndex] = kbts_CodepointToGlyph(*font.kb, text.data[CodepointIndex]);

        CodepointIndex += 1;
    }

    State : *kbts_shape_state = kbts_CreateShapeState(*font.kb);
    Config : kbts_shape_config = kbts_ShapeConfig(*font.kb, Script, .DONT_KNOW);

    GlyphCount : u32 = xx text.count;
    GlyphCapacity : u32 = GlyphCount;
    while kbts_Shape(State, *Config, Direction, Direction, Glyphs, *GlyphCount, GlyphCapacity) {
        Glyphs = cast(*kbts_glyph, realloc(Glyphs, size_of(kbts_glyph) * State.RequiredGlyphCapacity, size_of(kbts_glyph) * GlyphCapacity));
        GlyphCapacity = State.RequiredGlyphCapacity;
    }

    x_offset : float;
    y_offset : float;

    GlyphIndex : u64;
    while GlyphIndex < GlyphCount {
        kglyph : *kbts_glyph = *Glyphs[GlyphIndex];

        X, Y : s32;
        kbts_PositionGlyph(*Cursor, kglyph, *X, *Y);

        glyph : *Glyph = table_find_pointer(*font.glyphs, kglyph.Id);

        if !glyph {
            GlyphIndex += 1;
            continue;
        }

        v0 : Vector2;
        v1 : Vector2;
        if count_descent {
            v0 = render_pos + .{cast(float) x_offset,// + glyph.bearing_x,
                                cast(float) y_offset - glyph.bearing_y + size.y /*- max_descent*/};
        } else {
            v0 = render_pos + .{cast(float) x_offset,// + glyph.bearing_x,
                                cast(float) y_offset - (xx glyph.height - glyph.bearing_y)/* - glyph.height + draw_size.y*/};
        }

        v1 = v0 + Vector2.{cast(float) glyph.width, cast(float) glyph.height};
        x_offset += xx (FT_MulFix(Cursor.X - X, font.face.size.metrics.x_scale) >> 6);
        y_offset += xx (FT_MulFix(Cursor.Y - Y, font.face.size.metrics.y_scale) >> 6);

        // #if Y_IS_UP {
            // t0 := Vector2.{cast(float, glyph.x) / cast(float, ATLAS_SIZE), cast(float,  glyph.y) / cast(float, ATLAS_SIZE)};
            // t1 := t0 + Vector2.{cast(float, glyph.width) / cast(float, ATLAS_SIZE), -cast(float, glyph.height) / cast(float, ATLAS_SIZE)};
            t0 := glyph.st0;
            t1 := glyph.st1;
        // } else {
        //     t0 := Vector2.{cast(float, glyph.x / ATLAS_SIZE), cast(float, glyph.y / ATLAS_SIZE)};
        //     t1 := t0 + .{cast(float, glyph.width / ATLAS_SIZE), cast(float, glyph.height / ATLAS_SIZE)};
        // }

        array_add(*vertices, .{v0, t0, colour});
        array_add(*vertices, .{.{v0.x, v1.y}, .{t0.x, t1.y}, colour});
        array_add(*vertices, .{.{v1.x, v0.y}, .{t1.x, t0.y}, colour});

        array_add(*vertices, .{.{v1.x, v0.y}, .{t1.x, t0.y}, colour});
        array_add(*vertices, .{.{v0.x, v1.y}, .{t0.x, t1.y}, colour});
        array_add(*vertices, .{v1, t1, colour});

        GlyphIndex += 1;
    }

    free(Glyphs);


    view := identity_of(Matrix4);
    proj := window_proj;

    render_batch(.TRIANGLES, vertices, *text_shader, *font.texture, view, proj);

    // Restore modified GL state
    // restore_opengl_state(*opengl_state);
}

ShapeText :: (font: *Font, Cursor: *kbts_cursor, Codepoints: *u32, CodepointCount: u64, MainDirection: kbts_direction, Direction: kbts_direction, Script: kbts_script) -> Vector2, float {
    kglyphs : *kbts_glyph = cast(*kbts_glyph, alloc(xx (size_of(kbts_glyph) * CodepointCount)));

    CodepointIndex: u64;
    while CodepointIndex < CodepointCount {
        kglyphs[CodepointIndex] = kbts_CodepointToGlyph(*font.kb, Codepoints[CodepointIndex]);

        CodepointIndex += 1;
    }

    State : *kbts_shape_state = kbts_CreateShapeState(*font.kb);
    Config : kbts_shape_config = kbts_ShapeConfig(*font.kb, Script, .KBTS_LANGUAGE_DONT_KNOW);

    size : Vector2;
    max_descent : float;

    GlyphCount : u32 = xx CodepointCount;
    GlyphCapacity : u32 = GlyphCount;
    while kbts_Shape(State, *Config, MainDirection, Direction, kglyphs, *GlyphCount, GlyphCapacity) {
        kglyphs = cast(*kbts_glyph, realloc(kglyphs, size_of(kbts_glyph) * State.RequiredGlyphCapacity, size_of(kbts_glyph) * GlyphCapacity));
        GlyphCapacity = State.RequiredGlyphCapacity;
    }

    GlyphIndex : u64;
    while GlyphIndex < GlyphCount {
        kglyph : *kbts_glyph = *kglyphs[GlyphIndex];

        X : s32;
        Y : s32;
        kbts_PositionGlyph(Cursor, kglyph, *X, *Y);

        glyph : *Glyph = table_find_pointer(*font.glyphs, kglyph.Id);
        if glyph {
            size.y = max(size.y, xx glyph.height);
            size.x += xx (FT_MulFix(kglyph.AdvanceX, font.face.size.metrics.x_scale) >> 6);
            max_descent = max(max_descent, xx glyph.descent);
        }

        GlyphIndex += 1;
    }

    free(kglyphs);

    return size, max_descent;
}

next_segment_size :: (font : *Font, text : []u32, n : s32, max_width : float = 0.0, segment_type : kbts_break_flags) -> Vector2, float, s32, kbts_break_flags {
    textp : *u32 = text.data;
    Codepoints : *u32 = textp + n;
    CodepointCount : u64 = xx (text.count - (Codepoints - textp));

    Cursor : kbts_cursor;
    Direction : kbts_direction = .NONE;
    Script : kbts_script = .DONT_KNOW;
    RunStart : u64 = 0;
    BreakState : kbts_break_state;
    kbts_BeginBreak(*BreakState, .NONE, .NORMAL);

    CodepointIndex : u64;
    while CodepointIndex < xx CodepointCount {
        kbts_BreakAddCodepoint(*BreakState, Codepoints[CodepointIndex], 1, xx ((CodepointIndex + 1) == xx CodepointCount));
        Break : kbts_break;
        while kbts_Break(*BreakState, *Break) {

            if Break.Flags & .DIRECTION {
                Direction = Break.Direction;
                if(!Cursor.Direction) Cursor = kbts_Cursor(BreakState.MainDirection);
            }

            if Break.Flags & .SCRIPT {
                Script = Break.Script;
            }

            if (Break.Position > RunStart) && (Break.Flags & segment_type) {
                RunLength : u64 = Break.Position - RunStart;
                size, max_descent := ShapeText(font, *Cursor, Codepoints + RunStart, RunLength, BreakState.MainDirection, Direction, Script);

                return size, max_descent, xx Break.Position, Break.Flags;
            }
        }

        CodepointIndex += 1;
    }

    return .{}, 0, 0, xx 0;
}

next_word_size :: (font : *Font, text : []u32, n : s32, max_width : float = 0.0) -> Vector2, float, s32 {
    textp : *u32 = text.data;
    Codepoints : *u32 = textp + n;
    CodepointCount : u64 = xx (text.count - (Codepoints - textp));

    Cursor : kbts_cursor;
    Direction : kbts_direction = .NONE;
    Script : kbts_script = .DONT_KNOW;
    RunStart : u64 = 0;
    BreakState : kbts_break_state;
    kbts_BeginBreak(*BreakState, .NONE, .NORMAL);

    CodepointIndex : u64;
    while CodepointIndex < xx CodepointCount {
        kbts_BreakAddCodepoint(*BreakState, Codepoints[CodepointIndex], 1, xx ((CodepointIndex + 1) == xx CodepointCount));
        Break : kbts_break;
        while kbts_Break(*BreakState, *Break) {

            if Break.Flags & .DIRECTION {
                Direction = Break.Direction;
                if(!Cursor.Direction) Cursor = kbts_Cursor(BreakState.MainDirection);
            }

            if Break.Flags & .SCRIPT {
                Script = Break.Script;
            }

            if (Break.Position > RunStart) && (Break.Flags & .WORD) {
                RunLength : u64 = Break.Position - RunStart;
                size, max_descent := ShapeText(font, *Cursor, Codepoints + RunStart, RunLength, BreakState.MainDirection, Direction, Script);

                return size, max_descent, xx Break.Position;
            }
        }

        CodepointIndex += 1;
    }

    return .{}, 0, 0;
}

next_grapheme_size :: (font: *Font, text: []u32, n: s32, max_width: float = 0.0) -> Vector2, float, s32 {

    textp : *u32 = text.data;
    Codepoints : *u32 = textp + n;
    CodepointCount : u64 = xx (text.count - (Codepoints - textp));

    Cursor : kbts_cursor;
    Direction : kbts_direction = .NONE;
    Script : kbts_script = .DONT_KNOW;
    RunStart : u64 = 0;
    BreakState : kbts_break_state;
    kbts_BeginBreak(*BreakState, .NONE, .NORMAL);

    CodepointIndex : u64;
    while CodepointIndex < xx CodepointCount {
        kbts_BreakAddCodepoint(*BreakState, Codepoints[CodepointIndex], 1, xx ((CodepointIndex + 1) == xx CodepointCount));
        Break : kbts_break;
        while kbts_Break(*BreakState, *Break) {

            if Break.Flags & .DIRECTION {
                Direction = Break.Direction;
                if(!Cursor.Direction) Cursor = kbts_Cursor(BreakState.MainDirection);
            }

            if Break.Flags & .SCRIPT {
                Script = Break.Script;
            }

            if (Break.Position > RunStart) && (Break.Flags & .GRAPHEME) {
                RunLength : u64 = Break.Position - RunStart;
                size, max_descent := ShapeText(font, *Cursor, Codepoints + RunStart, RunLength, BreakState.MainDirection, Direction, Script);

                return size, max_descent, xx Break.Position;
            }
        }

        CodepointIndex += 1;
    }

    return .{}, 0, 0;
}

next_line_size :: (font: *Font, text: []u32, n: s32, max_width: float = 0.0) -> Vector2, float, s32 {
    textp : *u32 = text.data;
    Codepoints : *u32 = textp + n;
    CodepointCount : u64 = xx (text.count - (Codepoints - textp));

    Cursor : kbts_cursor;
    Direction : kbts_direction = .NONE;
    Script : kbts_script = .DONT_KNOW;
    RunStart : u64 = 0;
    BreakState : kbts_break_state;
    kbts_BeginBreak(*BreakState, .NONE, .NORMAL);

    CodepointIndex : u64;
    while CodepointIndex < xx CodepointCount {
        kbts_BreakAddCodepoint(*BreakState, Codepoints[CodepointIndex], 1, xx ((CodepointIndex + 1) == xx CodepointCount));
        Break : kbts_break;
        while kbts_Break(*BreakState, *Break) {

            if Break.Flags & .DIRECTION {
                Direction = Break.Direction;
                if(!Cursor.Direction) Cursor = kbts_Cursor(BreakState.MainDirection);
            }

            if Break.Flags & .SCRIPT {
                Script = Break.Script;
            }

            if (Break.Position > RunStart) && (Break.Flags & .LINE_HARD) {
                RunLength : u64 = Break.Position - RunStart;
                size, max_descent := ShapeText(font, *Cursor, Codepoints + RunStart, RunLength, BreakState.MainDirection, Direction, Script);

                return size, max_descent, xx Break.Position;
            }
        }

        CodepointIndex += 1;
    }

    return .{}, 0, 0;
}

make_texture_from_data :: (data: *u8, width: s32, height: s32) -> Texture {
    if !data {
        log("[Error] Could not load the texture.");
        return .{};
    }

    texture : Texture;

    texture.internal_format = GL_R8;
    texture.format = GL_RED;

    glGenTextures(1, *texture.id);
    glBindTexture(GL_TEXTURE_2D, texture.id);
    glTexImage2D(GL_TEXTURE_2D, 0, texture.internal_format, xx width, xx height, 0, texture.format, GL_UNSIGNED_BYTE, data);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

    texture.width = width;
    texture.height = height;
    texture.channels = 1;
    texture.srgb = true;

    return texture;
}

PRIMITIVE_TYPE :: enum u8 {
    NONE;
    TRIANGLE_STRIP :: 4;
    TRIANGLES :: 6;
}

render_batch :: (primitive_type: PRIMITIVE_TYPE, vertices: []$T, shader: *_Shader, texture: *Texture, view: Matrix4, proj: Matrix4) {

    if !vertices {
        //log("[Error] The batch has no vertices.");
        return;
    }

    use(shader);

    if texture {
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texture.id);
    }

    vao : GLuint;
    glGenVertexArrays(1, *vao);
    glBindVertexArray(vao);

    vbo : GLuint;
    glGenBuffers(1, *vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, cast(*Type_Info, T).runtime_size * vertices.count, vertices.data, GL_STATIC_DRAW);

    if T == {
        // case vertex_p2_st2_col4_a1;
        //     glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, size_of(vertex_p2_st2_col4_a1), xx offset_of(vertex_p2_st2_col4_a1, #code pos));
        //     glEnableVertexAttribArray(0);

        //     glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, size_of(vertex_p2_st2_col4_a1), xx offset_of(vertex_p2_st2_col4_a1, #code st));
        //     glEnableVertexAttribArray(1);

        //     glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, size_of(vertex_p2_st2_col4_a1), xx offset_of(vertex_p2_st2_col4_a1, #code col));
        //     glEnableVertexAttribArray(2);

        //     glVertexAttribPointer(3, 1, GL_FLOAT, GL_FALSE, size_of(vertex_p2_st2_col4_a1), xx offset_of(vertex_p2_st2_col4_a1, #code alpha));
        //     glEnableVertexAttribArray(3);

        // case vertex_p2_scale2_rot1_st2;
        //     glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, size_of(vertex_p2_scale2_rot1_st2), xx offset_of(vertex_p2_scale2_rot1_st2, #code pos));
        //     glEnableVertexAttribArray(0);

        //     glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, size_of(vertex_p2_scale2_rot1_st2), xx offset_of(vertex_p2_scale2_rot1_st2, #code scale));
        //     glEnableVertexAttribArray(1);

        //     glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, size_of(vertex_p2_scale2_rot1_st2), xx offset_of(vertex_p2_scale2_rot1_st2, #code rot));
        //     glEnableVertexAttribArray(2);

        //     glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, size_of(vertex_p2_scale2_rot1_st2), xx offset_of(vertex_p2_scale2_rot1_st2, #code st));
        //     glEnableVertexAttribArray(3);

        case vertex_p2_st2_col4;
            glEnableVertexAttribArray(0);
            glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, size_of(vertex_p2_st2_col4), xx (offset_of(vertex_p2_st2_col4, #code pos)));

            glEnableVertexAttribArray(1);
            glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, size_of(vertex_p2_st2_col4), xx (offset_of(vertex_p2_st2_col4, #code st)));

            glEnableVertexAttribArray(2);
            glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, size_of(vertex_p2_st2_col4), xx (offset_of(vertex_p2_st2_col4, #code col)));

        // case vertex_p2_col4;
        //     glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, size_of(vertex_p2_col4), xx offset_of(vertex_p2_col4, #code pos));
        //     glEnableVertexAttribArray(0);

        //     glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, size_of(vertex_p2_col4), xx offset_of(vertex_p2_col4, #code col));
        //     glEnableVertexAttribArray(1);
    }

    glUniformMatrix4fv(glGetUniformLocation(shader.id, "uni_view"), 1, GL_TRUE, xx *view);
    glUniformMatrix4fv(glGetUniformLocation(shader.id, "uni_proj"), 1, GL_TRUE, xx *proj);

    if primitive_type == {
        case .TRIANGLE_STRIP;
            glDrawArrays(GL_TRIANGLE_STRIP, 0, xx vertices.count);

        case .TRIANGLES;
            glDrawArrays(GL_TRIANGLES, 0, xx vertices.count);
    }

    glBindVertexArray(0);

    glDeleteBuffers(1, *vbo);
    glDeleteVertexArrays(1, *vao);
}
