#include <../source/PAL/glctx.h>
#include <../source/PAL/dataman.h>

#ifdef	CONFIG_CLIPBOARD
#include <../source/PAL/oshead.h>
#ifndef	__WIN32__
#define	DEFAULT_CLIP_PATH	"/tmp/clip.wmf"
#endif	/* !__WIN32__ */
#endif	/* CONFIG_CLIPBOARD */

#ifndef	__WIN32__

#ifdef	CONFIG_USE_NATIVE_METAFILE_API
#error	"There is no native support for metafiles on target platform"
#endif	/* CONFIG_USE_NATIVE_METAFILE_API */

#endif	/* __WIN32__ */

/*
 *	This object exposes only low-level drawing routines, but
 *	under Win32 one may compile with native metafile support,
 *	obtain metafile handle via metaGetHandleNP(), and than
 *	call any GDI function.
 */
typedef struct meta_fd_s
{
	libv_object_t		base;
	gl_tl_callbacks_t	meta_api;
	u32_t				x_ext, y_ext; /* raw extents */
    u32_t				mm_width, mm_height;
    vec_t				x_scale, y_scale;
	u32_t				vert_count;
    char				file_name[_MAX_PATH];
    struct
    {
    	s16_t		x;
    	s16_t		y;
		color4b_t 	c;
    }					edge[2];
#ifndef	CONFIG_USE_NATIVE_METAFILE_API
	u8_stack_t			cmd_buf;
	u32_t				cmd_header_len;
#else	/* CONFIG_USE_NATIVE_METAFILE_API */
	HDC					meta_dc;
	/* Used for OpenGL scene capture */
	LOGBRUSH			lbrush;
	LOGPEN				lpen;
	HFONT				font;
	HPEN				null_pen;
	HBRUSH				null_brush;
	POINT				pts[3];
#endif	/* CONFIG_USE_NATIVE_METAFILE_API */
    void		*raw;
    u32_t		nmax;
    u32_t		stored;
}	meta_fd_t, *meta_fd_p;

#ifdef	CHECKED_BUILD
#define META_MAGIC		0x4D455441
V_LOCAL char *meta_object = "metafile vector renderer";
#endif	/* CHECKED_BUILD */

#define	GET_METAFILE(handle,caller) \
		CHECK_AND_GET_OBJECT(meta_fd_t,handle,caller,meta_object,META_MAGIC)
		
/*	Aldus placeable metafile signature */
#define	PMPHKEY			0x9AC6CDD7

#include <../source/PAL/pack0.h>
/*	Placeable metafile header */
typedef struct aldus_header_s
{
	u32_t	key;
	u16_t	emptywmf;
	u16_t	left, top, right, bottom;
	u16_t	inch;
	u32_t	reserved;
	u16_t	checksum;
}	ATTRIBUTE_PACKED	aldus_header_t, *aldus_header_p;
#include <../source/PAL/ppack.h>

/*	Computes placeable metafile checksum */
V_LOCAL u16_t
pmfhCheckSum(aldus_header_t *pmfh)
{
	int i;
	u16_t sum=0;
	u16_t *ptr=(u16_t*)pmfh;
	for(i=0;i<10;i++)
		sum^=ptr[i];
	return sum;
}

#ifdef	CONFIG_USE_NATIVE_METAFILE_API

/*
 *	Win32 - specific (non - portable) functions.
 */

/*
 *	Call to get raw metafile extents.
 */
u32_t	V_GLOBAL
metaGetMaxXNP(vhandle_t handle)
{
	meta_fd_p self=GET_METAFILE(handle,"metaGetMaxX");
	if(self==NULL)
		return 0;
	return self->x_ext;
}

u32_t	V_GLOBAL
metaGetMaxYNP(vhandle_t handle)
{
	meta_fd_p self;
	meta_fd_p self=GET_METAFILE(handle,"metaGetMaxY");
	if(self==NULL)
		return 0;
	return self->x_ext;
}

/*
 *	Call to get GDI handle of metafile.
 */
void_p	V_GLOBAL
metaGetHandleNP(vhandle_t handle)
{
	meta_fd_p self;
    self=GET_METAFILE(handle,"metaHandleNP");
	if(self==NULL)
		return 0;
	return (void_p)self->meta_dc;
}

/*
 *	GDI op seems to be broken ...
 */
