//-----------------------------------------------------------------------------
// AScript freetype module
//-----------------------------------------------------------------------------
#include <ascript.h>
#include "ascript/ZLibHelper.h"
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_BITMAP_H
#include FT_OUTLINE_H
#include FT_GLYPH_H

#if defined(HAVE_WINDOWS_H)
#include <shlobj.h>
#endif

AScript_BeginModule(freetype)

AScript_DeclarePrivSymbol(num_faces);
AScript_DeclarePrivSymbol(face_index);
AScript_DeclarePrivSymbol(face_flag_scalable);
AScript_DeclarePrivSymbol(face_flag_fixed_width);
AScript_DeclarePrivSymbol(face_flag_sfnt);
AScript_DeclarePrivSymbol(face_flag_horizontal);
AScript_DeclarePrivSymbol(face_flag_vertical);
AScript_DeclarePrivSymbol(face_flag_kerning);
AScript_DeclarePrivSymbol(face_flag_fast_glyphs);
AScript_DeclarePrivSymbol(face_flag_multiple_masters);
AScript_DeclarePrivSymbol(face_flag_glyph_names);
AScript_DeclarePrivSymbol(face_flag_external_stream);
AScript_DeclarePrivSymbol(face_flag_hinter);
AScript_DeclarePrivSymbol(face_flag_cid_keyed);
AScript_DeclarePrivSymbol(face_flag_tricky);
AScript_DeclarePrivSymbol(style_flag_italic);
AScript_DeclarePrivSymbol(style_flag_bold);
AScript_DeclarePrivSymbol(family_name);
AScript_DeclarePrivSymbol(style_name);
AScript_DeclarePrivSymbol(bbox);
AScript_DeclarePrivSymbol(ascender);
AScript_DeclarePrivSymbol(descender);
AScript_DeclarePrivSymbol(height);

static FT_Library g_lib;

static String GetSysFontPathName();

//-----------------------------------------------------------------------------
// Object_Face declaration
//-----------------------------------------------------------------------------
class Handler {
private:
	Signal _sig;
	Stream *_pStream;
	FT_StreamRec *_pStreamRec;
public:
	inline Handler(Signal sig, Stream *pStream) :
			_sig(sig), _pStream(pStream), _pStreamRec(new FT_StreamRec) {}
	~Handler();
	bool OpenFace(Signal sig, int index, FT_Face *aface);
private:
	FT_ULong Read(FT_ULong pos, FT_Byte *buffer, FT_ULong count);
	static FT_ULong ReadStub(FT_Stream streamFT,
				FT_ULong pos, FT_Byte *buffer, FT_ULong count);
};

Handler::~Handler()
{
	Stream::Delete(_pStream);
	delete _pStreamRec;
}

bool Handler::OpenFace(Signal sig, int index, FT_Face *aface)
{
	::memset(_pStreamRec, 0x00, sizeof(FT_StreamRec));
	_pStreamRec->descriptor.pointer = this;
	_pStreamRec->size = 0x7fffffffL;
	_pStreamRec->pos = 0;
	_pStreamRec->base = NULL;
	_pStreamRec->read = ReadStub;
	_pStreamRec->close = NULL;
	FT_Open_Args ftargs;
	::memset(&ftargs, 0x00, sizeof(ftargs));
	ftargs.flags = FT_OPEN_STREAM;
	ftargs.stream = _pStreamRec;
	FT_Error err = ::FT_Open_Face(g_lib, &ftargs, index, aface);
	if (err) {
		sig.SetError(ERR_IOError, "font open error");
		return false;
	}
	return true;
}

FT_ULong Handler::Read(FT_ULong pos, FT_Byte *buffer, FT_ULong count)
{
	if (!_pStream->Seek(_sig, pos, Stream::SeekSet)) return 0;
	if (count == 0) return 0;
	//::printf("read %08x %d bytes\n", pos, count);
	return static_cast<FT_ULong>(_pStream->Read(_sig, buffer, count));
}

FT_ULong Handler::ReadStub(FT_Stream streamFT,
						FT_ULong pos, FT_Byte *buffer, FT_ULong count)
{
	Handler *pHandler = reinterpret_cast<Handler *>(streamFT->descriptor.pointer);
	return pHandler->Read(pos, buffer, count);
}

//-----------------------------------------------------------------------------
// Object_Face declaration
//-----------------------------------------------------------------------------
AScript_DeclarePrivClass(Face);

