#define GLYPH_SUBPIXEL_SEGMENTS 3

global read_only s32 Font_Oversample = 2;

inline s32 F_SubpixelSegmentFromP(r32 Value)
{
	s32 Result = (s32)(Value - Floor(Value))*GLYPH_SUBPIXEL_SEGMENTS;
	return(Result);
}

static void F_RasterizeGlyph(glyph_atlas *Atlas, font_id Font, glyph *Glyph, u32 Codepoint, r32 Size, s32 Subpixel)
{
	Glyph->Font = Font;
	Glyph->Codepoint = Codepoint;
	Glyph->Size = Size;
	Glyph->Subpixel = Subpixel;
	
	Assert(Subpixel < GLYPH_SUBPIXEL_SEGMENTS);
	
	loaded_font *LoadedFont = Atlas->Fonts + Font;
	stbtt_fontinfo *Info = &LoadedFont->Info;
	
	r32 Scale = stbtt_ScaleForMappingEmToPixels(Info, Size);
	
	s32 InternalIndex = (s32)(Glyph - Atlas->Glyphs);
	s32 GlyphsPerRow = Atlas->BitmapSize / Atlas->GlyphSize;
	
	v2_s32 BaseTextureOffset = V2S32((InternalIndex % GlyphsPerRow)*Atlas->GlyphSize,
                                     (InternalIndex / GlyphsPerRow)*Atlas->GlyphSize);
	
	int GlyphIndex = stbtt_FindGlyphIndex(Info, Codepoint);
	
	stbtt_GetGlyphBitmapBoxSubpixel(Info, GlyphIndex, Scale, Scale,
                                    (r32)Subpixel/GLYPH_SUBPIXEL_SEGMENTS, 0,
                                    &Glyph->P0.x, &Glyph->P0.y, &Glyph->P1.x, &Glyph->P1.y);
	
	Fill(Atlas->BitmapBuffer, 0, Atlas->GlyphSize*Atlas->GlyphSize);
	stbtt_MakeGlyphBitmapSubpixel(Info, Atlas->BitmapBuffer,
                                  Atlas->GlyphSize, Atlas->GlyphSize, Atlas->GlyphSize,
                                  Scale, Scale,
                                  (r32)Subpixel/GLYPH_SUBPIXEL_SEGMENTS, 0,
                                  GlyphIndex);
	
	s32 Advance, LeftSideBearing;
	stbtt_GetGlyphHMetrics(Info, GlyphIndex, &Advance, &LeftSideBearing);
	Glyph->Advance = Advance*Scale;
	Glyph->Offset.x = LeftSideBearing*Scale;
	
	Glyph->Offset.y = Glyph->P0.y + (LoadedFont->Ascent + LoadedFont->LineGap)*Scale;
	
	v2_s32 Dim = Glyph->P1 - Glyph->P0;
	
	Glyph->P0 = BaseTextureOffset;
	Glyph->P1 = BaseTextureOffset + Dim + V2S32(2, 2);
	
	Atlas->RenderCommands->FillRegion(Atlas->Texture,
                                      BaseTextureOffset, V2S32(Atlas->GlyphSize, Atlas->GlyphSize),
                                      Atlas->BitmapBuffer);
}