void	V_GLOBAL
metaTextExtentNP(vhandle_t handle,char *str,u32_t *cx,u32_t *cy)
{
	meta_fd_p self;
	HDC ref_dc;
	HFONT old_font, real_font;
	BOOL retval;
	SIZE sz;
	self=GET_METAFILE(handle,"metaTextExtentNP");
	if(self == NULL)
	{
		if(cx)
			*cx=0;
		if(cy)
			*cy=0;
        return;
	}
	ref_dc=GetDC(NULL);
	/* begin hack */
	real_font=SelectObject(self->meta_dc,GetStockObject(SYSTEM_FONT));
	SelectObject(self->meta_dc,real_font);
	/* end hack: we obtained current dc's font handle ! */
	old_font=SelectObject(ref_dc,real_font);
	retval=GetTextExtentPoint32(ref_dc,str,strlen(str),&sz);
	SelectObject(ref_dc,old_font);
	ReleaseDC(NULL,ref_dc);
	if(cx)
		*cx=sz.cx;
	if(cy)
		*cy=sz.cy;
	return;
}

/*
 *	Changes font for DC of metafile.
 *	Input:
 *		mf - wmf_t object;
 *		char_height - height of character (in metafile's logical
 *			units; 100 mean 1/10 of metafile's height etc.);
 *		deg_ori - orientation in degrees (counter-clockwise);
 *		weight - weight from 0 to 900;
 *		italic - if 1, italic font used;
 *		face - font face name.
 */
void	V_GLOBAL
metaChangeFontNP(vhandle_t handle,int char_height,int deg_ori,
	int weight,int italic,char *face)
{
	meta_fd_p self;
	HFONT hnew;
	self=GET_METAFILE(handle,"metaChangeFontNP");
	if(self == NULL)
        return;
	if(weight<0)
		weight=0;
	else
		if(weight>900)
			weight=900;
	hnew=CreateFont(char_height,0,deg_ori*10,deg_ori*10,weight,italic,0,0,
#ifdef	CONFIG_CYRILLIC_HACKS
		RUSSIAN_CHARSET,
#else	/* !CONFIG_CYRILLIC_HACKS */
		DEFAULT_CHARSET,
#endif	/* CONFIG_CYRILLIC_HACKS */
		OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,
		DEFAULT_QUALITY,DEFAULT_PITCH,face ? face : "Courier New");
	if(hnew)
	{
		SelectObject(self->meta_dc,hnew);
		if(self->font)
			DeleteObject(self->font);
		self->font=hnew;
	}
	return;
}

#else	/* !CONFIG_USE_NATIVE_METAFILE_API */

/*
 *	Hacked from wingdi.h
 */
#include <../source/PAL/pack0.h>
typedef struct metaheader_s
{
	u16_t	mtType;
    u16_t	mtHeaderSize;
    u16_t	mtVersion;
    u32_t	mtSize;
    u16_t	mtNoObjects;
    u32_t	mtMaxRecord;
    u16_t	mtNoParameters;
}	ATTRIBUTE_PACKED metaheader_t, *metaheader_p;
#include <../source/PAL/ppack.h>

/*
 *	BEGIN LOW-LEVEL HACK
 */

#define inject_le_word(arr,ind1,ind2,word) \
		(arr)[(ind2)]=(u8_t)(((u16_t)(word) >> 8) & 0xFF); \
		(arr)[(ind1)]=(u8_t)(word & 0xFF);

V_LOCAL	void
metaEmitInit(meta_fd_p dev)
{
	metaheader_t hdr = {
			ME2LE_SHORT(1),
			ME2LE_SHORT(9),
			ME2LE_SHORT(0x300),
			0, /* set later */
			ME2LE_SHORT(3),
			ME2LE_SHORT(10),
			ME2LE_SHORT(0)
		};
	u8_t seq[] = {
		/* SetMapMode(MM_ANOSOTROPIC) */
		0x04,0x00,0x00,0x00, 0x03,0x01, 0x08,0x00,
		/* SetWindowExt(0xFFFF,0xFFFF) */
		0x05,0x00,0x00,0x00, 0x0C,0x02, 0xFF,0xFF, 0xFF,0xFF,
		/* SetWindowOrg(0,0) */
		0x05,0x00,0x00,0x00, 0x0B,0x02, 0x00,0x00, 0x00,0x00,
		/* CreatePenIndirect - null pen handle, index 0 */
		0x08,0x00,0x00,0x00, 0xFA,0x02, 5,0, 1,0, 0,0, 0,0, 0,0,
		/* SelectObject */
		0x04,0x00,0x00,0x00, 0x2D,0x01, 0,0,
		/* CreateBlushIndirect - null brush handle, index 1 */
		0x07,0x00,0x00,0x00, 0xFC,0x02, 1,0, 0,0, 0,0, 0,0,
		/* SelectObject */
		0x04,0x00,0x00,0x00, 0x2D,0x01, 1,0
	};
	inject_le_word(seq,14,15,dev->y_ext);
	inject_le_word(seq,16,17,dev->x_ext);
	u8sPushBuffer(&dev->cmd_buf,(u8_t*)&hdr,sizeof(hdr));
	u8sPushBuffer(&dev->cmd_buf,seq,sizeof(seq) / sizeof(seq[0]));
}