class Object_Face : public Object {
public:
	AScript_DeclareObjectAccessor(Face)
private:
	Handler *_pHandler;
	FT_Face _face;
	Color _color;
	unsigned char _alpha;
	struct {
		double strength;
		double slant;
		struct {
			double cosNum, sinNum;
		} rotate;
	} _deco;
public:
	inline Object_Face() : Object(AScript_PrivClass(Face)),
								_pHandler(NULL), _face(NULL), _alpha(255) {
		ClearDeco();
	}
	virtual ~Object_Face();
	virtual Object *Clone() const;
	virtual Value DoPropGet(Signal sig, const Symbol *pSymbol, bool &evaluatedFlag);
	virtual String ToString(Signal sig, bool exprFlag);
	inline void SetColor(const Color &color) { _color = color; }
	inline void SetAlpha(unsigned char alpha) { _alpha = alpha; }
	inline const Color &GetColor() const { return _color; }
	inline unsigned char GetAlpha() const { return _alpha; }
	inline FT_Face &GetFace() { return _face; }
	inline void ClearDeco() {
		_deco.strength = 0;
		_deco.slant = 0;
		_deco.rotate.cosNum = 1., _deco.rotate.sinNum = 0;
	}
	bool Initialize(Signal sig, const char *pathName, int index);
	bool Initialize(Signal sig, Stream *pSstream, int index);
	bool SetPixelSizes(Signal sig, size_t width, size_t height);
	bool CalcSize(Signal sig, const String &str, size_t &width, size_t &height);
	bool DrawOnImage(Signal sig,
				Object_Image *pObjImage, int x, int y, const String &str);
	inline void SetStrength(double strength) { _deco.strength = strength; }
	inline void SetSlant(double slant) { _deco.slant = slant; }
	inline void SetRotate(double degree) {
		double rad = degree * 3.14159265358979323846 / 180;
		_deco.rotate.cosNum = ::cos(rad);
		_deco.rotate.sinNum = ::sin(rad);
	}
private:
	FT_GlyphSlot LoadChar(unsigned long codeUTF32);
	void DrawMonoOnImage(Object_Image *pObjImage, int x, int y,
				unsigned char *buffer, int width, int height, int pitch,
				int xOffset, int yOffset);
	void DrawGrayOnImage(Object_Image *pObjImage, int x, int y,
				unsigned char *buffer, int width, int height, int pitch,
				int xOffset, int yOffset);
};

//-----------------------------------------------------------------------------
// Object_Face implementation
//-----------------------------------------------------------------------------
Object_Face::~Object_Face()
{
	delete _pHandler;
	::FT_Done_Face(_face);
}

Object *Object_Face::Clone() const
{
	return NULL; //new Object_Face(*this);
}

Value Object_Face::DoPropGet(Signal sig, const Symbol *pSymbol, bool &evaluatedFlag)
{
	Environment &env = *this;
	evaluatedFlag = true;
	double y_ppem = _face->size->metrics.y_ppem;// pixels/EM
	double units_per_EM = _face->units_per_EM;	// fontUnits/EM (typically 2048 or 1000)
	if (pSymbol->IsIdentical(AScript_PrivSymbol(num_faces))) {
		return Value(_face->num_faces);
	} else if (pSymbol->IsIdentical(AScript_PrivSymbol(face_index))) {
		return Value(_face->face_index);
	} else if (pSymbol->IsIdentical(AScript_PrivSymbol(face_flag_scalable))) {
		return Value((_face->face_flags & FT_FACE_FLAG_SCALABLE)? true : false);
	} else if (pSymbol->IsIdentical(AScript_PrivSymbol(face_flag_fixed_width))) {
		return Value((_face->face_flags & FT_FACE_FLAG_FIXED_WIDTH)? true : false);
	} else if (pSymbol->IsIdentical(AScript_PrivSymbol(face_flag_sfnt))) {
		return Value((_face->face_flags & FT_FACE_FLAG_SFNT)? true : false);
	} else if (pSymbol->IsIdentical(AScript_PrivSymbol(face_flag_horizontal))) {
		return Value((_face->face_flags & FT_FACE_FLAG_HORIZONTAL)? true : false);
	} else if (pSymbol->IsIdentical(AScript_PrivSymbol(face_flag_vertical))) {
		return Value((_face->face_flags & FT_FACE_FLAG_VERTICAL)? true : false);
	} else if (pSymbol->IsIdentical(AScript_PrivSymbol(face_flag_kerning))) {
		return Value((_face->face_flags & FT_FACE_FLAG_KERNING)? true : false);
	} else if (pSymbol->IsIdentical(AScript_PrivSymbol(face_flag_fast_glyphs))) {
		return Value((_face->face_flags & FT_FACE_FLAG_FAST_GLYPHS)? true : false);
	} else if (pSymbol->IsIdentical(AScript_PrivSymbol(face_flag_multiple_masters))) {
		return Value((_face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS)? true : false);
	} else if (pSymbol->IsIdentical(AScript_PrivSymbol(face_flag_glyph_names))) {
		return Value((_face->face_flags & FT_FACE_FLAG_GLYPH_NAMES)? true : false);
	} else if (pSymbol->IsIdentical(AScript_PrivSymbol(face_flag_external_stream))) {
		return Value((_face->face_flags & FT_FACE_FLAG_EXTERNAL_STREAM)? true : false);
	} else if (pSymbol->IsIdentical(AScript_PrivSymbol(face_flag_hinter))) {
		return Value((_face->face_flags & FT_FACE_FLAG_HINTER)? true : false);
	} else if (pSymbol->IsIdentical(AScript_PrivSymbol(face_flag_cid_keyed))) {
		return Value::Null;
		//return Value((_face->face_flags & FT_FACE_FLAG_CID_KEYED)? true : false);
	} else if (pSymbol->IsIdentical(AScript_PrivSymbol(face_flag_tricky))) {
		return Value::Null;
		//return Value((_face->face_flags & FT_FACE_FLAG_TRICKY)? true : false);
	} else if (pSymbol->IsIdentical(AScript_PrivSymbol(style_flag_italic))) {
		return Value((_face->style_flags & FT_STYLE_FLAG_ITALIC)? true : false);
	} else if (pSymbol->IsIdentical(AScript_PrivSymbol(style_flag_bold))) {
		return Value((_face->style_flags & FT_STYLE_FLAG_BOLD)? true : false);
	} else if (pSymbol->IsIdentical(AScript_PrivSymbol(family_name))) {
		return Value(env, _face->family_name);
	} else if (pSymbol->IsIdentical(AScript_PrivSymbol(style_name))) {
		if (_face->style_name == NULL) return Value::Null;
		return Value(env, _face->style_name);
	} else if (pSymbol->IsIdentical(AScript_PrivSymbol(bbox))) {
		Value value;
		ValueList &valList = value.InitAsList(env);
		valList.push_back(Value(_face->bbox.xMin));
		valList.push_back(Value(_face->bbox.yMin));
		valList.push_back(Value(_face->bbox.xMax));
		valList.push_back(Value(_face->bbox.yMax));
		return value;
	} else if (pSymbol->IsIdentical(AScript_PrivSymbol(ascender))) {	// fontUnits
		return Value(_face->ascender * y_ppem / units_per_EM);
	} else if (pSymbol->IsIdentical(AScript_PrivSymbol(descender))) {	// fontUnits
		return Value(_face->descender * y_ppem / units_per_EM);
	} else if (pSymbol->IsIdentical(AScript_PrivSymbol(height))) {		// fontUnits
		return Value(_face->height * y_ppem / units_per_EM);
	}
	evaluatedFlag = false;
	return Value::Null;
}

