#include <v_lib.h>
#include "framebuf.h"

framebuf_p
fbAlloc(u32_t width,u32_t height)
{
	u32_t i;
	u32_t fb_size = width * height * 4 + height * sizeof(u8_p);
	framebuf_p fb = (framebuf_p)memAllocZ(sizeof(framebuf_t) + fb_size);
	if(!fb)
		return NULL;
	fb->orig_width = fb->width = width;
	fb->orig_height = fb->height = height;
	fb->row_offset = width * 4;
	fb->scanlines = (u8_p*)(fb+1);
	fb->scanlines[0] = (u8_p)(fb->scanlines + fb->height);
	for(i=1;i<height;i++)
		fb->scanlines[i] = fb->scanlines[i-1] + fb->row_offset;
	return fb;
}

framebuf_p
fbDuplicate(framebuf_p ref)
{
	framebuf_p fb = fbAlloc(ref->width,ref->height);
	if(!fb)
		return NULL;
	memCopy(fb->scanlines[0],ref->scanlines[0],ref->row_offset * ref->height);
	return fb;
}

framebuf_p
fbLoad(char *image_name)
{
	return fbLoadEx(image_name,0,0,false,false,false);
}

framebuf_p
fbLoadEx(char *image_name,
	u32_t target_width, u32_t target_height,
	vboolean force_powers2,vboolean force_square,vboolean magnify_at_resize)
{
	framebuf_p fb;
	u32_t width, height;
	u8_p pixels;
	char ext[_MAX_EXT];
	fsSplitPath(image_name,NULL,NULL,ext);
	if(!strcasecmp(ext,"bmp"))
		pixels = bmpLoad(image_name,&width,&height);
	else if(!strcasecmp(ext,"jpg"))
		pixels = jpgLoad(image_name,&width,&height);
	else
	{
		if(strcasecmp(ext,"tga"))
			conlPrintf(LOG_WARNING,"fbLoad(%s): unknown format, "
				"assuming TARGA.\n",image_name);
		pixels = tgaLoad(image_name,&width,&height);
	}
	if(!pixels)
		return NULL;
	if(!target_width)
		target_width = width;
	if(!target_height)
		target_height = height;
	if(force_powers2)
	{
		if(magnify_at_resize)
		{
			NOT_LESS_POWER_TWO(target_width,target_width,u32_t);
			NOT_LESS_POWER_TWO(target_height,target_height,u32_t);
		}
		else
		{
			if(!IS_POWER_TWO(target_width))
			{
				NOT_LESS_POWER_TWO(target_width,target_width,u32_t);
				target_width >>= 1;
			}
			if(!IS_POWER_TWO(target_height))
			{
				NOT_LESS_POWER_TWO(target_height,target_height,u32_t);
				target_height >>= 1;
			}
		}
	}
	if(force_square)
	{
		u32_t common;
		if(magnify_at_resize)
		{
			common = (target_width > target_height) ?
				target_width : target_height;
		}
		else
		{
			common = (target_width < target_height) ?
				target_width : target_height;
		}
		target_width = common;
		target_height = common;
	}
	if(width != target_width || height != target_height)
	{
		conlPrintf(LOG_TRACE,"fbLoadEx(): rescale (%ux%u)->(%ux%u)\n",
			width,height,target_width,target_height);
		pixels = imgScale(pixels,
			width,height,target_width,target_height,false);
		if(!pixels)
			return NULL;
	}
	fb = fbAlloc(target_width,target_height);
	if(!fb)
	{
		memFree(pixels);
		return NULL;
	}
	fb->orig_width = width;
	fb->orig_height = height;
	memCopy(fb->scanlines[0],pixels,target_width * target_height * 4);
	memFree(pixels);
	return fb;
}

void
fbClear(framebuf_p fb,vec_t r,vec_t g,vec_t b,vec_t a)
{
	u32_t packed, ub4;
	vec4_t rgba;
	vec4Set(rgba,r,g,b,a);
	rgbaByteFromFloat((u8_p)(&ub4),rgba);
	packed = LE2ME_LONG(ub4);
	memFill4ub(fb->scanlines[0],packed,fb->width * fb->height);
}