V_LOCAL	void
metaEmitPixel(meta_fd_p dev,s16_t x,s16_t y,u8_t r,u8_t g,u8_t b)
{
	u8_t seq[14]={0x07,0x00,0x00,0x00,0x1F,0x04};
	seq[6]=r;
	seq[7]=g;
	seq[8]=b;
	seq[9]=0;
	inject_le_word(seq,10,11,y);
	inject_le_word(seq,12,13,x);
	u8sPushBuffer(&dev->cmd_buf,seq,14);
}

V_LOCAL	void
metaEmitLine(meta_fd_p dev,
	s16_t x1,s16_t y1,		/* start point */
	s16_t x2,s16_t y2,		/* end point */
	u8_t r,u8_t g,u8_t b)	/* color */
{
	u8_t seq[] = {
		/* CreatePenIndirect - handle #2 */
		0x08,0x00,0x00,0x00, 0xFA,0x02, 0x00,0x00,0x01,0x00,0x00,0x00,
			0xFF,0xFF,0xFF, 0,
		/* SelectObject */
		/* 16 */ 0x04,0x00,0x00,0x00, 0x2D,0x01, 0x02,0x00,
		/* MoveTo */
		/* 24 */ 0x05,0x00,0x00,0x00, 0x14,0x02, 0x00, 0x00, 0x00, 0x00,
		/* LineTo */
		/* 34 */ 0x05,0x00,0x00,0x00, 0x13,0x02, 0xFF, 0xFF, 0xFF, 0xFF,
		/* SelectObject(handle #0 - null pen), make pen '#2' free */
		0x04,0x00,0x00,0x00, 0x2D,0x01, 0,0,
		/* DeleteObject(handle #2) */
		0x04,0x00,0x00,0x00, 0xF0,0x01, 2,0
		};
	/* inject color */
	seq[12]=r;
	seq[13]=g;
	seq[14]=b;
	/* inject initial coords */
	inject_le_word(seq,30,31,y1);
	inject_le_word(seq,32,33,x1);
	/* inject final coords */
	inject_le_word(seq,40,41,y2);
	inject_le_word(seq,42,43,x2);
	u8sPushBuffer(&dev->cmd_buf,seq,sizeof(seq) / sizeof(seq[0]));
}
		
V_LOCAL	void
metaEmitTriangle(meta_fd_p dev,
	s16_t x1,s16_t y1,		/* vertex 1 */
	s16_t x2,s16_t y2,		/* vertex 2 */
	s16_t x3,s16_t y3,		/* vertex 3 */
	u8_t r,u8_t g,u8_t b)	/* color */
{
	u8_t seq[] = {
		/* CreateBlushIndirect */
		0x07,0x00,0x00,0x00, 0xFC,0x02, 0x00,0x00,
			0,0,0,0, /* place to inject color */
			0x00,0x00,
		/* SelectObject */
		/* 14 */ 0x04,0x00,0x00,0x00, 0x2D,0x01, 2,0,
		/* Polygon(3, ...) */
		/* 22 */ 0x0A,0x00,0x00,0x00, 0x24,0x03, 0x03, 0x00,
			0,0,0,0, 0,0,0,0, 0,0,0,0, /* place to inject coords */
		/* SelectObject(handle #1 - null brush), make brush '#2' free */
		0x04,0x00,0x00,0x00, 0x2D,0x01, 1,0,
		/* DeleteObject(handle #2) */
		0x04,0x00,0x00,0x00, 0xF0,0x01, 2,0
		};
	/* inject color */
	seq[8]=r;
	seq[9]=g;
	seq[10]=b;
	/* inject coords */
	inject_le_word(seq,30,31,x1);
	inject_le_word(seq,32,33,y1);
	inject_le_word(seq,34,35,x2);
	inject_le_word(seq,36,37,y2);
	inject_le_word(seq,38,39,x3);
	inject_le_word(seq,40,41,y3);
	u8sPushBuffer(&dev->cmd_buf,seq,sizeof(seq) / sizeof(seq[0]));
}