String Object_Face::ToString(Signal sig, bool exprFlag)
{
	String str;
	str = "<face";
	str += ":";
	str += _face->family_name;
	if (_face->style_name != NULL) {
		str += ":";
		str += _face->style_name;
	}
	str += ":#";
	str += NumberToString(_face->face_index);
	str += ":";
	str += NumberToString(_face->num_faces);
	str += "faces";
	str += ">";
	return str;
}

bool Object_Face::Initialize(Signal sig, const char *pathName, int index)
{
	FT_Error err = ::FT_New_Face(g_lib, pathName, index, &_face);
	if (err) {
		sig.SetError(ERR_IOError, "error occurs in FT_New_Face()");
		return false;
	}
	return true;
}

bool Object_Face::Initialize(Signal sig, Stream *pStream, int index)
{
	_pHandler = new Handler(sig, Stream::Reference(pStream));
	return _pHandler->OpenFace(sig, index, &_face);
}

bool Object_Face::SetPixelSizes(Signal sig, size_t width, size_t height)
{
	FT_Error err = ::FT_Set_Pixel_Sizes(_face,
				static_cast<FT_UInt>(width), static_cast<FT_UInt>(height));
	if (err) {
		sig.SetError(ERR_IOError, "error occurs in FT_Set_Pixel_Sizes()");
		return false;
	}
	return true;
}

bool Object_Face::CalcSize(Signal sig, const String &str, size_t &width, size_t &height)
{
	int x = 0, y = 0;
	String::const_iterator p = str.begin();
	int xMin = 0, xMax = 0, yMin = 0, yMax = 0;
	do {
		unsigned long codeUTF32;
		p = AScript::NextUTF32(str, p, codeUTF32);
		if (codeUTF32 == 0x00000000) break;
		FT_GlyphSlot slot = LoadChar(codeUTF32);
		if (slot == NULL) continue;
		FT_Bitmap &bitmap = slot->bitmap;
		int xLeft = x + slot->bitmap_left;
		int yTop = y - slot->bitmap_top;
		int xRight = xLeft + bitmap.width;
		int yBottom = yTop + bitmap.rows;
		if (xMin > xLeft) xMin = xLeft;
		if (xMax < xRight) xMax = xRight;
		if (yMin > yTop) yMin = yTop;
		if (yMax < yBottom) yMax = yBottom;
		x += slot->advance.x >> 6;	// 26.6 fixed float
		y += slot->advance.y >> 6;	// 26.6 fixed float
	} while (p != str.end());
	width = xMax - xMin, height = yMax - yMin;
	return true;
}

