//-----------------------------------------------------------------------------
// AScript scribble module
//-----------------------------------------------------------------------------
#include <ascript.h>
#include "Application.h"

AScript_BeginModule(scribble)

//-----------------------------------------------------------------------------
// local properties
//-----------------------------------------------------------------------------
static struct {
	int burstLevel;
	wxPen *pPen;
	wxBrush *pBrush;
	wxFont *pFont;
	wxColour *pColorText;
} g_prop = {
	0, NULL, NULL, NULL, NULL,
};

AScript_DeclarePrivSymbol(fill)
AScript_DeclarePrivSymbol(solid)
AScript_DeclarePrivSymbol(dot)
AScript_DeclarePrivSymbol(long_dash)
AScript_DeclarePrivSymbol(short_dash)
AScript_DeclarePrivSymbol(dot_dash)
AScript_DeclarePrivSymbol(transparent)
AScript_DeclarePrivSymbol(stipple)
AScript_DeclarePrivSymbol(bdiagonal)
AScript_DeclarePrivSymbol(crossdiag)
AScript_DeclarePrivSymbol(fdiagonal)
AScript_DeclarePrivSymbol(cross)
AScript_DeclarePrivSymbol(horizontal)
AScript_DeclarePrivSymbol(vertical)

AScript_DeclarePrivSymbol(normal);
AScript_DeclarePrivSymbol(default);
AScript_DeclarePrivSymbol(decorative);
AScript_DeclarePrivSymbol(roman);
AScript_DeclarePrivSymbol(script);
AScript_DeclarePrivSymbol(swiss);
AScript_DeclarePrivSymbol(modern);
AScript_DeclarePrivSymbol(teletype);
AScript_DeclarePrivSymbol(slant);
AScript_DeclarePrivSymbol(italic);
AScript_DeclarePrivSymbol(light);
AScript_DeclarePrivSymbol(bold);

AScript_DeclarePrivSymbol(n);
AScript_DeclarePrivSymbol(ne);
AScript_DeclarePrivSymbol(e);
AScript_DeclarePrivSymbol(se);
AScript_DeclarePrivSymbol(s);
AScript_DeclarePrivSymbol(sw);
AScript_DeclarePrivSymbol(w);
AScript_DeclarePrivSymbol(nw);
AScript_DeclarePrivSymbol(c);

inline PanelScribble *GetPanelScribble()
{
	return Application::GetInstance()->GetFrameScribble()->GetPanelScribble();
}

inline void ConditionalRefresh(PanelScribble *pPanelScribble)
{
	if (g_prop.burstLevel == 0) {
		pPanelScribble->Refresh();
		pPanelScribble->Update();
	}
}

static wxPen *CreatePen(Signal sig, const char *colorName, int width, const Symbol *pSymbolStyle);
static wxBrush *CreateBrush(Signal sig, const char *colorName, const Symbol *pSymbolStyle);
static wxFont *CreateFont(Signal sig, int pointSize, const Symbol *pSymbolFamily,
	const Symbol *pSymbolStyle, const Symbol *pSymbolWeight, const char *faceName);
static void DeclareAnchorAttrs(Function *pFunc);
static wxRect GetAnchoredRect(int x, int y, int width, int height, const SymbolSet &symbolSet);

//-----------------------------------------------------------------------------
// AScript module functions: scribble
//-----------------------------------------------------------------------------
// scribble.show(flag:boolean => true)
AScript_DeclareFunction(show)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "flag", VTYPE_Boolean, OCCUR_Once, false, false, new Expr_Value(true));
	SetHelp("Shows or hides the scribble window according to the specified flag.");
}

AScript_ImplementFunction(show)
{
	FrameScribble *pFrameScribble = Application::GetInstance()->GetFrameScribble();
	pFrameScribble->ShowEx(args.GetBoolean(0));
	return Value::Null;
}

// scribble.isshown()
AScript_DeclareFunction(isshown)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	SetHelp("Returns true if the scribble window is shown.");
}

AScript_ImplementFunction(isshown)
{
	FrameScribble *pFrameScribble = Application::GetInstance()->GetFrameScribble();
	return Value(pFrameScribble->IsShown()? true : false);
}

// scribble.getsize()
AScript_DeclareFunction(getsize)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	SetHelp(
	"Returns a list containing width and height values of a drawable area\n"
	"in the scribble window.");
}

AScript_ImplementFunction(getsize)
{
	PanelScribble *pPanelScribble = GetPanelScribble();
	const wxBitmap *pBmp = pPanelScribble->GetBitmap();
	if (pBmp == NULL) return Value::Null;
	Value result;
	ValueList &valList = result.InitAsList(env);
	valList.push_back(pBmp->GetWidth());
	valList.push_back(pBmp->GetHeight());
	return result;
}

// scribble.refresh()
AScript_DeclareFunction(refresh)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	SetHelp("Refreshes the content of the scribble window.");
}

AScript_ImplementFunction(refresh)
{
	PanelScribble *pPanelScribble = GetPanelScribble();
	pPanelScribble->Refresh();
	pPanelScribble->Update();
	return Value::Null;
}

// scribble.burst() {block}
AScript_DeclareFunction(burst)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareBlock(OCCUR_Once);
	SetHelp(
	"Forms a burst section that delays the actual process of drawing instructions\n"
	"until the section ends. Burst sections can be nested and the drawing process\n"
	"shall be posponed to the timing when the outer section ends.");
}

AScript_ImplementFunction(burst)
{
	const Expr_Block *pExprBlock = args.GetBlock(env, sig);
	if (sig.IsSignalled()) return Value::Null;
	PanelScribble *pPanelScribble = GetPanelScribble();
	g_prop.burstLevel++;
	Value result = pExprBlock->Exec(env, sig);
	g_prop.burstLevel--;
	ConditionalRefresh(pPanelScribble);
	return result;
}

// scribble.clear()
AScript_DeclareFunction(clear)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	SetHelp("Clears a drawable area in the scribble window.");
}