/*
 *	END LOW-LEVEL HACK
 */

#endif	/* CONFIG_USE_NATIVE_METAFILE_API */

V_LOCAL u32_t V_CALL
meta_destroy(vhandle_t handle)
{
	vhandle_t df;
    meta_fd_p dev=(meta_fd_p)handle;
	aldus_header_t aph;
#ifdef	CONFIG_USE_NATIVE_METAFILE_API
	u32_t	size;
	u8_t	*bits;
	HMETAFILE hmf;
#else	/* !CONFIG_USE_NATIVE_METAFILE_API */
	metaheader_t *hdr;
	u8_t endseq[]={3,0,0,0,0,0};
#endif	/* !CONFIG_USE_NATIVE_METAFILE_API */
	/* Check reference count */
	UNREF_EXIT(dev);
#ifndef	CONFIG_USE_NATIVE_METAFILE_API
	/* Finish writing  */
	u8sPushBuffer(&dev->cmd_buf,endseq,sizeof(endseq) / sizeof(endseq[0]));
#else	/* CONFIG_USE_NATIVE_METAFILE_API */
	/* Get metafile handle */
	hmf=CloseMetaFile(dev->meta_dc);
	if(hmf == NULL)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"metaDestroy(): CloseMetaFile() failed (rc %d).\n",
			GetLastError());
#endif	/* CHECKED_BUILD */
		goto cleanup;
	}
#endif	/* CONFIG_USE_NATIVE_METAFILE_API */

	/*
	 * Note: System clipboard exists only only under __WIN32__.
	 * For any other platform we already perform file name
	 * substitution in metaCreateRender().
	 */
#if	defined(CONFIG_CLIPBOARD) && defined(__WIN32__)
	if(!strcasecmp(dev->file_name,libv_clipboard_name))
    {
        HGLOBAL hmfp;
#ifndef	CONFIG_USE_NATIVE_METAFILE_API
        HMETAFILE hmf;
#endif	/* !CONFIG_USE_NATIVE_METAFILE_API */
		METAFILEPICT *mfp;
		if(!OpenClipboard(NULL))
		{
#ifdef	CHECKED_BUILD
			conlPrintf(LOG_ERROR,
				"metaDestroy(): can't OpenClipboard() (rc %d).\n",
				GetLastError());
#endif	/* CHECKED_BUILD */
			goto cleanup;
		}

#ifndef	CONFIG_USE_NATIVE_METAFILE_API
    	/* Pass metafile data to GDI */
        hmf=SetMetaFileBitsEx((UINT)u8sCount(&dev->cmd_buf),
        	(CONST BYTE*)u8sGetStorage(&dev->cmd_buf));
#endif	/* CONFIG_USE_NATIVE_METAFILE_API */

		/* Allocate piece of GMEM_DDESHARE'd memory */
		hmfp=GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,
        	sizeof(METAFILEPICT));
		mfp=(METAFILEPICT*)GlobalLock(hmfp);
		V_ASSERT(mfp);
		memZero(mfp,sizeof(METAFILEPICT));
		EmptyClipboard();
		/* Dimensions in MM_HIMETRIC units */
		mfp->mm=MM_ANISOTROPIC;
		mfp->xExt=dev->mm_width * 100;
		mfp->yExt=dev->mm_height * 100;
		mfp->hMF=hmf;
		/* Release HGLOBAL */
		GlobalUnlock(hmfp);
		/* Pass it to system */
		SetClipboardData(CF_METAFILEPICT,hmfp);
		/* Done - close clipboard */
		CloseClipboard();
		DeleteMetaFile(hmf);
		goto cleanup;
    }
#endif	/* CONFIG_CLIPBOARD && __WIN32__ */

	aph.key=PMPHKEY;
	aph.emptywmf=0;
	aph.left=0;
	aph.top=0;
	aph.right=(u16_t)(10000 * dev->mm_width / 251);
	aph.bottom=(u16_t)(10000 * dev->mm_height / 251);
	aph.inch=1000;
	aph.reserved=0;
	aph.checksum=pmfhCheckSum(&aph);
	df=fileOpenWrite(dev->file_name);
	if(df == NULL)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"metaDestroy(): can't create output file '%s'.\n",
            	dev->file_name);