bool Object_Face::DrawOnImage(Signal sig,
				Object_Image *pObjImage, int x, int y, const String &str)
{
	unsigned long redFg = _color.GetRed();
	unsigned long greenFg = _color.GetGreen();
	unsigned long blueFg = _color.GetBlue();
	String::const_iterator p = str.begin();
	do {
		unsigned long codeUTF32;
		p = AScript::NextUTF32(str, p, codeUTF32);
		if (codeUTF32 == 0x00000000) break;
		FT_GlyphSlot slot = LoadChar(codeUTF32);
		if (slot == NULL) continue;
		if (slot->format == FT_GLYPH_FORMAT_BITMAP) {
			FT_Bitmap &bitmap = slot->bitmap;
			int xOrg = x + slot->bitmap_left;
			int yOrg = y - slot->bitmap_top;
			int xAdj = xOrg, yAdj = yOrg;
			int width = bitmap.width;
			int height = bitmap.rows;
			if (bitmap.buffer == NULL) {
				// nothing to do
			} else if (!pObjImage->AdjustCoord(xAdj, yAdj, width, height)) {
				// nothing to do
			} else if (bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
				DrawMonoOnImage(pObjImage, xAdj, yAdj, bitmap.buffer,
							width, height, bitmap.pitch, xAdj - xOrg, yAdj - yOrg);
			} else if (bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) {
				DrawGrayOnImage(pObjImage, xAdj, yAdj, bitmap.buffer,
							width, height, bitmap.pitch, xAdj - xOrg, yAdj - yOrg);
			}
		}
		x += slot->advance.x >> 6;	// 26.6 fixed float
		y += slot->advance.y >> 6;	// 26.6 fixed float
	} while (p != str.end());
	return true;
}

FT_GlyphSlot Object_Face::LoadChar(unsigned long codeUTF32)
{
	bool transformFlag = false;
	FT_Matrix matrix;	// 16.16 fixed float
	matrix.xx = 1 << 16;
	matrix.xy = 0;
	matrix.yx = 0;
	matrix.yy = 1 << 16;
	// slanting the font
	if (_deco.slant != 0.) {
		transformFlag = true;
		matrix.xy = static_cast<FT_Fixed>(_deco.slant * (1 << 16));
	}
	// rotating the font
	if (_deco.rotate.sinNum != 0.) {
		transformFlag = true;
		FT_Matrix matRot;
		int cos16 = static_cast<int>(_deco.rotate.cosNum * (1 << 16));
		int sin16 = static_cast<int>(_deco.rotate.sinNum * (1 << 16));
		matRot.xx = cos16;
		matRot.xy = -sin16;
		matRot.yx = sin16;
		matRot.yy = cos16;
		::FT_Matrix_Multiply(&matRot, &matrix);
	}
	// FT_Load_Char calls FT_Get_Char_Index and FT_Load_Glypy internally.
	FT_Error err = ::FT_Load_Char(_face, codeUTF32,
					FT_LOAD_DEFAULT | (transformFlag? FT_LOAD_NO_BITMAP : 0));
	if (err) return NULL;
	FT_GlyphSlot slot = _face->glyph;
	if (_deco.strength == 0.) {
		// nothing to do
	} else if (slot->format == FT_GLYPH_FORMAT_NONE) {
		// nothing to do
	} else if (slot->format == FT_GLYPH_FORMAT_COMPOSITE) {
		// nothing to do
	} else if (slot->format == FT_GLYPH_FORMAT_BITMAP) {
		FT_Pos xStrength = static_cast<FT_Pos>(_deco.strength * (1 << 6)); // 26.6 fixed float
		FT_Pos yStrength = 0;
		err = ::FT_Bitmap_Embolden(g_lib, &slot->bitmap, xStrength, yStrength);
		if (err) return NULL;
	} else if (slot->format == FT_GLYPH_FORMAT_OUTLINE) {
		FT_Pos strength = static_cast<FT_Pos>(_deco.strength * (1 << 6)); // 26.6 fixed float
		err = ::FT_Outline_Embolden(&slot->outline, strength);
		if (err) return NULL;
	} else if (slot->format == FT_GLYPH_FORMAT_PLOTTER) {
		// nothing to do
	}
	if (slot->format == FT_GLYPH_FORMAT_NONE) {
		// nothing to do
	} else if (slot->format == FT_GLYPH_FORMAT_COMPOSITE) {
		// nothing to do
	} else if (slot->format == FT_GLYPH_FORMAT_BITMAP) {
		// nothing to do
	} else if (slot->format == FT_GLYPH_FORMAT_OUTLINE) {
		if (transformFlag) {
			::FT_Outline_Transform(&slot->outline, &matrix);
		}
		// convert outline to bitmap
		err = ::FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL);
		if (err) return NULL;
		if (_deco.rotate.sinNum != 0.) {
			double cosNum = _deco.rotate.cosNum;
			double sinNum = -_deco.rotate.sinNum;
			FT_Pos x = slot->advance.x, y = slot->advance.y;
			slot->advance.x = static_cast<FT_Pos>(cosNum * x - sinNum * y);
			slot->advance.y = static_cast<FT_Pos>(sinNum * x + cosNum * y);
		}
	} else if (slot->format == FT_GLYPH_FORMAT_PLOTTER) {
		// nothing to do
	}
	return slot;
}