AScript_ImplementFunction(clear)
{
	PanelScribble *pPanelScribble = GetPanelScribble();
	do {
		wxBitmap *pBmp = pPanelScribble->GetBitmap();
		if (pBmp == NULL) return Value::Null;
		wxMemoryDC dc(*pBmp);
		dc.SetBrush(*wxWHITE_BRUSH);
		dc.Clear();
	} while (0);
	ConditionalRefresh(pPanelScribble);
	return Value::Null;
}

// scribble.setpen(color:string, width:number => 1, style:symbol => `solid):void
AScript_DeclareFunction(setpen)
{
	SetMode(RSLTMODE_Void, FLAG_None);
	DeclareArg(env, "color", VTYPE_String);
	DeclareArg(env, "width", VTYPE_Number, OCCUR_Once, false, false,
							new Expr_Value(1));
	DeclareArg(env, "style", VTYPE_Symbol, OCCUR_Once, false, false,
							new Expr_Symbol(AScript_PrivSymbol(solid)));
	SetHelp("Sets a pen with specified properties.");
}

AScript_ImplementFunction(setpen)
{
	wxPen *pPen = CreatePen(sig, args.GetString(0),
								args.GetInt(1), args.GetSymbol(2));
	if (sig.IsSignalled()) return Value::Null;
	delete g_prop.pPen;
	g_prop.pPen = pPen;
	return Value::Null;
}

// scribble.setbrush(color:string, style:symbol => `solid):void
AScript_DeclareFunction(setbrush)
{
	SetMode(RSLTMODE_Void, FLAG_None);
	DeclareArg(env, "color", VTYPE_String);
	DeclareArg(env, "style", VTYPE_Symbol, OCCUR_Once, false, false,
							new Expr_Symbol(AScript_PrivSymbol(solid)));
	SetHelp("Sets a brush with specified properties.");
}

AScript_ImplementFunction(setbrush)
{
	wxBrush *pBrush = CreateBrush(sig, args.GetString(0), args.GetSymbol(1));
	if (sig.IsSignalled()) return Value::Null;
	delete g_prop.pBrush;
	g_prop.pBrush = pBrush;
	return Value::Null;
}

// scribble.settextcolor(color:string):void
AScript_DeclareFunction(settextcolor)
{
	SetMode(RSLTMODE_Void, FLAG_None);
	DeclareArg(env, "color", VTYPE_String);
	SetHelp("Sets a color to draw texts.");
}

AScript_ImplementFunction(settextcolor)
{
	delete g_prop.pColorText;
	g_prop.pColorText = new wxColour(reinterpret_cast<const wxChar *>(args.GetString(0)));
	return Value::Null;
}

// scribble.setfont(height:number, family:symbol => `default,
//		style:symbol => `normal, weight:symbol => `normal, facename?:string):void
AScript_DeclareFunction(setfont)
{
	SetMode(RSLTMODE_Void, FLAG_None);
	DeclareArg(env, "height", VTYPE_Number);
	DeclareArg(env, "family", VTYPE_Symbol, OCCUR_Once, false, false,
								new Expr_Symbol(AScript_PrivSymbol(default)));
	DeclareArg(env, "style", VTYPE_Symbol, OCCUR_Once, false, false,
								new Expr_Symbol(AScript_PrivSymbol(normal)));
	DeclareArg(env, "weight", VTYPE_Symbol, OCCUR_Once, false, false,
								new Expr_Symbol(AScript_PrivSymbol(normal)));
	DeclareArg(env, "facename", VTYPE_String, OCCUR_ZeroOrOnce);
	SetHelp("Sets a font with specified properties.");
}

AScript_ImplementFunction(setfont)
{
	wxFont *pFont = CreateFont(sig, args.GetNumber(0),
			args.GetSymbol(1), args.GetSymbol(2), args.GetSymbol(3),
			args.IsString(4)? args.GetString(4) : "");
	if (sig.IsSignalled()) return Value::Null;
	delete g_prop.pFont;
	g_prop.pFont = pFont;
	return Value::Null;
}


// scribble.line(x1:number, y1:number, x2:number, y2:number):map:void
AScript_DeclareFunction(line)
{
	SetMode(RSLTMODE_Void, FLAG_Map);
	DeclareArg(env, "x1", VTYPE_Number);
	DeclareArg(env, "y1", VTYPE_Number);
	DeclareArg(env, "x2", VTYPE_Number);
	DeclareArg(env, "y2", VTYPE_Number);
	SetHelp("Draws a line.");
}

AScript_ImplementFunction(line)
{
	PanelScribble *pPanelScribble = GetPanelScribble();
	do {
		wxBitmap *pBmp = pPanelScribble->GetBitmap();
		if (pBmp == NULL) return Value::Null;
		wxMemoryDC dc(*pBmp);
		dc.SetPen(*g_prop.pPen);
		dc.DrawLine(args.GetInt(0), args.GetInt(1), args.GetInt(2), args.GetInt(3));
	} while (0);
	ConditionalRefresh(pPanelScribble);
	return Value::Null;
}

// scribble.lines(x[]:number, y[]:number):void
AScript_DeclareFunction(lines)
{
	SetMode(RSLTMODE_Void, FLAG_None);
	DeclareArg(env, "x", VTYPE_Number, OCCUR_Once, true);
	DeclareArg(env, "y", VTYPE_Number, OCCUR_Once, true);
	SetHelp("Draws lines.");
}

AScript_ImplementFunction(lines)
{
	int xOffset = 0, yOffset = 0;
	int len = static_cast<int>(ChooseMin(args.GetList(0).size(), args.GetList(1).size()));
	if (len == 0) return Value::Null;
	wxPoint *points = new wxPoint[len];
	ValueList::const_iterator px = args.GetList(0).begin();
	ValueList::const_iterator py = args.GetList(1).begin();
	for (int i = 0; i < len; i++, px++, py++) {
		points[i].x = static_cast<wxCoord>(px->GetNumber());
		points[i].y = static_cast<wxCoord>(py->GetNumber());
	}
	PanelScribble *pPanelScribble = GetPanelScribble();
	do {
		wxBitmap *pBmp = pPanelScribble->GetBitmap();
		if (pBmp == NULL) return Value::Null;
		wxMemoryDC dc(*pBmp);
		dc.SetPen(*g_prop.pPen);
		dc.DrawLines(len, points, xOffset, yOffset);
	} while (0);
	delete[] points;
	ConditionalRefresh(pPanelScribble);
	return Value::Null;
}