#endif	/* CHECKED_BUILD */
		goto cleanup;
	}
	filePutLittleLong(df,aph.key);
	filePutLittleShort(df,aph.emptywmf);
	filePutLittleShort(df,aph.left);
	filePutLittleShort(df,aph.top);
	filePutLittleShort(df,aph.right);
	filePutLittleShort(df,aph.bottom);
	filePutLittleShort(df,aph.inch);
	filePutLittleLong(df,aph.reserved);
	filePutLittleShort(df,aph.checksum);
#ifndef	CONFIG_USE_NATIVE_METAFILE_API
	hdr=(metaheader_t *)u8sGetStorage(&dev->cmd_buf);
	hdr->mtSize=ME2LE_LONG(u8sCount(&dev->cmd_buf) >> 1);
	fileWrite(df,hdr,u8sCount(&dev->cmd_buf));
	fileClose(df);
#else	/* CONFIG_USE_NATIVE_METAFILE_API */
	size=GetMetaFileBitsEx(hmf,0,NULL);
	if(!size)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"metaDestroy(): GetMetaFileBitsEx() failed (rc %d).\n",
			GetLastError());
#endif	/* CHECKED_BUILD */
		DeleteMetaFile(hmf);
		return false;
	}
	bits=(u8_t*)memAlloc(size);
	GetMetaFileBitsEx(hmf,size,bits);
	fileWrite(df,bits,size);
	memFree(bits);
#endif	/* CONFIG_USE_NATIVE_METAFILE_API */
cleanup:;
#ifndef	CONFIG_USE_NATIVE_METAFILE_API
	u8sClear(&dev->cmd_buf);
#else	/* CONFIG_USE_NATIVE_METAFILE_API */
	DeleteMetaFile(hmf);
#endif	/* CONFIG_USE_NATIVE_METAFILE_API */
    memFreeObject(handle);
	return 0;
}

/*
 *	Translates coord from GL viewport space to metafile coord space
 */
#define metaTranslateCoords(dev,x,y,coord) \
		x = (s16_t)(coord[0] * dev->x_scale); \
		y = (s16_t)(dev->y_ext - coord[1] * dev->y_scale);

/*
 *	Computes average for two colors
 */
#define	metaAverageColor2(c,c1,c2) \
		c[0] = (u8_t)(((u8_t)c1[0] >> 1) + ((u8_t)c2[0] >> 1)); \
		c[1] = (u8_t)(((u8_t)c1[1] >> 1) + ((u8_t)c2[1] >> 1)); \
		c[2] = (u8_t)(((u8_t)c1[2] >> 1) + ((u8_t)c2[2] >> 1));

/*
 *	Computes average for three colors
 */
#define	metaAverageColor3(c,c1,c2,c3) \
		c[0] = CLAMP_FLOAT_TO_BYTE(0.33333333 * \
        	((u32_t)c1[0]+(u32_t)c2[0]+(u32_t)c3[0])); \
		c[1] = CLAMP_FLOAT_TO_BYTE(0.33333333 * \
        	((u32_t)c1[1]+(u32_t)c2[1]+(u32_t)c3[1])); \
		c[2] = CLAMP_FLOAT_TO_BYTE(0.33333333 * \
			((u32_t)c1[2]+(u32_t)c2[2]+(u32_t)c3[2]));

#include <../source/PAL/argsused.h>
V_LOCAL	void V_CALL 
metaPoint(void *dev,vec3_t v,color4b_t c,vec2_t uv)
{
	s16_t x, y;
	meta_fd_p self=(meta_fd_p)dev;
	metaTranslateCoords(self,x,y,v);
#ifndef	CONFIG_USE_NATIVE_METAFILE_API
	metaEmitPixel(self,x,y,c[0],c[1],c[2]);
#else	/* CONFIG_USE_NATIVE_METAFILE_API */
	SetPixel(self->meta_dc,x,y,RGB(c[0],c[1],c[2]));
#endif	/* CONFIG_USE_NATIVE_METAFILE_API */
}

/*
 *	TODO: implement recursive subdivisions
 */
