//-----------------------------------------------------------------------------
// AScript emf (Enhanced Metafiles in Win32) module
//-----------------------------------------------------------------------------
#include <ascript.h>

AScript_BeginModule(emf)

//-----------------------------------------------------------------------------
// File structure
//-----------------------------------------------------------------------------
struct MetaHeader {
	enum { Size = 18 };
	XPackedUShort_LE(mtType);
	XPackedUShort_LE(mtHeaderSize);
	XPackedUShort_LE(mtVersion);
	XPackedULong_LE(mtSize);
	XPackedUShort_LE(mtNoObjects);
	XPackedULong_LE(mtMaxRecord);
	XPackedUShort_LE(mtNoParameters);
};

unsigned long _ENHMETA_SIGNATURE = 0x464d4520;

struct EnhMetaHeader {
	enum { Size = 88 };
	XPackedULong_LE(iType);				// Record type EMR_HEADER.
	XPackedULong_LE(nSize);				// Record size in bytes. This may be greater
										// than the sizeof(ENHMETAHEADER).
	XPackedLong_LE(rclBounds_left);		// Inclusive-inclusive bounds in device units.
	XPackedLong_LE(rclBounds_top);
	XPackedLong_LE(rclBounds_right);
	XPackedLong_LE(rclBounds_bottom);
	XPackedLong_LE(rclFrame_left);		// Inclusive-inclusive Picture Frame of 
	XPackedLong_LE(rclFrame_top);		// metafile in .01 mm units. 
	XPackedLong_LE(rclFrame_right);
	XPackedLong_LE(rclFrame_bottom);
	XPackedULong_LE(dSignature);		// Signature.  Must be ENHMETA_SIGNATURE.
	XPackedULong_LE(nVersion);			// Version number.
	XPackedULong_LE(nBytes);			// Size of the metafile in bytes.
	XPackedULong_LE(nRecords);			// Number of records in the metafile.
	XPackedUShort_LE(nHandles);			// Number of handles in the handle table.
										// Handle index zero is reserved.
	XPackedUShort_LE(sReserved);		// Reserved.  Must be zero.
	XPackedULong_LE(nDescription);		// Number of chars in the unicode description string.
										// This is 0 if there is no description string.
	XPackedULong_LE(offDescription);	// Offset to the metafile description record.
										// This is 0 if there is no description string.
	XPackedULong_LE(nPalEntries);		// Number of entries in the metafile palette.
	XPackedLong_LE(szlDevice_cx);		// Size of the reference device in pixels. 
	XPackedLong_LE(szlDevice_cy);
	XPackedLong_LE(szlMillimeters_cx);	// Size of the reference device in millimeters.
	XPackedLong_LE(szlMillimeters_cy);
};

struct MetaRecord {
	enum { Size = 6 };
	XPackedULong_LE(rdSize);			// Record size in bytes
	XPackedUShort_LE(rdFunction);		// Record type META_XXX
	//WORD rdParm[1];					// WORD array of parameters
};

struct EnhMetaRecord {
	enum { Size = 8 };
	XPackedULong_LE(iType);				// Record type EMR_XXX
	XPackedULong_LE(nSize);				// Record size in bytes
	//DWORD dParm[1];					// DWORD Array of parameters
};