void Object_Face::DrawMonoOnImage(Object_Image *pObjImage, int x, int y,
				unsigned char *buffer, int width, int height, int pitch,
				int xOffset, int yOffset)
{
	unsigned char red = _color.GetRed();
	unsigned char green = _color.GetGreen();
	unsigned char blue = _color.GetBlue();
	std::auto_ptr<Object_Image::Scanner>
				pScanner(pObjImage->CreateScanner(x, y, width, height));
	int bitOffset = xOffset % 8;
	unsigned char mask = 1 << (7 - bitOffset);
	const unsigned char *pLine = buffer + (xOffset / 8) + yOffset * pitch;
	const unsigned char *pPixel = pLine;
	bool alphaFlag = (pObjImage->GetFormat() == Object_Image::FORMAT_RGBA);
	for (;;) {
		if (!(*pPixel & mask)) {
			// nothing to do
		} else if (alphaFlag) {
			pScanner->StorePixel(red, green, blue, _alpha);
		} else {
			pScanner->StorePixel(red, green, blue);
		}
		if (pScanner->NextPixel()) {
			mask >>= 1;
			if (mask == 0x00) {
				mask = 1 << 7;
				pPixel++;
			}
		} else if (pScanner->NextLine()) {
			pLine += pitch;
			pPixel = pLine;
			mask = 1 << (7 - bitOffset);
		} else {
			break;
		}
	}
}

void Object_Face::DrawGrayOnImage(Object_Image *pObjImage, int x, int y,
				unsigned char *buffer, int width, int height, int pitch,
				int xOffset, int yOffset)
{
	unsigned long redFg = _color.GetRed();
	unsigned long greenFg = _color.GetGreen();
	unsigned long blueFg = _color.GetBlue();
	std::auto_ptr<Object_Image::Scanner>
				pScanner(pObjImage->CreateScanner(x, y, width, height));
	const unsigned char *pLine = buffer + xOffset + yOffset * pitch;
	const unsigned char *pPixel = pLine;
	bool alphaFlag = (pObjImage->GetFormat() == Object_Image::FORMAT_RGBA);
	for (;;) {
		unsigned long ratioFg = *pPixel;
		unsigned long ratioBg = 255 - ratioFg;
		unsigned long redBg = pScanner->GetRed();
		unsigned long greenBg = pScanner->GetGreen();
		unsigned long blueBg = pScanner->GetBlue();
		unsigned long red = redFg * ratioFg + redBg * ratioBg;
		unsigned long green = greenFg * ratioFg + greenBg * ratioBg;
		unsigned long blue = blueFg * ratioFg + blueBg * ratioBg;
		red /= 255, green /= 255, blue /= 255;
		if (alphaFlag) {
			pScanner->StorePixel(
					static_cast<unsigned char>(red),
					static_cast<unsigned char>(green),
					static_cast<unsigned char>(blue),
					_alpha);
		} else {
			pScanner->StorePixel(
					static_cast<unsigned char>(red),
					static_cast<unsigned char>(green),
					static_cast<unsigned char>(blue));
		}
		if (pScanner->NextPixel()) {
			pPixel++;
		} else if (pScanner->NextLine()) {
			pLine += pitch;
			pPixel = pLine;
		} else {
			break;
		}
	}
}

//-----------------------------------------------------------------------------
// AScript interfaces for Object_Face
//-----------------------------------------------------------------------------
// freetype.face#setcolor(color:color)
AScript_DeclareMethod(Face, setcolor)
{
	SetMode(RSLTMODE_Reduce, FLAG_None);
	DeclareArg(env, "color", VTYPE_Color);
}

AScript_ImplementMethod(Face, setcolor)
{
	Object_Face *pSelf = Object_Face::GetSelfObj(args);
	pSelf->SetColor(args.GetColorObj(0)->GetColor());
	return args.GetSelf();
}

// freetype.face#setalpha(alpha:number)
AScript_DeclareMethod(Face, setalpha)
{
	SetMode(RSLTMODE_Reduce, FLAG_None);
	DeclareArg(env, "alpha", VTYPE_Number);
}

AScript_ImplementMethod(Face, setalpha)
{
	Object_Face *pSelf = Object_Face::GetSelfObj(args);
	pSelf->SetAlpha(args.GetUChar(0));
	return args.GetSelf();
}

// freetype.face#setstrength(strength:number)
AScript_DeclareMethod(Face, setstrength)
{
	SetMode(RSLTMODE_Reduce, FLAG_None);
	DeclareArg(env, "strength", VTYPE_Number);
}