#include <../source/PAL/argsused.h>
V_LOCAL void V_CALL
metaLine(void *dev,vec3_t v1,color4b_t c1,vec2_t uv1,
	vec3_t v2,color4b_t c2,vec2_t uv2)
{
	s16_t x1, y1;
	s16_t x2, y2;
	color4b_t c;
	meta_fd_p self=(meta_fd_p)dev;
	metaTranslateCoords(self,x1,y1,v1);
	metaTranslateCoords(self,x2,y2,v2);
	metaAverageColor2(c,c1,c2);
#ifndef	CONFIG_USE_NATIVE_METAFILE_API
	metaEmitLine(self,x1,y1,x2,y2,c[0],c[1],c[2]);
#else	/* CONFIG_USE_NATIVE_METAFILE_API */
	self->lpen.lopnColor=RGB(c[0],c[1],c[2]);
	SelectObject(self->meta_dc,CreatePenIndirect(&self->lpen));
	MoveToEx(self->meta_dc,x1,y2,NULL);
	LineTo(self->meta_dc,x2,y2);
	DeleteObject(SelectObject(self->meta_dc,self->null_pen));
#endif	/* CONFIG_USE_NATIVE_METAFILE_API */
}

#include <../source/PAL/argsused.h>
V_LOCAL void V_CALL
metaBeginPolygon(void *dev,int nverts)
{
	meta_fd_p self=(meta_fd_p)dev;
	self->vert_count=0; /* begin new triangle fan */
}

/*
 *	TODO: implement recursive subdivisions
 */
#include <../source/PAL/argsused.h>
void	V_CALL
metaPolygonVertex(void *dev,vec3_t v,color4b_t c,vec2_t uv)
{
	u32_t count;
	s16_t x3, y3;
	color4b_t ac;
	meta_fd_p self=(meta_fd_p)dev;
	count=self->vert_count;
	if(count < 2)
	{
		metaTranslateCoords(self,
			self->edge[count].x,self->edge[count].y,v);
		rgbaByteCopy(self->edge[count].c,c);
		self->vert_count = count + 1;
		return;
	}
	/* emit next triangle of triangle fan */
	metaTranslateCoords(self,x3,y3,v);
	metaAverageColor3(ac,self->edge[0].c,self->edge[1].c,c);
#ifndef	CONFIG_USE_NATIVE_METAFILE_API
	metaEmitTriangle(self,
		self->edge[0].x, self->edge[0].y,
		self->edge[1].x, self->edge[1].y,
		x3, y3,
		ac[0],ac[1],ac[2]);
#else	/* CONFIG_USE_NATIVE_METAFILE_API */
	self->lbrush.lbColor=RGB(ac[0],ac[1],ac[2]);
	SelectObject(self->meta_dc,CreateBrushIndirect(&self->lbrush));
	self->pts[0].x = self->edge[0].x; self->pts[0].y = self->edge[0].y;
	self->pts[1].x = self->edge[1].x; self->pts[1].y = self->edge[1].y;
	self->pts[2].x = x3; self->pts[2].y = y3;
	Polygon(self->meta_dc,self->pts,3);
	DeleteObject(SelectObject(self->meta_dc,self->null_brush));
#endif	/* CONFIG_USE_NATIVE_METAFILE_API */
	/* Set new last vertex */
	self->edge[1].x=x3;
	self->edge[1].y=y3;
	rgbaByteCopy(self->edge[1].c,c);
}

#include <../source/PAL/argsused.h>
V_LOCAL void V_CALL
metaPassThrough(void *dev,vec_t v)
{
}

vhandle_t V_GLOBAL
metaCreateRenderer(char *file_name,u32_t mm_width,u32_t mm_height)
{
	return metaCreateRendererEx(file_name,mm_width,mm_height,
		1.0f,1.0f,0x7FFF,0x7FFF);
}

vhandle_t V_GLOBAL
metaCreateRendererEx(char *file_name,
	u32_t mm_width,u32_t mm_height,
	vec_t logical_width,vec_t logical_height,
	s16_t raw_width,s16_t raw_height)
{
	meta_fd_p dev;
	if(file_name==NULL || !*file_name)
    {
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,"metaCreateRendererEx('%s',%u,%u,%g,%g,%u,%u): "
			"bad output name.\n",strSafe(file_name),mm_width,mm_height,
			logical_width,logical_height,raw_width,raw_height);
#endif	/* CHECKED_BUILD */
    	return NULL;
    }
    if(IsZero(logical_width) || IsZero(logical_height))
    {
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,"metaCreateRendererEx('%s',%u,%u,%g,%g,%u,%u): "
			"bad viewport size.\n",strSafe(file_name),mm_width,mm_height,
			logical_width,logical_height,raw_width,raw_height);