const unsigned long _EMR_ABORTPATH				= 68;
const unsigned long _EMR_ANGLEARC				= 41;
const unsigned long _EMR_ARC					= 45;
const unsigned long _EMR_ARCTO					= 55;
const unsigned long _EMR_BEGINPATH				= 59;
const unsigned long _EMR_BITBLT					= 76;
const unsigned long _EMR_CHORD					= 46;
const unsigned long _EMR_CLOSEFIGURE			= 61;
const unsigned long _EMR_CREATEBRUSHINDIRECT	= 39;
const unsigned long _EMR_CREATEDIBPATTERNBRUSHPT= 94;
const unsigned long _EMR_CREATEMONOBRUSH		= 93;
const unsigned long _EMR_CREATEPALETTE			= 49;
const unsigned long _EMR_CREATEPEN				= 38;
const unsigned long _EMR_DELETEOBJECT			= 40;
const unsigned long _EMR_ELLIPSE				= 42;
const unsigned long _EMR_ENDPATH				= 60;
const unsigned long _EMR_EOF					= 14;
const unsigned long _EMR_EXCLUDECLIPRECT		= 29;
const unsigned long _EMR_EXTCREATEFONTINDIRECTW	= 82;
const unsigned long _EMR_EXTCREATEPEN			= 95;
const unsigned long _EMR_EXTFLOODFILL			= 53;
const unsigned long _EMR_EXTSELECTCLIPRGN		= 75;
const unsigned long _EMR_EXTTEXTOUTA			= 83;
const unsigned long _EMR_EXTTEXTOUTW			= 84;
const unsigned long _EMR_FILLPATH				= 62;
const unsigned long _EMR_FILLRGN				= 71;
const unsigned long _EMR_FLATTENPATH			= 65;
const unsigned long _EMR_FRAMERGN				= 72;
const unsigned long _EMR_GDICOMMENT				= 70;
const unsigned long _EMR_HEADER					= 1;
const unsigned long _EMR_INTERSECTCLIPRECT		= 30;
const unsigned long _EMR_INVERTRGN				= 73;
const unsigned long _EMR_LINETO					= 54;
const unsigned long _EMR_MASKBLT				= 78;
const unsigned long _EMR_MODIFYWORLDTRANSFORM	= 36;
const unsigned long _EMR_MOVETOEX				= 27;
const unsigned long _EMR_OFFSETCLIPRGN			= 26;
const unsigned long _EMR_PAINTRGN				= 74;
const unsigned long _EMR_PIE					= 47;
const unsigned long _EMR_PLGBLT					= 79;
const unsigned long _EMR_POLYBEZIER				= 2;
const unsigned long _EMR_POLYBEZIER16			= 85;
const unsigned long _EMR_POLYBEZIERTO			= 5;
const unsigned long _EMR_POLYBEZIERTO16			= 88;
const unsigned long _EMR_POLYDRAW				= 56;
const unsigned long _EMR_POLYDRAW16				= 92;
const unsigned long _EMR_POLYGON				= 3;
const unsigned long _EMR_POLYGON16				= 86;
const unsigned long _EMR_POLYLINE				= 4;
const unsigned long _EMR_POLYLINE16				= 87;
const unsigned long _EMR_POLYLINETO				= 6;
const unsigned long _EMR_POLYLINETO16			= 89;
const unsigned long _EMR_POLYPOLYGON			= 8;
const unsigned long _EMR_POLYPOLYGON16			= 91;
const unsigned long _EMR_POLYPOLYLINE			= 7;
const unsigned long _EMR_POLYPOLYLINE16			= 90;
const unsigned long _EMR_POLYTEXTOUTA			= 96;
const unsigned long _EMR_POLYTEXTOUTW			= 97;
const unsigned long _EMR_REALIZEPALETTE			= 52;
const unsigned long _EMR_RECTANGLE				= 43;
const unsigned long _EMR_RESIZEPALETTE			= 51;
const unsigned long _EMR_RESTOREDC				= 34;
const unsigned long _EMR_ROUNDRECT				= 44;
const unsigned long _EMR_SAVEDC					= 33;
const unsigned long _EMR_SCALEVIEWPORTEXTEX		= 31;
const unsigned long _EMR_SCALEWINDOWEXTEX		= 32;
const unsigned long _EMR_SELECTCLIPPATH			= 67;
const unsigned long _EMR_SELECTOBJECT			= 37;
const unsigned long _EMR_SELECTPALETTE			= 48;
const unsigned long _EMR_SETARCDIRECTION		= 57;
const unsigned long _EMR_SETBKCOLOR				= 25;
const unsigned long _EMR_SETBKMODE				= 18;
const unsigned long _EMR_SETBRUSHORGEX			= 13;
const unsigned long _EMR_SETCOLORADJUSTMENT		= 23;
const unsigned long _EMR_SETDIBITSTODEVICE		= 80;
const unsigned long _EMR_SETMAPMODE				= 17;
const unsigned long _EMR_SETMAPPERFLAGS			= 16;
const unsigned long _EMR_SETMETARGN				= 28;
const unsigned long _EMR_SETMITERLIMIT			= 58;
const unsigned long _EMR_SETPALETTEENTRIES		= 50;
const unsigned long _EMR_SETPIXELV				= 15;
const unsigned long _EMR_SETPOLYFILLMODE		= 19;
const unsigned long _EMR_SETROP2				= 20;
const unsigned long _EMR_SETSTRETCHBLTMODE		= 21;
const unsigned long _EMR_SETTEXTALIGN			= 22;
const unsigned long _EMR_SETTEXTCOLOR			= 24;
const unsigned long _EMR_SETVIEWPORTEXTEX		= 11;
const unsigned long _EMR_SETVIEWPORTORGEX		= 12;
const unsigned long _EMR_SETWINDOWEXTEX			= 9;
const unsigned long _EMR_SETWINDOWORGEX			= 10;
const unsigned long _EMR_SETWORLDTRANSFORM		= 35;
const unsigned long _EMR_STRETCHBLT				= 77;
const unsigned long _EMR_STRETCHDIBITS			= 81;
const unsigned long _EMR_STROKEANDFILLPATH		= 63;
const unsigned long _EMR_STROKEPATH				= 64;
const unsigned long _EMR_WIDENPATH				= 66;