AScript_ImplementMethod(Face, setstrength)
{
	Object_Face *pSelf = Object_Face::GetSelfObj(args);
	pSelf->SetStrength(args.GetDouble(0));
	return args.GetSelf();
}

// freetype.face#setslant(slant:number)
AScript_DeclareMethod(Face, setslant)
{
	SetMode(RSLTMODE_Reduce, FLAG_None);
	DeclareArg(env, "slant", VTYPE_Number);
}

AScript_ImplementMethod(Face, setslant)
{
	Object_Face *pSelf = Object_Face::GetSelfObj(args);
	pSelf->SetSlant(args.GetDouble(0));
	return args.GetSelf();
}

// freetype.face#setrotate(degree:number)
AScript_DeclareMethod(Face, setrotate)
{
	SetMode(RSLTMODE_Reduce, FLAG_None);
	DeclareArg(env, "degree", VTYPE_Number);
}

AScript_ImplementMethod(Face, setrotate)
{
	Object_Face *pSelf = Object_Face::GetSelfObj(args);
	pSelf->SetRotate(args.GetDouble(0));
	return args.GetSelf();
}

// freetype.face#setsize(width:number, height:number)
AScript_DeclareMethod(Face, setsize)
{
	SetMode(RSLTMODE_Reduce, FLAG_None);
	DeclareArg(env, "width", VTYPE_Number);
	DeclareArg(env, "height", VTYPE_Number);
}

AScript_ImplementMethod(Face, setsize)
{
	Object_Face *pSelf = Object_Face::GetSelfObj(args);
	if (!pSelf->SetPixelSizes(sig, args.GetSizeT(0), args.GetSizeT(1))) return Value::Null;
	return args.GetSelf();
}

// freetype.face#setheight(height:number)
AScript_DeclareMethod(Face, setheight)
{
	SetMode(RSLTMODE_Reduce, FLAG_None);
	DeclareArg(env, "height", VTYPE_Number);
}

AScript_ImplementMethod(Face, setheight)
{
	Object_Face *pSelf = Object_Face::GetSelfObj(args);
	if (!pSelf->SetPixelSizes(sig, 0, args.GetSizeT(0))) return Value::Null;
	return args.GetSelf();
}

// freetype.face#cleardeco()
AScript_DeclareMethod(Face, cleardeco)
{
	SetMode(RSLTMODE_Reduce, FLAG_None);
}

AScript_ImplementMethod(Face, cleardeco)
{
	Object_Face *pSelf = Object_Face::GetSelfObj(args);
	pSelf->ClearDeco();
	return args.GetSelf();
}

// freetype.face#drawtext(image:image, x:number, y:number, str:string):map:reduce
AScript_DeclareMethod(Face, drawtext)
{
	SetMode(RSLTMODE_Reduce, FLAG_Map);
	DeclareArg(env, "image", VTYPE_Image);
	DeclareArg(env, "x", VTYPE_Number);
	DeclareArg(env, "y", VTYPE_Number);
	DeclareArg(env, "str", VTYPE_String);
	SetHelp("Draws a text on the image.");
}

AScript_ImplementMethod(Face, drawtext)
{
	Object_Face *pObjFace = Object_Face::GetSelfObj(args);
	Object_Image *pObjImage = Object_Image::GetObject(args, 0);
	int x = args.GetInt(1);
	int y = args.GetInt(2);
	const String &str = args.GetStringObj(3)->GetStringSTL();
	if (pObjFace->DrawOnImage(sig, pObjImage, x, y, str)) return Value::Null;
	return args.GetSelf();
}

// freetype.face#calcsize(str:string):map
AScript_DeclareMethod(Face, calcsize)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "str", VTYPE_String);
}

AScript_ImplementMethod(Face, calcsize)
{
	Object_Face *pObjFace = Object_Face::GetSelfObj(args);
	const String &str = args.GetStringObj(0)->GetStringSTL();
	size_t width, height;
	if (!pObjFace->CalcSize(sig, str, width, height)) return Value::Null;
	Value result;
	ValueList &valList = result.InitAsList(env);
	valList.push_back(Value(static_cast<unsigned int>(width)));
	valList.push_back(Value(static_cast<unsigned int>(height)));
	return result;
}

// freetype.face#calcbbox(x:number, y:number, str:string):map
AScript_DeclareMethod(Face, calcbbox)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "x", VTYPE_Number);
	DeclareArg(env, "y", VTYPE_Number);
	DeclareArg(env, "str", VTYPE_String);
}

