/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */


		#include "binding_helpers.h"
		#include "ui/ui.h"
		#include "font/font_renderer.h"
		#include "picture/pict_io.h"

		using namespace GS;
		using namespace GS::Script;

		void		IterateTextParameters(HSQUIRRELVM vm, int idx, TextState &state);


//------------------------------------------------------------------------------
SQInteger			NewPicture(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETINT(w)
	__SQ_GETINT(h)
	__SQ_GETEND
	Picture *p = new Picture;
	if	(!p || !p->AllocAs(w, h))
		return sq_throwerror(vm, String::Format("Failed to allocate a new %dx%d picture.", w, h));
	__SQ_RETURNSAFEPTR(p, typetag_Picture)
}
SQInteger			LoadPicture(HSQUIRRELVM vm)
{
	__SQ_GETSTART(1)
	__SQ_GETSTRING(path)
	Picture *p = new Picture;
	if	(!p)
		return sq_throwerror(vm, "Failed to allocate picture.");
	if	(!PictureIO::Get().Load(*p, path))
		return sq_throwerror(vm, String::Format("Failed to load picture '%s'.", path));
	__SQ_GETEND
	__SQ_RETURNSAFEPTR(p, typetag_Picture)
}
SQInteger			PictureClone(HSQUIRRELVM vm)
{
	__SQ_GETSINGLESAFEPTR(p, Picture, typetag_Picture)
	__SQ_RETURNSAFEPTR(new Picture(*p), typetag_Picture);
}
//------------------------------------------------------------------------------

//-----------------------------------------------------------------------------
SQInteger			PictureWriteText(HSQUIRRELVM vm)
{
	__SQ_GETSTART(5)
	__SQ_GETSAFEPTR(p, Picture, typetag_Picture)
	__SQ_GETRECT(out_rect)
	__SQ_GETSTRING(text)
	__SQ_GETSAFEPTR(font, FontEx, typetag_Font)

	TextState state;
	IterateTextParameters(vm, -1, state);
	__SQ_GETUPDATESTACK

	state.font = font;

	iRect clip_rect = p->GetRect();
	FontRenderer::Format(text, state, out_rect);
	FontRenderer::Compose(*p, text, state, out_rect, clip_rect);

	__SQ_GETEND
	__SQ_RETURN
}
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
SQInteger			PictureApplyConvolution(HSQUIRRELVM vm)
{
	Picture *pic;
	if	(!CObject::Get(vm, -6, (void **)&pic, typetag_Picture))
		return -1;
	if	(!pic)
		return sq_throwerror(vm, "Invalid picture.");

	// Kernel size.
	SQInteger kw, kh;
	sq_getinteger(vm, -5, &kw);
	sq_getinteger(vm, -4, &kh);

	int	kernel[1024];
	if	((kw * kh) > 1024)
		return sq_throwerror(vm, "The convolution kernel is exceeding 1024 entries.");

	sq_pushnull(vm);	// iterator
	for	(int n = 0; n < (kw * kh); ++n)
	{
		if	(SQ_FAILED(sq_next(vm, -4)))
			break;

		// Here -1 is the value and -2 is the key.
		SQInteger v;
		sq_getinteger(vm, -1, &v);
		kernel[n] = v;
		sq_pop(vm, 2);	// Pops key and val before the next iteration.
	}
	sq_pop(vm, 1);		// Pop the iterator.

	SQFloat weight;
	sq_getfloat(vm, -2, &weight);
	SQInteger pass;
	sq_getinteger(vm, -1, &pass);

	sq_pop(vm, 4);		// Pop function arguments.

	if	(!pic->ApplyConvolution(kw, kh, kernel, int(weight * 256), pass))
		return sq_throwerror(vm, "Convolution filter failed.");

	return 0;
}
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
SQInteger			PictureLine(HSQUIRRELVM vm)
{
	__SQ_GETSTART(6)
	__SQ_GETSAFEPTR(pic, Picture, typetag_Picture)
	__SQ_GETFLOAT(sx)
	__SQ_GETFLOAT(sy)
	__SQ_GETFLOAT(ex)
	__SQ_GETFLOAT(ey)
	__SQ_GETVECTORW(c)
	__SQ_GETEND

	fRect rect = pic->GetRect().AsFloat();
	rect.ex -= 2; rect.ey -= 2;
	if ((rect.ex <= rect.sx) || (rect.ey < rect.sy))
		return 0;
	pic->DrawLineHQ(sx, sy, ex, ey, c.x, c.y, c.z, c.w, &rect);
	return 0;
}
//-----------------------------------------------------------------------------