//-----------------------------------------------------------------------------
// Function
//-----------------------------------------------------------------------------
class EMR_basement {
protected:
	unsigned long _iType;
	size_t _dwordsParms;
	char *_buff;
public:
	inline EMR_basement(unsigned long iType, size_t dwordsParms) :
					_iType(iType), _dwordsParms(dwordsParms), _buff(NULL) {
		size_t bytes = _dwordsParms * 4;
		if (bytes > 0) {
			_buff = new char [bytes];
			::memset(_buff, 0x00, bytes);
		}
	}
	virtual ~EMR_basement() {
		delete[] _buff;
	}
	bool Read(Signal sig, Stream &stream) {
		size_t bytes = _dwordsParms * 4;
		size_t bytesRead = stream.Read(sig, _buff, bytes);
		if (sig.IsSignalled()) return false;
		if (bytesRead < bytes) {
			sig.SetError(ERR_FormatError, "failed to read a record");
			return false;
		}
		return true;
	}
};

class EMR_SetMapMode : public EMR_basement {
public:
	enum { Type = 17 };
	struct Parms {
		XPackedLong_LE(fnMapMode);
	};
public:
	inline EMR_SetMapMode() : EMR_basement(Type, 1) {}
	inline EMR_SetMapMode(int fnMapMode) : EMR_basement(Type, 1) {
		Parms &parms = *reinterpret_cast<Parms *>(_buff);
		XPackLong(parms.fnMapMode, fnMapMode);
	}
};

class EMR_CreatePen : public EMR_basement {
public:
	enum { Type = 38 };
	struct Parms {
		XPackedLong_LE(fnPenStyle);
		XPackedLong_LE(nWidth);
		XPackedULong_LE(crColor);
	};
public:
	inline EMR_CreatePen() : EMR_basement(Type, 5) {}
	inline EMR_CreatePen(int fnMapMode) : EMR_basement(Type, 5) {
		Parms &parms = *reinterpret_cast<Parms *>(_buff);
		XPackLong(parms.fnMapMode, fnMapMode);
	}
};

//-----------------------------------------------------------------------------
// AScript module functions: emf
//-----------------------------------------------------------------------------
// emf.test(stream:stream)
AScript_DeclareFunction(test)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "stream", VTYPE_Stream);
}

AScript_ImplementFunction(test)
{
	Stream &stream = args.GetStream(0);
	unsigned long nBytes = 0;
	unsigned long nRecords = 0;
	unsigned long nDescription = 0;
	unsigned long offDescription = 0;
	unsigned long nPalEntries = 0;
	do {
		EnhMetaHeader hdr;
		stream.Read(sig, &hdr, EnhMetaHeader::Size);
		unsigned long dSignature = XUnpackULong(hdr.dSignature);
		if (dSignature != _ENHMETA_SIGNATURE) {
			sig.SetError(ERR_FormatError, "invalid signature of EMF file");
			return Value::Null;
		}
		nBytes			= XUnpackULong(hdr.nBytes);
		nRecords		= XUnpackULong(hdr.nRecords);
		nDescription	= XUnpackULong(hdr.nDescription);
		offDescription	= XUnpackULong(hdr.offDescription);
		nPalEntries		= XUnpackULong(hdr.nPalEntries);
		unsigned long nSize = XUnpackULong(hdr.nSize);
		if (nSize > EnhMetaHeader::Size) {
			stream.Seek(sig, nSize - EnhMetaHeader::Size, Stream::SeekCur);
		}
	} while (0);
	::printf("%d, %d, %d, %d, %d\n", nBytes, nRecords, nDescription, offDescription, nPalEntries);
	for (unsigned long iRecord = 0; iRecord < nRecords; iRecord++) {
		EnhMetaRecord rec;
		if (stream.Read(sig, &rec, EnhMetaRecord::Size) < EnhMetaRecord::Size) break;
		::printf("iType = %d, nSize = %d\n", XUnpackULong(rec.iType), XUnpackULong(rec.nSize));
		unsigned long nSize = XUnpackULong(rec.nSize);
		if (nSize > EnhMetaRecord::Size) {
			stream.Seek(sig, nSize - EnhMetaRecord::Size, Stream::SeekCur);
		}
		
	}
	return Value::Null;
}

// Module entry
AScript_ModuleEntry()
{
	AScript_AssignFunction(test);
}

AScript_ModuleTerminate()
{
}

AScript_EndModule(emf, emf)

AScript_RegisterModule(emf)