static glyph *F_GlyphFromAtlas(glyph_atlas *Atlas, font_id Font, u32 Codepoint, r32 Size, s32 Subpixel)
{
	glyph *Glyph = 0;
	
	for(s32 GlyphIndex = 0;
        GlyphIndex < Atlas->GlyphsUsed;
        ++GlyphIndex)
	{
		glyph *At = Atlas->Glyphs + GlyphIndex;
		if((At->Font == Font) && (At->Codepoint == Codepoint) && (At->Size == Size) && (At->Subpixel == Subpixel))
		{
			Glyph = At;
			break;
		}
	}
	
	if(Glyph)
	{
		DLLRemove_NP(Atlas->LRUFirst, Atlas->LRULast, Glyph, LRUNext, LRUPrev);
	}
	else
	{
		if(Atlas->GlyphsUsed < Atlas->MaxGlyphCount)
		{
			Glyph = Atlas->Glyphs + Atlas->GlyphsUsed++;
		}
		else
		{
			Glyph = Atlas->LRUFirst;
			Assert(Glyph);
			
			DLLRemove_NP(Atlas->LRUFirst, Atlas->LRULast, Glyph, LRUNext, LRUPrev);
		}
		
		F_RasterizeGlyph(Atlas, Font, Glyph, Codepoint, Size, Subpixel);
	}
	
	DLLInsertLast_NP(Atlas->LRUFirst, Atlas->LRULast, Glyph, LRUNext, LRUPrev);
	
	return(Glyph);
}

static loaded_font F_CreateFontFromFile(arena *Arena, string Filepath)
{
    loaded_font Result = {};
    Result.Data = Platform_ReadEntireFile(Arena, Filepath);
    if(!Result.Data.Count)
    {
        Result.Data = Platform_ReadEntireFile(Arena, StrLit("data/fonts/fallback.ttf"));
        if(!Result.Data.Count)
        {
            temp Scratch = GetScratch(&Arena, 1);
            Platform.ShowMessage(PushFormat(Scratch.Arena, "Unable to fallback font.", Filepath), Platform_Message_Fatal);
            ReleaseScratch(Scratch);
        }
    }
    stbtt_InitFont(&Result.Info, Result.Data.Data, stbtt_GetFontOffsetForIndex(Result.Data.Data, 0));
    stbtt_GetFontVMetrics(&Result.Info, &Result.Ascent, &Result.Descent, &Result.LineGap);
    
    return(Result);
}

static glyph_atlas *F_CreateGlyphAtlas(vn_render_commands *RenderCommands, s32 BitmapSize, s32 GlyphSize)
{
    arena *Arena = ArenaAlloc(Megabytes(1), true, "Glyph Atlas Arena");
    glyph_atlas *Atlas = PushStruct(Arena, glyph_atlas);
    Atlas->Arena = Arena;
    
    Atlas->BitmapSize = BitmapSize;
    Atlas->GlyphSize = GlyphSize;
    
    Atlas->MaxGlyphCount = (BitmapSize / GlyphSize)*(BitmapSize / GlyphSize);
    Atlas->Glyphs = PushArray(Atlas->Arena, glyph, Atlas->MaxGlyphCount);
    
    Atlas->RenderCommands = RenderCommands;
    Atlas->Texture = RenderCommands->AllocateTexture(V2S32(BitmapSize, BitmapSize), Render_TextureFormat_R8, false, 0);
    
    Atlas->Fonts[Font_Regular]          = F_CreateFontFromFile(Atlas->Arena, StrLit("data/fonts/Roboto-Regular.ttf"));
    Atlas->Fonts[Font_Bold]             = F_CreateFontFromFile(Atlas->Arena, StrLit("data/fonts/Roboto-Bold.ttf"));
    Atlas->Fonts[Font_Monospace]        = F_CreateFontFromFile(Atlas->Arena, StrLit("data/fonts/DejaVuSansMono.ttf"));
    Atlas->Fonts[Font_MonospaceOblique] = F_CreateFontFromFile(Atlas->Arena, StrLit("data/fonts/DejaVuSansMono-Oblique.ttf"));
    Atlas->Fonts[Font_Fancy]            = F_CreateFontFromFile(Atlas->Arena, StrLit("data/fonts/Merriweather-Regular.ttf"));
    Atlas->Fonts[Font_Icons]            = F_CreateFontFromFile(Atlas->Arena, StrLit("data/fonts/icons.ttf"));
    
    Atlas->BitmapBuffer = PushArray(Atlas->Arena, u8, GlyphSize*GlyphSize);
    
    return(Atlas);
}