// scribble.rectangle(x:number, y:number, width:number, height:number):map:void:[fill]
AScript_DeclareFunction(rectangle)
{
	SetMode(RSLTMODE_Void, FLAG_Map);
	DeclareArg(env, "x", VTYPE_Number);
	DeclareArg(env, "y", VTYPE_Number);
	DeclareArg(env, "width", VTYPE_Number);
	DeclareArg(env, "height", VTYPE_Number);
	DeclareAttr(AScript_PrivSymbol(fill));
	DeclareAnchorAttrs(this);
	SetHelp("Draws a rectangle.");
}

AScript_ImplementFunction(rectangle)
{
	PanelScribble *pPanelScribble = GetPanelScribble();
	do {
		wxBitmap *pBmp = pPanelScribble->GetBitmap();
		if (pBmp == NULL) return Value::Null;
		wxMemoryDC dc(*pBmp);
		dc.SetPen(*g_prop.pPen);
		if (args.IsSet(AScript_PrivSymbol(fill))) {
			dc.SetBrush(*g_prop.pBrush);
		} else {
			dc.SetBrush(*wxTRANSPARENT_BRUSH);
		}
		wxRect rc = GetAnchoredRect(args.GetInt(0), args.GetInt(1),
					args.GetInt(2), args.GetInt(3), args.GetAttrs());
		dc.DrawRectangle(rc.x, rc.y, rc.width, rc.height);
	} while (0);
	ConditionalRefresh(pPanelScribble);
	return Value::Null;
}

// scribble.ellipse(x:number, y:number, width:number, height:number):map:void:[fill]
AScript_DeclareFunction(ellipse)
{
	SetMode(RSLTMODE_Void, FLAG_Map);
	DeclareArg(env, "x", VTYPE_Number);
	DeclareArg(env, "y", VTYPE_Number);
	DeclareArg(env, "width", VTYPE_Number);
	DeclareArg(env, "height", VTYPE_Number);
	DeclareAttr(AScript_PrivSymbol(fill));
	DeclareAnchorAttrs(this);
	SetHelp("Draws an ellipse shape.");
}

AScript_ImplementFunction(ellipse)
{
	PanelScribble *pPanelScribble = GetPanelScribble();
	do {
		wxBitmap *pBmp = pPanelScribble->GetBitmap();
		if (pBmp == NULL) return Value::Null;
		wxMemoryDC dc(*pBmp);
		dc.SetPen(*g_prop.pPen);
		if (args.IsSet(AScript_PrivSymbol(fill))) {
			dc.SetBrush(*g_prop.pBrush);
		} else {
			dc.SetBrush(*wxTRANSPARENT_BRUSH);
		}
		wxRect rc = GetAnchoredRect(args.GetInt(0), args.GetInt(1),
					args.GetInt(2), args.GetInt(3), args.GetAttrs());
		dc.DrawEllipse(rc.x, rc.y, rc.width, rc.height);
	} while (0);
	ConditionalRefresh(pPanelScribble);
	return Value::Null;
}

// scribble.text(x:number, y:number, text:string):map:void
AScript_DeclareFunction(text)
{
	SetMode(RSLTMODE_Void, FLAG_Map);
	DeclareArg(env, "x", VTYPE_Number);
	DeclareArg(env, "y", VTYPE_Number);
	DeclareArg(env, "text", VTYPE_String);
	DeclareAnchorAttrs(this);
	SetHelp("Draws a text.");
}

AScript_ImplementFunction(text)
{
	PanelScribble *pPanelScribble = GetPanelScribble();
	do {
		wxBitmap *pBmp = pPanelScribble->GetBitmap();
		if (pBmp == NULL) return Value::Null;
		wxMemoryDC dc(*pBmp);
		dc.SetFont(*g_prop.pFont);
		dc.SetTextForeground(*g_prop.pColorText);
		wxSize size = dc.GetTextExtent(reinterpret_cast<const wxChar *>(args.GetString(2)));
		wxRect rc = GetAnchoredRect(args.GetInt(0), args.GetInt(1),
						size.GetWidth(), size.GetHeight(), args.GetAttrs());
		dc.DrawText(args.GetString(2), rc.x, rc.y);
	} while (0);
	ConditionalRefresh(pPanelScribble);
	return Value::Null;
}

// scribble.textrot(x:number, y:number, text:string, angle:number):map:void
AScript_DeclareFunction(textrot)
{
	SetMode(RSLTMODE_Void, FLAG_Map);
	DeclareArg(env, "x", VTYPE_Number);
	DeclareArg(env, "y", VTYPE_Number);
	DeclareArg(env, "text", VTYPE_String);
	DeclareArg(env, "angle", VTYPE_Number);
	SetHelp("Draws a rotated text.");
}

AScript_ImplementFunction(textrot)
{
	PanelScribble *pPanelScribble = GetPanelScribble();
	do {
		wxBitmap *pBmp = pPanelScribble->GetBitmap();
		if (pBmp == NULL) return Value::Null;
		wxMemoryDC dc(*pBmp);
		dc.SetFont(*g_prop.pFont);
		dc.SetTextForeground(*g_prop.pColorText);
		dc.DrawRotatedText(args.GetString(2),
				args.GetInt(0), args.GetInt(1), args.GetNumber(3));
	} while (0);
	ConditionalRefresh(pPanelScribble);
	return Value::Null;
}