void fbReadPixel4ubv(framebuf_p fb,u32_t x,u32_t y,u8_p rgba_ret)
{
	register u8_p ptr;
	if(x > fb->width || y > fb->height)
		return;
	ptr = fb->scanlines[y] + (x << 2);
	*((u32_t*)rgba_ret) = *((u32_t*)ptr);
}

void fbDrawPixel4ubv(framebuf_p fb,u32_t x,u32_t y,u8_p rgba)
{
	register u8_p ptr;
	if(x > fb->width || y > fb->height)
		return;
	ptr = fb->scanlines[y] + (x << 2);
	*((u32_t*)ptr) = *((u32_t*)rgba);
}

void fbReadPixel3v(framebuf_p fb,u32_t x,u32_t y,vec3_t rgb_ret)
{
	register u8_p ptr;
	if(x > fb->width || y > fb->height)
		return;
	ptr = fb->scanlines[y] + (x << 2);
	rgbFromByte(rgb_ret,ptr);
}

void fbReadPixel4v(framebuf_p fb,u32_t x,u32_t y,vec4_t rgba_ret)
{
	register u8_p ptr;
	if(x > fb->width || y > fb->height)
		return;
	ptr = fb->scanlines[y] + (x << 2);
	rgbaFromByte(rgba_ret,ptr);
}

void fbDrawPixel3v(framebuf_p fb,u32_t x,u32_t y,vec3_t rgb)
{
	register u8_p ptr;
	if(x > fb->width || y > fb->height)
		return;
	ptr = fb->scanlines[y] + (x << 2);
	rgbByteFromFloat(ptr,rgb);
	ptr[3] = 255;
}

void fbDrawPixel3(framebuf_p fb,u32_t x,u32_t y,vec_t r,vec_t g,vec_t b)
{
	vec3_t rgb;
	register u8_p ptr;
	rgb[0] = r; rgb[1] = g; rgb[2] = b;
	if(x > fb->width || y > fb->height)
		return;
	ptr = fb->scanlines[y] + (x << 2);
	rgbByteFromFloat(ptr,rgb);
	ptr[3] = 255;
}

void fbDrawPixel4(framebuf_p fb,u32_t x,u32_t y,vec_t r,vec_t g,vec_t b,vec_t a)
{
	vec4_t rgba;
	register u8_p ptr;
	rgba[0] = r; rgba[1] = g; rgba[2] = b; rgba[3] = a;
	if(x > fb->width || y > fb->height)
		return;
	ptr = fb->scanlines[y] + (x << 2);
	rgbaByteFromFloat(ptr,rgba);
}

void fbDrawPixel4v(framebuf_p fb,u32_t x,u32_t y,vec4_t rgba)
{
	register u8_p ptr;
	if(x > fb->width || y > fb->height)
		return;
	ptr = fb->scanlines[y] + (x << 2);
	rgbaByteFromFloat(ptr,rgba);
}

void fbSaveEx(framebuf_p fb,char *image_name,vboolean restore_orig_dims)
{
	u8_p pixels;
	u32_t width, height;
	char ext[_MAX_EXT];
	fsSplitPath(image_name,NULL,NULL,ext);
	if(restore_orig_dims &&
		(fb->width!=fb->orig_width || fb->height!=fb->orig_height))
	{
		conlPrintf(LOG_TRACE,"fbSaveEx(): rescale (%ux%u)->(%ux%u)\n",
			fb->width,fb->height,
			fb->orig_width,fb->orig_height);
		pixels = imgScale(fb->scanlines[0],fb->width,fb->height,
			fb->orig_width,fb->orig_height,true);
		width = fb->orig_width;
		height = fb->orig_height;
	}
	else
	{
		pixels = fb->scanlines[0];
		width = fb->width;
		height = fb->height;
	}
	if(!strcasecmp(ext,"bmp"))
	{
		bmpSave(image_name,width,height,pixels);
	}
	else
	{
		if(strcasecmp(ext,"tga"))
			conlPrintf(LOG_WARNING,"fbSave(%s): unknown extension, "
				"saving as 32-bit TARGA.\n",image_name);
		tgaSaveEx(image_name,width,height,pixels,true);
	}
	if(pixels != fb->scanlines[0])
		memFree(pixels);
}

void fbSave(framebuf_p fb,char *image_name)
{
	fbSaveEx(fb,image_name,true);
}