// ******************* still buggy
AScript_ImplementMethod(Face, calcbbox)
{
	Object_Face *pObjFace = Object_Face::GetSelfObj(args);
	int x = args.GetInt(0);
	int y = args.GetInt(1);
	const String &str = args.GetStringObj(2)->GetStringSTL();
	size_t width, height;
	if (!pObjFace->CalcSize(sig, str, width, height)) return Value::Null;
	Value result;
	ValueList &valList = result.InitAsList(env);
	valList.push_back(Value(x));
	valList.push_back(Value(y - pObjFace->GetFace()->ascender / 4));
	valList.push_back(Value(static_cast<unsigned int>(width)));
	valList.push_back(Value(static_cast<unsigned int>(height)));
	return result;
}

// implementation of class Face
AScript_ImplementPrivClass(Face)
{
	AScript_AssignMethod(Face, setcolor);
	AScript_AssignMethod(Face, setalpha);
	AScript_AssignMethod(Face, setstrength);
	AScript_AssignMethod(Face, setslant);
	AScript_AssignMethod(Face, setrotate);
	AScript_AssignMethod(Face, setsize);
	AScript_AssignMethod(Face, setheight);
	AScript_AssignMethod(Face, cleardeco);
	AScript_AssignMethod(Face, drawtext);
	AScript_AssignMethod(Face, calcsize);
	AScript_AssignMethod(Face, calcbbox);
}

//-----------------------------------------------------------------------------
// AScript interfaces for Object_Image
// These methods are available after importing freetype module.
//-----------------------------------------------------------------------------
// image#drawtext(face:face, x:number, y:number, str:string):map:reduce
AScript_DeclareMethod(Image, drawtext)
{
	SetMode(RSLTMODE_Reduce, FLAG_Map);
	DeclareArg(env, "face", AScript_PrivVTYPE(Face));
	DeclareArg(env, "x", VTYPE_Number);
	DeclareArg(env, "y", VTYPE_Number);
	DeclareArg(env, "str", VTYPE_String);
	SetHelp("Draws a text on the image.");
}

AScript_ImplementMethod(Image, drawtext)
{
	Object_Image *pObjImage = Object_Image::GetSelfObj(args);
	Object_Face *pObjFace = Object_Face::GetObject(args, 0);
	int x = args.GetInt(1);
	int y = args.GetInt(2);
	const String &str = args.GetStringObj(3)->GetStringSTL();
	if (pObjFace->DrawOnImage(sig, pObjImage, x, y, str)) return Value::Null;
	return args.GetSelf();
}

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

AScript_ImplementFunction(test)
{
	//Handler handler(sig, args.GetStream(0));
	//FT_Face face;
	//handler.OpenFace(sig, 0, &face);
	//printf("%s\n", face->family_name);
#if 0
	FT_Face face;
	FT_Error err = ::FT_New_Face(g_lib, "arial.ttf", 0, &face);
	
	FT_Stream streamFT = new FT_StreamRec;
	
	Handler handler(sig, args.GetStream(0));
	::memset(streamFT, 0x00, sizeof(*streamFT));
	streamFT->descriptor.pointer = &handler;
	streamFT->size = 0x7fffffffL;
	streamFT->pos = 0;
	streamFT->base = NULL;
	streamFT->read = Handler::ReadStub;
	streamFT->close = NULL;
	FT_Open_Args ftargs;
	::memset(&ftargs, 0x00, sizeof(ftargs));
	ftargs.flags = FT_OPEN_STREAM;
	ftargs.stream = streamFT;
	err = ::FT_Open_Face(g_lib, &ftargs, 0, &face);
	
	delete streamFT;
	::printf("err: %d\n", err);
#endif
#if 0
	String pathName = GetSysFontPathName();
	Directory *pDirectoryParent = Directory::OpenDirectory(
						env, sig, pathName.c_str(), Directory::NF_Signal);
	Directory *pDirectory = NULL;
	while ((pDirectory = pDirectoryParent->Next(env, sig)) != NULL) {
		if (pDirectory->IsMatchName("*.ttf", true) ||
								pDirectory->IsMatchName("*.ttc", true)) {
			FT_Face face;
			String pathName = pDirectory->MakePathName(false);
			FT_Error err = ::FT_New_Face(g_lib, pathName.c_str(), 0, &face);
			if (err) continue;	// just ignore errored one
			for (int i = 0; i < face->num_faces; i++) {
				::printf("%d %-20s %-20s %08x %08x\n", i,
					face->family_name, face->style_name,
					face->face_flags, face->style_flags);
			}
			::FT_Done_Face(face);
		}
	}
#endif
	return Value::Null;
}

