/* Copyright (c) 2007 Scott Lembcke
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include <stddef.h>
#include <string.h>

#include "sokol/sokol_gfx.h"

// #include "chipmunk/chipmunk_private.h"
// #include "ChipmunkDemo.h"
#include "ChipmunkDemoTextSupport.h"

#include "VeraMoBd.ttf_sdf.h"

//#define Scale 3.0f
#define Scale 0.70f
#define LineHeight (18.0f*Scale)

// char -> glyph indexes generated by the lonesock tool.
static int glyph_indexes[256];

cpTransform ChipmunkDemoTextMatrix;

#define GLSL33(x) "#version 330\n" #x

static sg_bindings bindings;
static sg_pipeline pipeline;

typedef struct {float x, y;} float2;
typedef struct {uint8_t r, g, b, a;} RGBA8;
typedef struct {float2 pos; float2 uv; RGBA8 color;} Vertex;
typedef uint16_t Index;

typedef struct {
		float U_vp_matrix[16];
} Uniforms;

// Meh, just max out 16 bit index size.
#define VERTEX_MAX (64*1024)
#define INDEX_MAX (128*1024)

static sg_buffer VertexBuffer, IndexBuffer;
static size_t VertexCount, IndexCount;

static Vertex Vertexes[VERTEX_MAX];
static uint16_t Indexes[INDEX_MAX];
	
void
ChipmunkDemoTextInit(void)
{
	VertexBuffer = sg_make_buffer(&(sg_buffer_desc){
		.label = "ChipmunkDemoText Vertex Buffer",
		.size = VERTEX_MAX*sizeof(Vertex),
		.type = SG_BUFFERTYPE_VERTEXBUFFER,
		.usage = SG_USAGE_STREAM,
	});
	
	IndexBuffer = sg_make_buffer(&(sg_buffer_desc){
		.label = "ChipmunkDemoText Index Buffer",
		.size = INDEX_MAX*sizeof(Index),
		.type = SG_BUFFERTYPE_INDEXBUFFER,
		.usage = SG_USAGE_STREAM,
	});
	
	sg_image sdf_texture = sg_make_image(&(sg_image_desc){
		.label = "ChipmunkDemoText SDF Atlas",
		.width = sdf_tex_width, .height = sdf_tex_height,
		.pixel_format = SG_PIXELFORMAT_L8,
		.min_filter = SG_FILTER_LINEAR,
		.mag_filter = SG_FILTER_LINEAR,
		.wrap_u = SG_WRAP_CLAMP_TO_EDGE,
		.wrap_v = SG_WRAP_CLAMP_TO_EDGE,
		.content.subimage[0][0] = {.ptr = sdf_data, .size = sizeof(sdf_data)},
	});
	
	bindings = (sg_bindings){
		.vertex_buffers[0] = VertexBuffer,
		.index_buffer = IndexBuffer,
		.fs_images[0] = sdf_texture,
	};
	
	sg_shader shd = sg_make_shader(&(sg_shader_desc){
		.vs.uniform_blocks[0] = {
			.size = sizeof(Uniforms),
			.uniforms[0] = {.name = "U_vp_matrix", .type = SG_UNIFORMTYPE_MAT4},
		},
		.vs.source = GLSL33(
			layout(location = 0) in vec2 IN_pos;
			layout(location = 1) in vec2 IN_uv;
			layout(location = 2) in vec4 IN_color;
			
			uniform mat4 U_vp_matrix;
			
			out struct {
				vec2 uv;
				vec4 color;
			} FRAG;
			
			void main(){
				gl_Position = U_vp_matrix*vec4(IN_pos, 0, 1);
				FRAG.uv = IN_uv;
				FRAG.color = IN_color;
			}
		),
		.fs.images[0] = {.name = "U_texture", .type = SG_IMAGETYPE_2D},
		.fs.source = GLSL33(
			in struct {
				vec2 uv;
				vec4 color;
			} FRAG;
			
			uniform sampler2D U_texture;
			out vec4 OUT_color;
			
			void main(){
				float sdf = texture(U_texture, FRAG.uv).r;
				float fw = 0.5*fwidth(sdf);
				float mask = smoothstep(0.5 - fw, 0.5 + fw, sdf);
				
				OUT_color = FRAG.color*mask;
				OUT_color.a = 0.25*smoothstep(0.0, 0.5, sdf);
			}
		),
	});
	
	pipeline = sg_make_pipeline(&(sg_pipeline_desc){
		.shader = shd,
		.blend = {
			.enabled = true,
			.src_factor_rgb = SG_BLENDFACTOR_ONE,
			.dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA
		},
		.index_type = SG_INDEXTYPE_UINT16,
		.layout = {
			.attrs = {
				[0] = {.offset = offsetof(Vertex, pos), .format = SG_VERTEXFORMAT_FLOAT2},
				[1] = {.offset = offsetof(Vertex, uv), .format = SG_VERTEXFORMAT_FLOAT2},
				[2] = {.offset = offsetof(Vertex, color), .format = SG_VERTEXFORMAT_UBYTE4N},
			}
		}
	});
	
	// Fill in the glyph index array.
	for(int i=0; i<sdf_num_chars; i++){
		int char_index = sdf_spacing[i*8];
		glyph_indexes[char_index] = i;
	}
}

static Vertex *push_vertexes(size_t vcount, const Index *index_src, size_t icount){
	cpAssertHard(VertexCount + vcount <= VERTEX_MAX || IndexCount + icount <= INDEX_MAX, "Geometry buffer full.");
	
	Vertex *vertex_dst = Vertexes + VertexCount;
	size_t base = VertexCount;
	VertexCount += vcount;
	
	Index *index_dst = Indexes + IndexCount;
	for(size_t i = 0; i < icount; i++) index_dst[i] = index_src[i] + (Index)base;
	IndexCount += icount;
	
	return vertex_dst;
}

static float
PushChar(int character, float x, float y)
{
	int i = glyph_indexes[character];
	float w = (float)sdf_tex_width;
	float h = (float)sdf_tex_height;
	
	float gw = (float)sdf_spacing[i*8 + 3];
	float gh = (float)sdf_spacing[i*8 + 4];
	
	float txmin = sdf_spacing[i*8 + 1]/w;
	float tymin = sdf_spacing[i*8 + 2]/h;
	float txmax = txmin + gw/w;
	float tymax = tymin + gh/h;
	
	float s = Scale/scale_factor;
	float xmin = x + sdf_spacing[i*8 + 5]/scale_factor*Scale;
	float ymin = y + (sdf_spacing[i*8 + 6]/scale_factor - gh)*Scale;
	float xmax = xmin + gw*Scale;
	float ymax = ymin + gh*Scale;
	
	RGBA8 color = {0xFD, 0xF6, 0xE3, 0xFF};
	Index indexes[] = {0, 1, 2, 0, 2, 3};
	Vertex *vertexes = push_vertexes(4, indexes, 6);
	vertexes[0] = (Vertex){{xmin, ymin}, {txmin, tymax}, color};
	vertexes[1] = (Vertex){{xmin, ymax}, {txmin, tymin}, color};
	vertexes[2] = (Vertex){{xmax, ymax}, {txmax, tymin}, color};
	vertexes[3] = (Vertex){{xmax, ymin}, {txmax, tymax}, color};
	
	return sdf_spacing[i*8 + 7]*s;
}

#undef ChipmunkDemoTextDrawString

void
ChipmunkDemoTextDrawString(cpVect pos, char const *str)
{
	float x = (float)pos.x, y = (float)pos.y;
	
	for(size_t i=0, len=strlen(str); i<len; i++){
		if(str[i] == '\n'){
			y -= LineHeight;
			x = (float)pos.x;
//		} else if(str[i] == '*'){ // print out the last demo key
//			glutBitmapCharacter(GLUT_BITMAP_HELVETICA_10, 'A' + demoCount - 1);
		} else {
			x += (float)PushChar(str[i], x, y);
		}
	}
}

void
ChipmunkDemoTextFlushRenderer(void)
{
	cpTransform t = ChipmunkDemoTextMatrix;
	Uniforms uniforms = {
		.U_vp_matrix = {(float)t.a , (float)t.b , 0.0f, 0.0f, (float)t.c , (float)t.d , 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, (float)t.tx, (float)t.ty, 0.0f, 1.0f},
	};
	
	sg_update_buffer(VertexBuffer, Vertexes, VertexCount*sizeof(Vertex));
	sg_update_buffer(IndexBuffer, Indexes, IndexCount*sizeof(Index));
	
	sg_apply_pipeline(pipeline);
	sg_apply_bindings(&bindings);
	sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, &uniforms, sizeof(Uniforms));
	sg_draw(0, IndexCount, 1);
	
	VertexCount = 0;
	IndexCount = 0;
}

void
ChipmunkDemoTextClearRenderer(void)
{
	VertexCount = IndexCount = 0;
}


static size_t PushedVertexCount, PushedIndexCount;

void
ChipmunkDemoTextPushRenderer(void)
{
	PushedVertexCount = VertexCount;
	PushedIndexCount = IndexCount;
}

void
ChipmunkDemoTextPopRenderer(void)
{
	VertexCount = PushedVertexCount;
	IndexCount = PushedIndexCount;
}