// scribble.putimage(x:number, y:number, image:image):map:void
AScript_DeclareFunction(putimage)
{
	SetMode(RSLTMODE_Void, FLAG_Map);
	DeclareArg(env, "x", VTYPE_Number);
	DeclareArg(env, "y", VTYPE_Number);
	DeclareArg(env, "image", VTYPE_Image);
	SetHelp("Draws an image.");
}

AScript_ImplementFunction(putimage)
{
	PanelScribble *pPanelScribble = GetPanelScribble();
	wxBitmap *pBmp = pPanelScribble->GetBitmap();
	if (pBmp == NULL) return Value::Null;
#if USE_WINDOWS_DIB
	Object_Image *pObjImage = args.GetImageObj(2);
	wxMemoryDC dc(*pBmp);
	wxBitmap bmpSrc;
	bmpSrc.SetHBITMAP(pObjImage->GetHBITMAP());
	bmpSrc.SetDepth(static_cast<int>(pObjImage->GetBitsPerPixel()));
	bmpSrc.SetWidth(static_cast<int>(pObjImage->GetWidth()));
	bmpSrc.SetHeight(static_cast<int>(pObjImage->GetWidth()));
	dc.DrawBitmap(bmpSrc, args.GetInt(0), args.GetInt(1));
	bmpSrc.SetHBITMAP(NULL);
	ConditionalRefresh(pPanelScribble);
#endif
	return Value::Null;
}

// scribble.iskey(keycode:number):map
AScript_DeclareFunction(iskey)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "keycode", VTYPE_Number);
	SetHelp("Returns true if the specified key is currently pressed.");
}

AScript_ImplementFunction(iskey)
{
	PanelScribble *pPanelScribble = GetPanelScribble();
	int keyCode = args.GetNumber(0);
	return Value(pPanelScribble->IsKeyPressed(keyCode));
}

// scribble.glsection():void {block}
AScript_DeclareFunction(glsection)
{
	SetMode(RSLTMODE_Void, FLAG_None);
	DeclareBlock(OCCUR_Once);
}