//------------------------------------------------------------------------------
SQInteger			PictureLoadContent(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(pic, Picture, typetag_Picture)
	__SQ_GETSTRING(path)
	bool r = PictureIO::Get().Load(*pic, path);
	__SQ_GETEND
	__SQ_RETURNBOOL(r)
}
SQInteger			PictureSaveTGA(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(p, Picture, typetag_Picture)
	__SQ_GETSTRING(path)
	if	(!PictureIO::Get().TgaSave(*p, path))
		return sq_throwerror(vm, String::Format("Failed to save picture to '%s'", path));
	__SQ_GETEND
	__SQ_RETURN
}
SQInteger			PictureSaveJPG(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(p, Picture, typetag_Picture)
	__SQ_GETSTRING(path)
	p->Convert(PixelFormat::RGB8);
	if	(!PictureIO::Get().Save(*p, path, "IJG"))
		return sq_throwerror(vm, String::Format("Failed to save picture to '%s'", path));
	__SQ_GETEND
	__SQ_RETURN
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
SQInteger			PictureGetRect(HSQUIRRELVM vm)
{
	__SQ_GETSINGLESAFEPTR(p, Picture, typetag_Picture)
	iRect rect(0, 0, 0, 0);
	if	(p)	rect.Set(0, 0, p->GetWidth(), p->GetHeight());
	__SQ_RETURNRECT(rect)
}
SQInteger			PictureAlloc(HSQUIRRELVM vm)
{
	__SQ_GETSTART(3)
	__SQ_GETSAFEPTR(p, Picture, typetag_Picture)
	__SQ_GETINT(w)
	__SQ_GETINT(h)
	__SQ_GETEND
	if	(!p->AllocAs(w, h))
		return sq_throwerror(vm, "Failed to allocate picture buffer");
	__SQ_RETURN
}
SQInteger			PictureResize(HSQUIRRELVM vm)
{
	__SQ_GETSTART(3)
	__SQ_GETSAFEPTR(p, Picture, typetag_Picture)
	__SQ_GETINT(w)
	__SQ_GETINT(h)
	__SQ_GETEND
	p->Resize(w, h);
	__SQ_RETURN
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
SQInteger			PictureSetPixel(HSQUIRRELVM vm)
{
	__SQ_GETSTART(4)
	__SQ_GETSAFEPTR(p, Picture, typetag_Picture)
	__SQ_GETFLOAT(x)
	__SQ_GETFLOAT(y)
	__SQ_GETVECTORW(c)
	__SQ_GETEND
	p->DrawPlot(x, y, c.x, c.y, c.z, c.w);
	__SQ_RETURN
}
SQInteger			PictureGetPixel(HSQUIRRELVM vm)
{
	__SQ_GETSTART(3)
	__SQ_GETSAFEPTR(p, Picture, typetag_Picture)
	__SQ_GETFLOAT(x)
	__SQ_GETFLOAT(y)
	__SQ_GETEND
	Color c;
	p->Sample(x / p->GetWidth(), y / p->GetHeight(), c);
	__SQ_RETURNVECTORW(Vector4(c.x, c.y, c.z, c.w))
}
SQInteger			PictureFlip(HSQUIRRELVM vm)
{
	__SQ_GETSTART(3)
	__SQ_GETSAFEPTR(p, Picture, typetag_Picture)
	__SQ_GETBOOL(h)
	__SQ_GETBOOL(v)
	__SQ_GETEND
	p->Flip(asbool(h), asbool(v));
	__SQ_RETURN
}
SQInteger			PictureFillLockAlpha(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(p, Picture, typetag_Picture)
	__SQ_GETVECTORW(c)
	__SQ_GETEND
	p->Fill(c.x, c.y, c.z, c.w, 0, true);
	__SQ_RETURN
}
SQInteger			PictureFill(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(p, Picture, typetag_Picture)
	__SQ_GETVECTORW(c)
	__SQ_GETEND
	p->Fill(c.x, c.y, c.z, c.w);
	__SQ_RETURN
}
SQInteger			PictureFillRect(HSQUIRRELVM vm)
{
	__SQ_GETSTART(3)
	__SQ_GETSAFEPTR(p, Picture, typetag_Picture)
	__SQ_GETVECTORW(c)
	__SQ_GETRECT(clip_rect)
	__SQ_GETEND
	p->Fill(c.x, c.y, c.z, c.w, &clip_rect);
	__SQ_RETURN
}
SQInteger			PictureBlitRect(HSQUIRRELVM vm)
{
	__SQ_GETSTART(5)
	__SQ_GETSAFEPTR(src, Picture, typetag_Picture)
	__SQ_GETSAFEPTR(dst, Picture, typetag_Picture)
	__SQ_GETRECT(src_rect)
	__SQ_GETRECT(dst_rect)
	__SQ_GETINT(blend_mode)
	__SQ_GETEND
	Picture::Blit(*src, *dst, &src_rect, &dst_rect, (Picture::BlendMode)blend_mode);
	__SQ_RETURN
}
SQInteger			PictureBlitRectMasked(HSQUIRRELVM vm)
{
	__SQ_GETSTART(5)
	__SQ_GETSAFEPTR(src, Picture, typetag_Picture)
	__SQ_GETSAFEPTR(dst, Picture, typetag_Picture)
	__SQ_GETSAFEPTR(msk, Picture, typetag_Picture)
	__SQ_GETRECT(src_rect)
	__SQ_GETRECT(dst_rect)
	__SQ_GETEND
	Picture::BlitMask(*src, *dst, *msk, &src_rect, &dst_rect);
	__SQ_RETURN
}
SQInteger			PictureBlit(HSQUIRRELVM vm)
{
	__SQ_GETSTART(3)
	__SQ_GETSAFEPTR(src, Picture, typetag_Picture)
	__SQ_GETSAFEPTR(dst, Picture, typetag_Picture)
	__SQ_GETINT(blend_mode)
	__SQ_GETEND
	Picture::Blit(*src, *dst, 0, 0, (Picture::BlendMode)blend_mode);
	__SQ_RETURN
}
//------------------------------------------------------------------------------

//--------------------------------------------------------
void				RegisterPictureBinding(HSQUIRRELVM vm)
//--------------------------------------------------------
{
/*#
	Topic: Picture
	Type: Picture
#*/

/*#
	Section: PictureManagement
	Desc: Management
#*/
	/*#
		Func: NewPicture
		Proto: Picture:int width,int height
		Desc: Create a new picture, width and height are specified in pixels.
		Example: local picture = NewPicture()
	#*/
	sq_register(vm, NewPicture, "NewPicture", _SC(".nn"));
	/*#
		Func: LoadPicture
		Proto: Picture:string path
		Desc: Load a picture. This function supports the following formats: Jpeg, Bmp, Targa, Png, Gif and PSD with alpha channel.
		Note: No caching mechanism involved, every call to this function will result in a filesystem access.
		Example: local picture = LoadPicture("assets/picture.png")
	#*/
	sq_register(vm, LoadPicture, "LoadPicture", _SC(".s"));
	/*#
		Func: PictureClone
		Proto: Picture:Picture source
		Desc: Clone a picture, return the cloned picture.
	#*/
	sq_register(vm, PictureClone, "PictureClone", _SC(".x"));

/*#
	Section: PictureDrawing
	Desc: Drawing
#*/
	/*#
		Func: PictureAlloc
		Proto: bool:Picture,width,height
		Desc: Change the picture internal storage dimensions, existing data will be lost.
	#*/
	sq_register(vm, PictureAlloc, "PictureAlloc", _SC(".xnn"));
	/*#
		Func: PictureLoadContent
		Proto: bool:Picture,string path
		Desc: Reload a picture object content from file.
		See: LoadPicture
	#*/
	sq_register(vm, PictureLoadContent, "PictureLoadContent", _SC(".xs"));
	/*#
		Func: PictureSaveTGA
		Proto: void:Picture,string path
		Desc: Save a picture object to Targa.
		Note: Make sure that you have access to the filesystem you plan on writing to.
		See: SystemHasMountPoint
	#*/
	sq_register(vm, PictureSaveTGA, "PictureSaveTGA", _SC(".xs"));
	/*#
		Func: PictureSaveJPG
		Proto: void:Picture,string path
		Desc: Save a picture object to jpg.
		Note: Make sure that you have access to the filesystem you plan on writing to.
		See: SystemHasMountPoint
	#*/
	sq_register(vm, PictureSaveJPG, "PictureSaveJPG", _SC(".xs"));	
	/*#
		Func: PictureGetRect
		Proto: Rect:Picture
		Desc: Return a picture bounding rectangle.
		Example:
local pict = PictureLoad("picture.psd")
local rect = PictureGetRect(pict)

print("Picture width = " + rect.GetWidth() + ", height = " + rect.GetHeight())
	#*/
	sq_register(vm, PictureGetRect, "PictureGetRect", _SC(".x"));
	/*#
		Func: PictureFill
		Proto: void:Picture,Vector rgba
		Desc: Fill a picture with a solid color defined as an RGBA vector.
	#*/
	sq_register(vm, PictureFill, "PictureFill", _SC(".xx"));
	/*#
		Func: PictureFillLockAlpha
		Proto: void:Picture,Vector rgba
		Desc: Fill a picture with a solid color defined from an RGBA vector, does not write to alpha.
	#*/
	sq_register(vm, PictureFillLockAlpha, "PictureFillLockAlpha", _SC(".xx"));
	/*#
		Func: PictureFillRect
		Proto: void:Picture,Vector rgba,Rect
		Desc: Fill a rectangle inside a picture with a solid color defined from an RGBA vector.
	#*/
	sq_register(vm, PictureFillRect, "PictureFillRect", _SC(".xxx"));
	/*#
		Func: PictureApplyConvolution
		Proto: void:Picture,int kernel_width,int kernel_height,array kernel_values,float filter_weight,int pass_count
		Desc: Apply a convolution filter to the picture using a user specified kernel of values.<br>
			  <br>
			  The <em>weight</em> and <em>pass</em> parameters will be 1 in most use case, 
			  kernel values are integer in the nominal range [0;255].
		Example:
function BlurPicture(picture, blur_strength = 1, blur_pass_count = 1)
{
	local kernel =	// 7x7 kernel
	[
		0, 1, 2, 4, 2, 1, 0,
		1, 2, 4, 6, 4, 2, 1,
		2, 3, 5, 8, 5, 3, 2,
		2, 4, 8, 8, 8, 4, 2,
		2, 3, 5, 8, 5, 3, 2,
		1, 2, 4, 6, 4, 2, 1,
		0, 1, 2, 4, 2, 1, 0
	]
	PictureApplyConvolution(picture, 7, 7, kernel, blur_strength, blur_pass_count)
}
	#*/
	sq_register(vm, PictureApplyConvolution, "PictureApplyConvolution", _SC(".xiiani"));
	/*#
		Func: PictureLine
		Proto: bool:Texture,float start_x,float start_y,float end_x,float end_y,Vector rgba
		Desc: Draw a line, color is specified as an RGBA vector.
	#*/
	sq_register(vm, PictureLine, "PictureLine", _SC(".xnnnnx"));
	/*#
		Func: PictureWriteFont
		Proto: void:Picture,rect,string text,Font font,TextState
		Desc: Render formatted text to a picture.<br>
		TextState is table containing the following keys:<br>
		<ul>
		<li><b>'size'</b>: Size in pixels.
		<li><b>'color'</b>: Hexadecimal RGBA (eg. 0xff0000ff for red at 100% opacity).
		<li><b>'align'</b>: Text alignment, can be any of "left", "center", "right" or "justify".
		<li><b>'format'</b>: Text formating, can be any of "standard", "paragraph" or "column".
		<li><b>'tracking'</b>: Integer value specifying an extra space between glyphs.
		<li><b>'heading'</b>: Integer value specifying an extra space between lines.
		</ul>
	#*/
	sq_register(vm, PictureWriteText, "PictureWriteFont", _SC(".xxsxt"));
	sq_register(vm, PictureWriteText, "PictureWriteText", _SC(".xxsxt"));
	/*#
		Func: PictureSetPixel
		Proto: void:Picture,float x,float y,Vector rgba
		Desc: Set picture pixel at coordinate {x, y} in picture space from an RGBA vector.
	#*/
	sq_register(vm, PictureSetPixel, "PictureSetPixel", _SC(".xnnx"));
	/*#
		Func: PictureGetPixel
		Proto: Vector:Picture,float x,float y
		Desc: Return the picture pixel at coordinate {x, y} in picture space as an RGBA vector.
	#*/
	sq_register(vm, PictureGetPixel, "PictureGetPixel", _SC(".xnn"));

/*#
	Section: PictureManipulation
	Desc: Manipulation
#*/
	/*#
		Func: PictureResize
		Proto: void:Picture,float w,float h
		Desc: Resize picture.
	#*/
	sq_register(vm, PictureResize, "PictureResize", _SC(".xii"));
	/*#
		Func: PictureFlip
		Proto: void:Picture,bool horizontal, bool vertical
		Desc: Flip picture on one or both axis.
	#*/
	sq_register(vm, PictureFlip, "PictureFlip", _SC(".xbb"));

/*#
	Section: PictureBlitting
	Desc: Blitting
#*/
	/*#
		Func: PictureBlit
		Proto: void:Picture source,Picture destination,BlendMode mode
		Desc: Blit a picture to another picture with a selectable blend mode.
	#*/
	sq_register(vm, PictureBlit, "PictureBlit", _SC(".xxi"));
	/*#
		Func: PictureBlitRect
		Proto: void:Picture source,Picture destination,Rect source,Rect destination,BlendMode mode
		Desc: Blit a picture to another picture, both clipping zones and the blend mode can be specified.
	#*/
	sq_register(vm, PictureBlitRect, "PictureBlitRect", _SC(".xxxxi"));
	/*#
		Func: PictureBlitRectMasked
		Proto: void:Picture source,Picture destination,Picture mask,Rect source,Rect destination
		Desc: Blit a picture to another picture using a third picture alpha channel as an opacity mask.
	#*/
	sq_register(vm, PictureBlitRectMasked, "PictureBlitRectMasked", _SC(".xxxxx"));

	sq_pushroottable(vm);

	/*#
		Enum: BlendMode
		Values: BlendReplace,BlendAdd,BlendCompose,BlendComposeFast,BlendMultiply,BlendMultiply2x,BlendAlphaAdd,BlendAlphaMultiply,BlendAlphaMultiply2x,RgbToAlpha
	#*/
	sq_pushstring(vm, "BlendReplace", -1); sq_pushinteger(vm, Picture::BlendReplace); sq_newslot(vm, -3, true);
	sq_pushstring(vm, "BlendAdd", -1); sq_pushinteger(vm, Picture::BlendAdd); sq_newslot(vm, -3, true);
	sq_pushstring(vm, "BlendCompose", -1); sq_pushinteger(vm, Picture::BlendCompose); sq_newslot(vm, -3, true);
	sq_pushstring(vm, "BlendComposeFast", -1); sq_pushinteger(vm, Picture::BlendComposeFast); sq_newslot(vm, -3, true);
	sq_pushstring(vm, "BlendMultiply", -1); sq_pushinteger(vm, Picture::BlendMultiply); sq_newslot(vm, -3, true);
	sq_pushstring(vm, "BlendMultiply2x", -1); sq_pushinteger(vm, Picture::BlendMultiply2x); sq_newslot(vm, -3, true);
	sq_pushstring(vm, "BlendAlphaAdd", -1); sq_pushinteger(vm, Picture::BlendAlphaAdd); sq_newslot(vm, -3, true);
	sq_pushstring(vm, "BlendAlphaMultiply", -1); sq_pushinteger(vm, Picture::BlendAlphaMultiply); sq_newslot(vm, -3, true);
	sq_pushstring(vm, "BlendAlphaMultiply2x", -1); sq_pushinteger(vm, Picture::BlendAlphaMultiply2x); sq_newslot(vm, -3, true);
	sq_pushstring(vm, "RgbToAlpha", -1); sq_pushinteger(vm, Picture::RgbToAlpha); sq_newslot(vm, -3, true);

	sq_pop(vm, 1);
}