#endif	/* CHECKED_BUILD */
    	return NULL;
    }
    if(!raw_width || !raw_height)
    {
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,"metaCreateRendererEx('%s',%u,%u,%g,%g,%u,%u): "
			"bad output viewport size.\n",strSafe(file_name),mm_width,mm_height,
			logical_width,logical_height,raw_width,raw_height);
#endif	/* CHECKED_BUILD */
    	return NULL;
    }
    ALLOC_OBJECT(dev,meta_fd_t,0,META_MAGIC);
    dev->base.destroy=meta_destroy;
	dev->meta_api.point=metaPoint;
	dev->meta_api.line=metaLine;
	dev->meta_api.begin_polygon=metaBeginPolygon;
	dev->meta_api.polygon_vertex=metaPolygonVertex;
	dev->meta_api.pass_through=metaPassThrough;

    dev->x_ext = raw_width;
    dev->y_ext = raw_height;

	if(!mm_width || !mm_height)
	{
		/* Set width and height to fit picture in A4 sheet */
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,"metaCreateRenderer('%s',%g,%g,%u,%u):"
			" using default output size 240 x 160 mm.\n",
            	strSafe(file_name),logical_width,logical_height,
                mm_width,mm_height);
#endif	/* CHECKED_BUILD */
		dev->mm_width = 240;
		dev->mm_height = 160;
	}
	else
	{
		/* Convert millimeters to inches * 1000 */
	    dev->mm_width = mm_width;
    	dev->mm_height = mm_height;
	}
    dev->x_scale = 1 / logical_width * dev->x_ext;
    dev->y_scale = 1 / logical_height * dev->y_ext;
	dev->vert_count=0;
#if defined(CONFIG_CLIPBOARD) && !defined(__WIN32__)
	/*
	 * There is no system clipboard on target platform.
	 * Data will be written in '/tmp/clip.wmf'
	 */
	if(!strcasecmp(file_name,libv_clipboard_name))
		strCopy(dev->file_name,DEFAULT_CLIP_PATH,_MAX_PATH);
	else
		strCopy(dev->file_name,file_name,_MAX_PATH);
#else	/* !CONFIG_CLIPBOARD || __WIN32__ */
	/* We are either on WIN32, or no clipboard cupport requested */
	strCopy(dev->file_name,file_name,_MAX_PATH);
#endif	/* !CONFIG_CLIPBOARD || __WIN32__ */
#ifndef	CONFIG_USE_NATIVE_METAFILE_API
    u8sInit(&dev->cmd_buf);
    metaEmitInit(dev);
    dev->cmd_header_len=u8sCount(&dev->cmd_buf);
#else	/* CONFIG_USE_NATIVE_METAFILE_API */
	dev->null_pen=GetStockObject(NULL_PEN);
	dev->null_brush=GetStockObject(NULL_BRUSH);
	dev->lbrush.lbStyle=BS_SOLID;
	dev->lpen.lopnStyle=PS_SOLID;
	dev->lpen.lopnWidth.x=1;
	dev->lpen.lopnWidth.y=1;
	dev->meta_dc=CreateMetaFile(NULL);
	SetMapMode(dev->meta_dc,MM_ANISOTROPIC);
	SetWindowExtEx(dev->meta_dc,dev->x_ext,dev->y_ext,NULL);
	SetWindowOrgEx(dev->meta_dc,0,0,NULL);
	SelectObject(dev->meta_dc,dev->null_pen);
	SelectObject(dev->meta_dc,dev->null_brush);
#endif	/* CONFIG_USE_NATIVE_METAFILE_API */
	/* Clear unused fields */
	dev->nmax=0;
	dev->stored=0;
	dev->raw=NULL;
    return (vhandle_t)dev;
}

/*
 *	Note: metaClearBuffer() invalidates Win32 device context, so it must
 *	than be re-aquired via metaGetHandleNP()
 */
void	V_GLOBAL
metaClearBuffer(vhandle_t handle)
{
	meta_fd_p self=GET_METAFILE(handle,"metaClearBuffer");
	if(self == NULL)
        return;
#ifndef	CONFIG_USE_NATIVE_METAFILE_API
    u8sResize(&self->cmd_buf,self->cmd_header_len);
#else	/* CONFIG_USE_NATIVE_METAFILE_API */
	DeleteMetaFile(CloseMetaFile(self->meta_dc));
	self->meta_dc=CreateMetaFile(NULL);
	SetMapMode(self->meta_dc,MM_ANISOTROPIC);
	SetWindowExtEx(self->meta_dc,self->x_ext,self->y_ext,NULL);
	SetWindowOrgEx(self->meta_dc,0,0,NULL);
	SelectObject(self->meta_dc,self->null_pen);
	SelectObject(self->meta_dc,self->null_brush);
#endif	/* CONFIG_USE_NATIVE_METAFILE_API */
	return;
}