static r32 PushText(render_group *Group, glyph_atlas *Atlas, font_id Font,
                    v2_r32 P, r32 Size, v4_r32 Color,
                    string Text)
{
    r32 OffsetX = 0;
    u8 *TextBegin = Text.Data;
    u8 *TextEnd = TextBegin+Text.Count;
    for(u8 *Byte = TextBegin; Byte < TextEnd;)
    {
        string_decode Decode = DecodeUTF8Codepoint(Byte, TextEnd-Byte);
        Byte += Decode.Size;
        u32 Codepoint = Decode.Codepoint;
        
        glyph *Glyph = F_GlyphFromAtlas(Atlas, Font, Codepoint, Size*Font_Oversample, F_SubpixelSegmentFromP(P.x*Font_Oversample));
        Assert(Glyph);
        
        v2_r32 GlyphP = P + Glyph->Offset*(1.0 / Font_Oversample) + V2R32(OffsetX, 1);
        
        v2_r32 RenderDim = ConvertV2ToR32(Glyph->P1 - Glyph->P0);
        v2_r32 Dim = RenderDim*(1.0 / Font_Oversample);
        
        PushTexturedQuad(Group,
                         Range2R32(GlyphP, GlyphP+Dim),
                         Range2R32(ConvertV2ToR32(Glyph->P0), ConvertV2ToR32(Glyph->P1)),
                         Color, Color, Color, Color, 0, 0, 0, 0, 0, 0, Atlas->Texture);
        
        OffsetX += Glyph->Advance/Font_Oversample;
    }
    return(OffsetX);
}

static void PushTextF(render_group *Group, glyph_atlas *Atlas, font_id Font,
                      v2_r32 P, r32 Size, v4_r32 Color,
                      char *Format, ...)
{
    temp Scratch = GetScratch(0, 0);
    
    va_list Arguments;
    va_start(Arguments, Format);
    string String = PushFormatVariadic(Scratch.Arena, Format, Arguments);
    va_end(Arguments);
    
    PushText(Group, Atlas, Font, P, Size, Color, String);
    
    ReleaseScratch(Scratch);
}

inline r32 CalculateRasterizedTextWidth(glyph_atlas *Atlas, font_id Font, r32 Size, string Text)
{
    r32 X = 0;
    
    u8 *TextBegin = Text.Data;
    u8 *TextEnd = TextBegin+Text.Count;
    for(u8 *Byte = TextBegin; Byte < TextEnd;)
    {
        string_decode Decode = DecodeUTF8Codepoint(Byte, TextEnd-Byte);
        Byte += Decode.Size;
        u32 Codepoint = Decode.Codepoint;
        
        glyph *Glyph = F_GlyphFromAtlas(Atlas, Font, Codepoint, Size*Font_Oversample, F_SubpixelSegmentFromP(X*Font_Oversample));
        Assert(Glyph);
        
        X += Glyph->Advance/Font_Oversample;
    }
    
    return(X);
}

inline r32 CalculateRasterizedTextHeight(glyph_atlas *Atlas, font_id Font, r32 Size, string Text)
{
    r32 Scale = stbtt_ScaleForMappingEmToPixels(&Atlas->Fonts[Font].Info, Size)/
        stbtt_ScaleForPixelHeight(&Atlas->Fonts[Font].Info, Size);
    
    r32 Y = Size*Scale;
    
    u8 *TextBegin = Text.Data;
    u8 *TextEnd = TextBegin+Text.Count;
    for(u8 *Byte = TextBegin; Byte < TextEnd;)
    {
        string_decode Decode = DecodeUTF8Codepoint(Byte, TextEnd-Byte);
        Byte += Decode.Size;
        u32 Codepoint = Decode.Codepoint;
        
        if(Codepoint == '\n')
        {
            Y += Size*Scale;
        }
    }
    return(Y);
}