AScript_ImplementFunction(glsection)
{
	PanelScribble *pPanelScribble = GetPanelScribble();
	do {
		wxBitmap *pBmp = pPanelScribble->GetBitmap();
		if (pBmp == NULL) return Value::Null;
		HBITMAP hBmp = reinterpret_cast<HBITMAP>(pBmp->GetHBITMAP());
		PIXELFORMATDESCRIPTOR pfd = { 
			sizeof(PIXELFORMATDESCRIPTOR), 1,
			PFD_DRAW_TO_BITMAP | PFD_SUPPORT_OPENGL, PFD_TYPE_RGBA,
			pBmp->GetDepth(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
			32, 0, 0, PFD_MAIN_PLANE, 0, 0, 0, 0
		}; 
		HDC hdc = ::CreateCompatibleDC(NULL);
		HBITMAP hBmpOld = reinterpret_cast<HBITMAP>(::SelectObject(hdc, hBmp));
		int iPixelFormat = ::ChoosePixelFormat(hdc, &pfd);
		::SetPixelFormat(hdc, iPixelFormat, &pfd);
		HGLRC hglrc = ::wglCreateContext(hdc);
		::wglMakeCurrent(hdc, hglrc);
		const Expr_Block *pExprBlock = args.GetBlock(env, sig);
		if (!sig.IsSignalled()) {
			pExprBlock->Exec(env, sig);
		}
		::wglMakeCurrent(NULL, NULL);
		::wglDeleteContext(hglrc);
		::SelectObject(hdc, hBmpOld);
		::DeleteDC(hdc);
		ConditionalRefresh(pPanelScribble);
	} while (0);
#if 0
	{
		HWND hwnd = reinterpret_cast<HWND>(pPanelScribble->GetHWND());
		PIXELFORMATDESCRIPTOR pfd = { 
			sizeof(PIXELFORMATDESCRIPTOR), 1,
			PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, PFD_TYPE_RGBA,
			16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
			32, 0, 0, PFD_MAIN_PLANE, 0, 0, 0, 0
		}; 
		HDC hdc = ::GetDC(hwnd);
		int iPixelFormat = ::ChoosePixelFormat(hdc, &pfd);
		::SetPixelFormat(hdc, iPixelFormat, &pfd);
		HGLRC hglrc = ::wglCreateContext(hdc);
		::wglMakeCurrent(hdc, hglrc);
		args.GetBlock()->Exec(env, sig);
		::wglMakeCurrent(NULL, NULL);
		::SwapBuffers(hdc);
		::wglDeleteContext(hglrc);
		::ReleaseDC(hwnd, hdc);
	}
#endif
	return Value::Null;
}

// Module entry
AScript_ModuleEntry()
{
	g_prop.burstLevel = 0;
	g_prop.pPen = new wxPen(*wxBLACK, 1, wxSOLID);
	g_prop.pBrush = new wxBrush(*wxBLACK, wxSOLID);
	g_prop.pFont = new wxFont(10, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL,
										wxFONTWEIGHT_NORMAL, false, wxT(""));
	g_prop.pColorText = new wxColour(*wxBLACK);
	// symbol realization
	AScript_RealizePrivSymbol(fill)
	AScript_RealizePrivSymbol(solid)
	AScript_RealizePrivSymbol(dot)
	AScript_RealizePrivSymbol(long_dash)
	AScript_RealizePrivSymbol(short_dash)
	AScript_RealizePrivSymbol(dot_dash)
	AScript_RealizePrivSymbol(transparent)
	AScript_RealizePrivSymbol(stipple)
	AScript_RealizePrivSymbol(bdiagonal)
	AScript_RealizePrivSymbol(crossdiag)
	AScript_RealizePrivSymbol(fdiagonal)
	AScript_RealizePrivSymbol(cross)
	AScript_RealizePrivSymbol(horizontal)
	AScript_RealizePrivSymbol(vertical)
	AScript_RealizePrivSymbol(normal);
	AScript_RealizePrivSymbol(default);
	AScript_RealizePrivSymbol(decorative);
	AScript_RealizePrivSymbol(roman);
	AScript_RealizePrivSymbol(script);
	AScript_RealizePrivSymbol(swiss);
	AScript_RealizePrivSymbol(modern);
	AScript_RealizePrivSymbol(teletype);
	AScript_RealizePrivSymbol(slant);
	AScript_RealizePrivSymbol(italic);
	AScript_RealizePrivSymbol(light);
	AScript_RealizePrivSymbol(bold);
	AScript_RealizePrivSymbol(n);
	AScript_RealizePrivSymbol(ne);
	AScript_RealizePrivSymbol(e);
	AScript_RealizePrivSymbol(se);
	AScript_RealizePrivSymbol(s);
	AScript_RealizePrivSymbol(sw);
	AScript_RealizePrivSymbol(w);
	AScript_RealizePrivSymbol(nw);
	AScript_RealizePrivSymbol(c);
	// variable assignment
	AScript_AssignValue(WXK_BACK,			WXK_BACK);
	AScript_AssignValue(WXK_TAB,			WXK_TAB);
	AScript_AssignValue(WXK_RETURN,			WXK_RETURN);
	AScript_AssignValue(WXK_ESCAPE,			WXK_ESCAPE);
	AScript_AssignValue(WXK_SPACE,			WXK_SPACE);
	AScript_AssignValue(WXK_DELETE,			WXK_DELETE);
	AScript_AssignValue(WXK_START,			WXK_START);
	AScript_AssignValue(WXK_LBUTTON,		WXK_LBUTTON);
	AScript_AssignValue(WXK_RBUTTON,		WXK_RBUTTON);
	AScript_AssignValue(WXK_CANCEL,			WXK_CANCEL);
	AScript_AssignValue(WXK_MBUTTON,		WXK_MBUTTON);
	AScript_AssignValue(WXK_CLEAR,			WXK_CLEAR);
	AScript_AssignValue(WXK_SHIFT,			WXK_SHIFT);
	AScript_AssignValue(WXK_ALT,			WXK_ALT);
	AScript_AssignValue(WXK_CONTROL,		WXK_CONTROL);
	AScript_AssignValue(WXK_MENU,			WXK_MENU);
	AScript_AssignValue(WXK_PAUSE,			WXK_PAUSE);
	AScript_AssignValue(WXK_CAPITAL,		WXK_CAPITAL);
	AScript_AssignValue(WXK_END,			WXK_END);
	AScript_AssignValue(WXK_HOME,			WXK_HOME);
	AScript_AssignValue(WXK_LEFT,			WXK_LEFT);
	AScript_AssignValue(WXK_UP,				WXK_UP);
	AScript_AssignValue(WXK_RIGHT,			WXK_RIGHT);
	AScript_AssignValue(WXK_DOWN,			WXK_DOWN);
	AScript_AssignValue(WXK_SELECT,			WXK_SELECT);
	AScript_AssignValue(WXK_PRINT,			WXK_PRINT);
	AScript_AssignValue(WXK_EXECUTE,		WXK_EXECUTE);
	AScript_AssignValue(WXK_SNAPSHOT,		WXK_SNAPSHOT);
	AScript_AssignValue(WXK_INSERT,			WXK_INSERT);
	AScript_AssignValue(WXK_HELP,			WXK_HELP);
	AScript_AssignValue(WXK_NUMPAD0,		WXK_NUMPAD0);
	AScript_AssignValue(WXK_NUMPAD1,		WXK_NUMPAD1);
	AScript_AssignValue(WXK_NUMPAD2,		WXK_NUMPAD2);
	AScript_AssignValue(WXK_NUMPAD3,		WXK_NUMPAD3);
	AScript_AssignValue(WXK_NUMPAD4,		WXK_NUMPAD4);
	AScript_AssignValue(WXK_NUMPAD5,		WXK_NUMPAD5);
	AScript_AssignValue(WXK_NUMPAD6,		WXK_NUMPAD6);
	AScript_AssignValue(WXK_NUMPAD7,		WXK_NUMPAD7);
	AScript_AssignValue(WXK_NUMPAD8,		WXK_NUMPAD8);
	AScript_AssignValue(WXK_NUMPAD9,		WXK_NUMPAD9);
	AScript_AssignValue(WXK_MULTIPLY,		WXK_MULTIPLY);
	AScript_AssignValue(WXK_ADD,			WXK_ADD);
	AScript_AssignValue(WXK_SEPARATOR,		WXK_SEPARATOR);
	AScript_AssignValue(WXK_SUBTRACT,		WXK_SUBTRACT);
	AScript_AssignValue(WXK_DECIMAL,		WXK_DECIMAL);
	AScript_AssignValue(WXK_DIVIDE,			WXK_DIVIDE);
	AScript_AssignValue(WXK_F1,				WXK_F1);
	AScript_AssignValue(WXK_F2,				WXK_F2);
	AScript_AssignValue(WXK_F3,				WXK_F3);
	AScript_AssignValue(WXK_F4,				WXK_F4);
	AScript_AssignValue(WXK_F5,				WXK_F5);
	AScript_AssignValue(WXK_F6,				WXK_F6);
	AScript_AssignValue(WXK_F7,				WXK_F7);
	AScript_AssignValue(WXK_F8,				WXK_F8);
	AScript_AssignValue(WXK_F9,				WXK_F9);
	AScript_AssignValue(WXK_F10,			WXK_F10);
	AScript_AssignValue(WXK_F11,			WXK_F11);
	AScript_AssignValue(WXK_F12,			WXK_F12);
	AScript_AssignValue(WXK_F13,			WXK_F13);
	AScript_AssignValue(WXK_F14,			WXK_F14);
	AScript_AssignValue(WXK_F15,			WXK_F15);
	AScript_AssignValue(WXK_F16,			WXK_F16);
	AScript_AssignValue(WXK_F17,			WXK_F17);
	AScript_AssignValue(WXK_F18,			WXK_F18);
	AScript_AssignValue(WXK_F19,			WXK_F19);
	AScript_AssignValue(WXK_F20,			WXK_F20);
	AScript_AssignValue(WXK_F21,			WXK_F21);
	AScript_AssignValue(WXK_F22,			WXK_F22);
	AScript_AssignValue(WXK_F23,			WXK_F23);
	AScript_AssignValue(WXK_F24,			WXK_F24);
	AScript_AssignValue(WXK_NUMLOCK,		WXK_NUMLOCK);
	AScript_AssignValue(WXK_SCROLL,			WXK_SCROLL);
	AScript_AssignValue(WXK_PAGEUP,			WXK_PAGEUP);
	AScript_AssignValue(WXK_PAGEDOWN,		WXK_PAGEDOWN);
	AScript_AssignValue(WXK_NUMPAD_SPACE,	WXK_NUMPAD_SPACE);
	AScript_AssignValue(WXK_NUMPAD_TAB,		WXK_NUMPAD_TAB);
	AScript_AssignValue(WXK_NUMPAD_ENTER, 	WXK_NUMPAD_ENTER);
	AScript_AssignValue(WXK_NUMPAD_F1,		WXK_NUMPAD_F1);
	AScript_AssignValue(WXK_NUMPAD_F2,		WXK_NUMPAD_F2);
	AScript_AssignValue(WXK_NUMPAD_F3,		WXK_NUMPAD_F3);
	AScript_AssignValue(WXK_NUMPAD_F4,		WXK_NUMPAD_F4);
	AScript_AssignValue(WXK_NUMPAD_HOME,	WXK_NUMPAD_HOME);
	AScript_AssignValue(WXK_NUMPAD_LEFT,	WXK_NUMPAD_LEFT);
	AScript_AssignValue(WXK_NUMPAD_UP,		WXK_NUMPAD_UP);
	AScript_AssignValue(WXK_NUMPAD_RIGHT,	WXK_NUMPAD_RIGHT);
	AScript_AssignValue(WXK_NUMPAD_DOWN,	WXK_NUMPAD_DOWN);
	AScript_AssignValue(WXK_NUMPAD_PAGEUP,	WXK_NUMPAD_PAGEUP);
	AScript_AssignValue(WXK_NUMPAD_PAGEDOWN,WXK_NUMPAD_PAGEDOWN);
	AScript_AssignValue(WXK_NUMPAD_END,		WXK_NUMPAD_END);
	AScript_AssignValue(WXK_NUMPAD_BEGIN,	WXK_NUMPAD_BEGIN);
	AScript_AssignValue(WXK_NUMPAD_INSERT,	WXK_NUMPAD_INSERT);
	AScript_AssignValue(WXK_NUMPAD_DELETE,	WXK_NUMPAD_DELETE);
	AScript_AssignValue(WXK_NUMPAD_EQUAL,	WXK_NUMPAD_EQUAL);
	AScript_AssignValue(WXK_NUMPAD_MULTIPLY,WXK_NUMPAD_MULTIPLY);
	AScript_AssignValue(WXK_NUMPAD_ADD,		WXK_NUMPAD_ADD);
	AScript_AssignValue(WXK_NUMPAD_SEPARATOR,WXK_NUMPAD_SEPARATOR);
	AScript_AssignValue(WXK_NUMPAD_SUBTRACT,WXK_NUMPAD_SUBTRACT);
	AScript_AssignValue(WXK_NUMPAD_DECIMAL,	WXK_NUMPAD_DECIMAL);
	AScript_AssignValue(WXK_NUMPAD_DIVIDE,	WXK_NUMPAD_DIVIDE);
	AScript_AssignValue(WXK_WINDOWS_LEFT,	WXK_WINDOWS_LEFT);
	AScript_AssignValue(WXK_WINDOWS_RIGHT,	WXK_WINDOWS_RIGHT);
	AScript_AssignValue(WXK_WINDOWS_MENU,	WXK_WINDOWS_MENU);
	AScript_AssignValue(WXK_COMMAND,		WXK_COMMAND);
	AScript_AssignValue(WXK_SPECIAL1,		WXK_SPECIAL1);
	AScript_AssignValue(WXK_SPECIAL2,		WXK_SPECIAL2);
	AScript_AssignValue(WXK_SPECIAL3,		WXK_SPECIAL3);
	AScript_AssignValue(WXK_SPECIAL4,		WXK_SPECIAL4);
	AScript_AssignValue(WXK_SPECIAL5,		WXK_SPECIAL5);
	AScript_AssignValue(WXK_SPECIAL6,		WXK_SPECIAL6);
	AScript_AssignValue(WXK_SPECIAL7,		WXK_SPECIAL7);
	AScript_AssignValue(WXK_SPECIAL8,		WXK_SPECIAL8);
	AScript_AssignValue(WXK_SPECIAL9,		WXK_SPECIAL9);
	AScript_AssignValue(WXK_SPECIAL10,		WXK_SPECIAL10);
	AScript_AssignValue(WXK_SPECIAL11,		WXK_SPECIAL11);
	AScript_AssignValue(WXK_SPECIAL12,		WXK_SPECIAL12);
	AScript_AssignValue(WXK_SPECIAL13,		WXK_SPECIAL13);
	AScript_AssignValue(WXK_SPECIAL14,		WXK_SPECIAL14);
	AScript_AssignValue(WXK_SPECIAL15,		WXK_SPECIAL15);
	AScript_AssignValue(WXK_SPECIAL16,		WXK_SPECIAL16);
	AScript_AssignValue(WXK_SPECIAL17,		WXK_SPECIAL17);
	AScript_AssignValue(WXK_SPECIAL18,		WXK_SPECIAL18);
	AScript_AssignValue(WXK_SPECIAL19,		WXK_SPECIAL19);
	AScript_AssignValue(WXK_SPECIAL20,		WXK_SPECIAL20);
	AScript_AssignValue(WXK_A,				'A');
	AScript_AssignValue(WXK_B,				'B');
	AScript_AssignValue(WXK_C,				'C');
	AScript_AssignValue(WXK_D,				'D');
	AScript_AssignValue(WXK_E,				'E');
	AScript_AssignValue(WXK_F,				'F');
	AScript_AssignValue(WXK_G,				'G');
	AScript_AssignValue(WXK_H,				'H');
	AScript_AssignValue(WXK_I,				'I');
	AScript_AssignValue(WXK_J,				'J');
	AScript_AssignValue(WXK_K,				'K');
	AScript_AssignValue(WXK_L,				'L');
	AScript_AssignValue(WXK_M,				'M');
	AScript_AssignValue(WXK_N,				'N');
	AScript_AssignValue(WXK_O,				'O');
	AScript_AssignValue(WXK_P,				'P');
	AScript_AssignValue(WXK_Q,				'Q');
	AScript_AssignValue(WXK_R,				'R');
	AScript_AssignValue(WXK_S,				'S');
	AScript_AssignValue(WXK_T,				'T');
	AScript_AssignValue(WXK_U,				'U');
	AScript_AssignValue(WXK_V,				'V');
	AScript_AssignValue(WXK_W,				'W');
	AScript_AssignValue(WXK_X,				'X');
	AScript_AssignValue(WXK_Y,				'Y');
	AScript_AssignValue(WXK_Z,				'Z');
	AScript_AssignValue(WXK_0,				'0');
	AScript_AssignValue(WXK_1,				'1');
	AScript_AssignValue(WXK_2,				'2');
	AScript_AssignValue(WXK_3,				'3');
	AScript_AssignValue(WXK_4,				'4');
	AScript_AssignValue(WXK_5,				'5');
	AScript_AssignValue(WXK_6,				'6');
	AScript_AssignValue(WXK_7,				'7');
	AScript_AssignValue(WXK_8,				'8');
	AScript_AssignValue(WXK_9,				'9');
	// function assignment
	AScript_AssignFunction(show);
	AScript_AssignFunction(isshown);
	AScript_AssignFunction(getsize);
	AScript_AssignFunction(refresh);
	AScript_AssignFunction(burst);
	AScript_AssignFunction(clear);
	AScript_AssignFunction(setpen);
	AScript_AssignFunction(setbrush);
	AScript_AssignFunction(settextcolor);
	AScript_AssignFunction(setfont);
	AScript_AssignFunction(line);
	AScript_AssignFunction(lines);
	AScript_AssignFunction(rectangle);
	AScript_AssignFunction(ellipse);
	AScript_AssignFunction(text);
	AScript_AssignFunction(textrot);
	AScript_AssignFunction(putimage);
	AScript_AssignFunction(iskey);
	AScript_AssignFunction(glsection);
}

AScript_ModuleTerminate()
{
}

//-----------------------------------------------------------------------------
// utilities
//-----------------------------------------------------------------------------
wxPen *CreatePen(Signal sig, const char *colorName, int width, const Symbol *pSymbolStyle)
{
	int style = wxSOLID;
	if (pSymbolStyle->IsIdentical(AScript_PrivSymbol(transparent))) {
		style = wxTRANSPARENT;
	} else if (pSymbolStyle->IsIdentical(AScript_PrivSymbol(solid))) {
		style = wxSOLID;
	} else if (pSymbolStyle->IsIdentical(AScript_PrivSymbol(dot))) {
		style = wxDOT;
	} else if (pSymbolStyle->IsIdentical(AScript_PrivSymbol(long_dash))) {
		style = wxLONG_DASH;
	} else if (pSymbolStyle->IsIdentical(AScript_PrivSymbol(short_dash))) {
		style = wxSHORT_DASH;
	} else if (pSymbolStyle->IsIdentical(AScript_PrivSymbol(dot_dash))) {
		style = wxDOT_DASH;
	} else if (pSymbolStyle->IsIdentical(AScript_PrivSymbol(stipple))) {
		style = wxSTIPPLE;
	} else if (pSymbolStyle->IsIdentical(AScript_PrivSymbol(bdiagonal))) {
		style = wxBDIAGONAL_HATCH;
	} else if (pSymbolStyle->IsIdentical(AScript_PrivSymbol(crossdiag))) {
		style = wxCROSSDIAG_HATCH;
	} else if (pSymbolStyle->IsIdentical(AScript_PrivSymbol(fdiagonal))) {
		style = wxFDIAGONAL_HATCH;
	} else if (pSymbolStyle->IsIdentical(AScript_PrivSymbol(cross))) {
		style = wxCROSS_HATCH;
	} else if (pSymbolStyle->IsIdentical(AScript_PrivSymbol(horizontal))) {
		style = wxHORIZONTAL_HATCH;
	} else if (pSymbolStyle->IsIdentical(AScript_PrivSymbol(vertical))) {
		style = wxVERTICAL_HATCH;
	} else {
		sig.SetError(ERR_ValueError, "invalid stye symbol %s", pSymbolStyle->GetName());
		return NULL;
	}
	return new wxPen(colorName, width, style);
}

wxBrush *CreateBrush(Signal sig, const char *colorName, const Symbol *pSymbolStyle)
{
	int style = wxSOLID;
	if (pSymbolStyle->IsIdentical(AScript_PrivSymbol(transparent))) {
		style = wxTRANSPARENT;
	} else if (pSymbolStyle->IsIdentical(AScript_PrivSymbol(solid))) {
		style = wxSOLID;
	} else if (pSymbolStyle->IsIdentical(AScript_PrivSymbol(stipple))) {
		style = wxSTIPPLE;
	} else if (pSymbolStyle->IsIdentical(AScript_PrivSymbol(bdiagonal))) {
		style = wxBDIAGONAL_HATCH;
	} else if (pSymbolStyle->IsIdentical(AScript_PrivSymbol(crossdiag))) {
		style = wxCROSSDIAG_HATCH;
	} else if (pSymbolStyle->IsIdentical(AScript_PrivSymbol(fdiagonal))) {
		style = wxFDIAGONAL_HATCH;
	} else if (pSymbolStyle->IsIdentical(AScript_PrivSymbol(cross))) {
		style = wxCROSS_HATCH;
	} else if (pSymbolStyle->IsIdentical(AScript_PrivSymbol(horizontal))) {
		style = wxHORIZONTAL_HATCH;
	} else if (pSymbolStyle->IsIdentical(AScript_PrivSymbol(vertical))) {
		style = wxVERTICAL_HATCH;
	} else {
		sig.SetError(ERR_ValueError, "invalid stye symbol %s", pSymbolStyle->GetName());
		return NULL;
	}
	return new wxBrush(colorName, style);
}

wxFont *CreateFont(Signal sig, int pointSize, const Symbol *pSymbolFamily,
	const Symbol *pSymbolStyle, const Symbol *pSymbolWeight, const char *faceName)
{
	wxFontFamily family = wxFONTFAMILY_DEFAULT;
	if (pSymbolFamily->IsIdentical(AScript_PrivSymbol(default))) {
		family = wxFONTFAMILY_DEFAULT;
	} else if (pSymbolFamily->IsIdentical(AScript_PrivSymbol(decorative))) {
		family = wxFONTFAMILY_DECORATIVE;
	} else if (pSymbolFamily->IsIdentical(AScript_PrivSymbol(roman))) {
		family = wxFONTFAMILY_ROMAN;
	} else if (pSymbolFamily->IsIdentical(AScript_PrivSymbol(script))) {
		family = wxFONTFAMILY_SCRIPT;
	} else if (pSymbolFamily->IsIdentical(AScript_PrivSymbol(swiss))) {
		family = wxFONTFAMILY_SWISS;
	} else if (pSymbolFamily->IsIdentical(AScript_PrivSymbol(modern))) {
		family = wxFONTFAMILY_MODERN;
	} else if (pSymbolFamily->IsIdentical(AScript_PrivSymbol(teletype))) {
		family = wxFONTFAMILY_TELETYPE;
	} else {
		sig.SetError(ERR_ValueError, "invalid family symbol %s", pSymbolFamily->GetName());
		return NULL;
	}
	int style = wxFONTSTYLE_NORMAL;
	if (pSymbolStyle->IsIdentical(AScript_PrivSymbol(normal))) {
		style = wxFONTSTYLE_NORMAL;
	} else if (pSymbolStyle->IsIdentical(AScript_PrivSymbol(slant))) {
		style = wxFONTSTYLE_SLANT;
	} else if (pSymbolStyle->IsIdentical(AScript_PrivSymbol(italic))) {
		style = wxFONTSTYLE_ITALIC;
	} else {
		sig.SetError(ERR_ValueError, "invalid style symbol %s", pSymbolStyle->GetName());
		return NULL;
	}
	wxFontWeight weight = wxFONTWEIGHT_NORMAL;
	if (pSymbolWeight->IsIdentical(AScript_PrivSymbol(normal))) {
		weight = wxFONTWEIGHT_NORMAL;
	} else if (pSymbolWeight->IsIdentical(AScript_PrivSymbol(light))) {
		weight = wxFONTWEIGHT_LIGHT;
	} else if (pSymbolWeight->IsIdentical(AScript_PrivSymbol(bold))) {
		weight = wxFONTWEIGHT_BOLD;
	} else {
		sig.SetError(ERR_ValueError, "invalid weight symbol %s", pSymbolWeight->GetName());
		return NULL;
	}
	return new wxFont(pointSize, family, style, weight, false, faceName);
}

void DeclareAnchorAttrs(Function *pFunc)
{
	pFunc->DeclareAttr(AScript_PrivSymbol(n));
	pFunc->DeclareAttr(AScript_PrivSymbol(ne));
	pFunc->DeclareAttr(AScript_PrivSymbol(e));
	pFunc->DeclareAttr(AScript_PrivSymbol(se));
	pFunc->DeclareAttr(AScript_PrivSymbol(s));
	pFunc->DeclareAttr(AScript_PrivSymbol(sw));
	pFunc->DeclareAttr(AScript_PrivSymbol(w));
	pFunc->DeclareAttr(AScript_PrivSymbol(nw));
	pFunc->DeclareAttr(AScript_PrivSymbol(c));
}

wxRect GetAnchoredRect(int x, int y, int width, int height, const SymbolSet &symbolSet)
{
	wxRect rc;
	rc.x =
		(symbolSet.IsSet(AScript_PrivSymbol(nw)) ||
		 symbolSet.IsSet(AScript_PrivSymbol(sw)) ||
		 symbolSet.IsSet(AScript_PrivSymbol(w)))? x :
		(symbolSet.IsSet(AScript_PrivSymbol(ne)) ||
		 symbolSet.IsSet(AScript_PrivSymbol(se)) ||
		 symbolSet.IsSet(AScript_PrivSymbol(e)))? x - width :
		(symbolSet.IsSet(AScript_PrivSymbol(c)) ||
		 symbolSet.IsSet(AScript_PrivSymbol(n)) ||
		 symbolSet.IsSet(AScript_PrivSymbol(s)))? x - width / 2 :
		x;
	rc.y =
		(symbolSet.IsSet(AScript_PrivSymbol(nw)) ||
		 symbolSet.IsSet(AScript_PrivSymbol(ne)) ||
		 symbolSet.IsSet(AScript_PrivSymbol(n)))? y :
		(symbolSet.IsSet(AScript_PrivSymbol(sw)) ||
		 symbolSet.IsSet(AScript_PrivSymbol(se)) ||
		 symbolSet.IsSet(AScript_PrivSymbol(s)))? y - height :
		(symbolSet.IsSet(AScript_PrivSymbol(c)) ||
		 symbolSet.IsSet(AScript_PrivSymbol(w)) ||
		 symbolSet.IsSet(AScript_PrivSymbol(e)))? y - height / 2 :
		y;
	rc.width = width, rc.height = height;
	return rc;
}

AScript_EndModule(scribble, scribble)

AScript_RegisterModule(scribble)