gl_tl_callbacks_p V_GLOBAL
metaGetRefAPI(vhandle_t handle)
{
	meta_fd_p self=GET_METAFILE(handle,"metaGetRefAPI");
	if(self == NULL)
        return NULL;
	return &self->meta_api;
}

void V_GLOBAL
metaDestroyRenderer(vhandle_t handle)
{
	libvCloseHandle(handle,"metaDestroyRenderer",meta_object,META_MAGIC);
}


/*
 *	Capture API is platform - independant.
 */

/*
**	How to use:
**		handle=metaBeginCapture( ... );
**		do
**			glDrawSomething();
**		while(metaDrawAgain(handle));
**		metaFinishCapture(handle);
*/

vhandle_t	V_GLOBAL
metaBeginCapture(char *file_name,u32_t mmWidth,u32_t mmHeight)
{
	gl_context_t *ctx;
	meta_fd_p dev;
    u32_t feedback_buffer_hint = 512 * 1024;
    vec_t logical_width, logical_height;
    ctx=vglSafeGetCurrentContext("metaBeginCapture");
    if(ctx == NULL)
    	return NULL;
	logical_width = (vec_t)ctx->width;
	logical_height = (vec_t)ctx->height;
    dev = (meta_fd_p)metaCreateRendererEx(file_name,mmWidth,mmHeight,
    	logical_width,logical_height,0x7FFF,0x7FFF);
	if(dev == NULL)
    	return NULL;
	dev->nmax=feedback_buffer_hint/sizeof(GLfloat);
	dev->raw=vglBeginFeedback(dev->nmax);
	if(dev->raw == NULL)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"metaBeginCapture('%s',%d,%d): "
			"vglBeginFeedback() failed.\n",file_name,mmWidth,mmHeight);
#endif	/* CHECKED_BUILD */
		metaDestroyRenderer(dev);
		return NULL;
	}
	return (vhandle_t)dev;
}

/*
**	Returns 'true' if feedback buffer owerflowed.
*/
vboolean V_GLOBAL
metaDrawAgain(vhandle_t handle)
{
	meta_fd_p self=GET_METAFILE(handle,"metaDrawAgain");
	if(self == NULL)
        return false;
	/* Change render mode, test for feedback overflow */
	self->stored = MAXLONG;
    vglEndFeedback(&self->stored);
    if(self->stored <= self->nmax)
    	return false; /* Ok to decode */
   	/* Increase feedback buffer twice, draw again */
   	self->nmax <<= 1;
	memFree(self->raw);
	self->raw=vglBeginFeedback(self->nmax);
	if(self->raw == NULL)
	{
#ifdef	CHECKED_BUILD
		conlPuts(LOG_ERROR,
           	"metaDrawAgain(): vglBeginFeedback() failed.\n");
#endif	/* CHECKED_BUILD */
		return false; /* but next call will FAIL */
	}
    metaClearBuffer(handle);
	return true; /* caller have to draw scene again */
}

vboolean V_GLOBAL
metaFinishCapture(vhandle_t handle)
{
	vboolean success;
	meta_fd_p self=GET_METAFILE(handle,"metaFinishCapture");
	if(self == NULL)
        return false;
	/* Try to decode feedback buffer */
    if(self->raw)
    {
		success = vglDecodeFeedback(metaGetRefAPI(handle),handle,
			self->raw,self->stored);
		memFree(self->raw);
	}
    else
    	success = false;
	metaDestroyRenderer(handle);
    return success;
}


/*
 *	Creates metafile renderer, enters feedback mode, calls DrawProc(param)
 *	and closes metafile.
 *	Feedback buffer size automatically increases if needed.
 */
vboolean	V_GLOBAL
vglDrawToMetafile(char *file_name,u32_t mmWidth,u32_t mmHeight,
	void (V_CALL *DrawProc)(void *param),void *param)
{
	vhandle_t capture;
	capture=metaBeginCapture(file_name,mmWidth,mmHeight);
	if(capture == NULL)
		return false;
	do
		DrawProc(param);
	while(metaDrawAgain(capture));
	return metaFinishCapture(capture);
}