#if 0
	FT_Error err;
	FT_Face face;
	err = ::FT_New_Face(g_lib, "", 0, &face);
	//err = ::FT_Set_Char_Size(face, 0, 16 * 64, 300, 300);
	err = ::FT_Set_Pixel_Sizes(face, 0, 16);
	FT_GlyphSlot slot = face->glyph;
	FT_ULong codeUTF32 = 0x00000000;
	//FT_UInt glyph_index = ::FT_Get_Char_Index(face, codeUTF32);
	//err = ::FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
	//err = ::FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
	err = ::FT_Load_Char(face, codeUTF32, FT_LOAD_RENDER);
	FT_Bitmap &bitmap = slot->bitmap;
	bitmap.rows;
	bitmap.width;
	bitmap.pitch;
	bitmap.buffer;
	slot->bitmap_left;
	slot->bitmap_top;
	slot->advance.x;
	slot->advance.y;
	//err = ::FT_New_Memory_Face(g_lib, buffer, size, 0, &face);
	//FT_Open_Args args;
	//err = ::FT_Open_Face(g_lib, &args, face_index, &face);
	::FT_Done_Face(face);
	return Value::Null;
#endif

// freetype.open(name:string, index:number => 0):map
AScript_DeclareFunction(open)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "name", VTYPE_String);
	DeclareArg(env, "index", VTYPE_Number,
						OCCUR_Once, false, false, new Expr_Value(0));
}

AScript_ImplementFunction(open)
{
	Object_Face *pObjFace = new Object_Face();
	const char *name = args.GetString(0);
	int index = args.GetInt(1);
	if (*name == '\0') {
		sig.SetError(ERR_ValueError, "specify font filename");
		return Value::Null;
	}
	String pathName = name;
	if (!Directory::IsExist(env, sig, pathName.c_str())) {
		bool foundFlag = false;
		pathName = GetSysFontPathName();
		if (!pathName.empty()) {
			OAL::JoinPathName(pathName, name);
			foundFlag = Directory::IsExist(env, sig, pathName.c_str());
		}
		if (!foundFlag) {
			sig.SetError(ERR_ResourceError, "can't find a font file %s", name);
			return Value::Null;
		}
	}
	Stream *pStream = Directory::OpenStream(env, sig,
							pathName.c_str(), Stream::ATTR_Readable, NULL);
	if (sig.IsSignalled()) return Value::Null;
	if (pStream->HasNameSuffix(".gz")) {
		ZLib::Stream_Inflater *pStreamInflater =
						new ZLib::Stream_Inflater(sig, pStream, InvalidSize);
		if (!pStreamInflater->Initialize(sig, 31)) return Value::Null;
		pStream = pStreamInflater;
	}
	if (!pStream->IsBwdSeekable()) {
		Stream *pStreamPrefetch = Stream::Prefetch(sig, pStream, true);
		if (sig.IsSignalled()) return Value::Null;
		pStream = pStreamPrefetch;
	}
	pObjFace->Initialize(sig, pStream, index);
	if (sig.IsSignalled()) return Value::Null;
	return Value(pObjFace);
}

// Module entry
AScript_ModuleEntry()
{
	::FT_Init_FreeType(&g_lib);
	// symbol realization
	AScript_RealizePrivSymbol(num_faces);
	AScript_RealizePrivSymbol(face_index);
	AScript_RealizePrivSymbol(face_flag_scalable);
	AScript_RealizePrivSymbol(face_flag_fixed_width);
	AScript_RealizePrivSymbol(face_flag_sfnt);
	AScript_RealizePrivSymbol(face_flag_horizontal);
	AScript_RealizePrivSymbol(face_flag_vertical);
	AScript_RealizePrivSymbol(face_flag_kerning);
	AScript_RealizePrivSymbol(face_flag_fast_glyphs);
	AScript_RealizePrivSymbol(face_flag_multiple_masters);
	AScript_RealizePrivSymbol(face_flag_glyph_names);
	AScript_RealizePrivSymbol(face_flag_external_stream);
	AScript_RealizePrivSymbol(face_flag_hinter);
	AScript_RealizePrivSymbol(face_flag_cid_keyed);
	AScript_RealizePrivSymbol(face_flag_tricky);
	AScript_RealizePrivSymbol(style_flag_italic);
	AScript_RealizePrivSymbol(style_flag_bold);
	AScript_RealizePrivSymbol(family_name);
	AScript_RealizePrivSymbol(style_name);
	AScript_RealizePrivSymbol(bbox);
	AScript_RealizePrivSymbol(ascender);
	AScript_RealizePrivSymbol(descender);
	AScript_RealizePrivSymbol(height);
	// class realization
	AScript_RealizePrivClass(Face, "face", env.LookupClass(VTYPE_Object));
	// method assignment to image type
	AScript_AssignMethodTo(VTYPE_Image, Image, drawtext);
	// function assignment
	AScript_AssignFunction(test);
	AScript_AssignFunction(open);
}

AScript_ModuleTerminate()
{
	::FT_Done_FreeType(g_lib);
}

#if defined(HAVE_WINDOWS_H)
String GetSysFontPathName()
{
	char pathName[MAX_PATH];
	if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_FONTS, NULL, 0, pathName))) {
		return String(pathName);
	}
	return String("");
}
#else
String GetSysFontPathName()
{
	return String("");
}
#endif


AScript_EndModule(freetype, freetype)

AScript_RegisterModule(freetype)
