/*
 *  psychlops_g_canvas_API_WIN32.cpp
 *  Psychlops Standard Library (MacOSX)
 *
 *  Last Modified 2009/05/12 by Kenchi HOSOKAWA
 *  (C) 2005-2009 Kenchi HOSOKAWA, Kazushi MARUYA, Takao SATO
 */

#include <vector>
#include <map>

#include <windows.h>
#include <gl/gl.h>
#include <gl/glu.h>

#include "psychlops_g_API_win32gl.h"
#include "../win32/psychlops_app_state_Win32.h"
#include "../win32/psychlops_io_API_WIN32.h"
#include "../win32/psychlops_io_display_Win32.h"
#include "../../core/ApplicationInterfaces/psychlops_code_exception.h"
#include "../../core/graphic/psychlops_g_color.h"
#include "../../core/graphic/psychlops_g_image.h"
#include "../../core/graphic/psychlops_g_font.h"
#include "../../core/math/psychlops_m_util.h"




namespace Psychlops {


	WNDCLASSEX APICanvasProperties::wcx;
	bool APICanvasProperties::wcx_setted = false;
	void APICanvasProperties::setWindowClass() {
		if(!wcx_setted) {
			wcx.cbSize        = sizeof(WNDCLASSEX);
			wcx.style         = CS_GLOBALCLASS;
			wcx.lpfnWndProc   = (WNDPROC)&(APIApplicationProperties::proc);
			wcx.cbClsExtra    = 0;
			wcx.cbWndExtra    = 0;
			wcx.hInstance     = APIApplicationProperties::startupinfo.hInstance_;
			wcx.hIcon         = NULL;
			wcx.hIconSm       = NULL;
			wcx.hCursor       = NULL;
			wcx.hbrBackground = NULL;
			wcx.lpszMenuName  = "Psychlops Window";
			wcx.lpszClassName = APIApplicationProperties::startupinfo.pClassName;
			if (!RegisterClassEx(&wcx)) Exception(typeid(APICanvasProperties), "API ERROR", "Failed to regist main window.");
			wcx_setted = true;
		}
	}

	APICanvasProperties::APICanvasProperties()
	 : outer(0), has_instance_(false), the_display_(0), savedGammaRamp_ptr_(&savedGammaRamp_), calibration_mode_(Color::NOTHING)
	  ,vleft(0), vright(0), vtop(0), vbottom(0)
	{
		the_display_ = GetDC(0);
	}
	APICanvasProperties::~APICanvasProperties() {
		outer = 0;
		destroyCanvasInstance();
	}
	void APICanvasProperties::getDisplayMertix(const Display& d) {
		HDC display_ = d.api_->display_;
		width_ = GetDeviceCaps(display_, HORZRES);
		height_ = GetDeviceCaps(display_, VERTRES);
		colordepth_ = GetDeviceCaps(display_, BITSPIXEL);
		refreshrate_ = GetDeviceCaps(display_, VREFRESH);
		vleft=d.area.getLeft(); vright=d.area.getRight(); vtop=d.area.getTop(); vbottom=d.area.getBottom();
	}
	void APICanvasProperties::generateCanvasInstance(const Display& d) {
		the_display_ = d.api_->display_;
		vleft=d.area.getLeft(); vright=d.area.getRight(); vtop=d.area.getTop(); vbottom=d.area.getBottom();
		memset(&mode_,0,sizeof(mode_));
		memset(&original_mode_,0,sizeof(original_mode_));
		original_mode_.dmSize = sizeof(mode_);
		original_mode_.dmPelsWidth  = GetDeviceCaps(the_display_, HORZRES);
		original_mode_.dmPelsHeight = GetDeviceCaps(the_display_, VERTRES);
		original_mode_.dmBitsPerPel	= GetDeviceCaps(the_display_, BITSPIXEL);
		original_mode_.dmDisplayFrequency = GetDeviceCaps(the_display_, VREFRESH);
		original_mode_.dmFields     = DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT|DM_DISPLAYFREQUENCY;
		mode_ = original_mode_;
		generateCanvasInstance(mode_, d);
	}
	void APICanvasProperties::generateCanvasInstance(int d_width, int d_height, int d_colordepth, double d_refreshrate, const Display& d) {
		the_display_ = d.api_->display_;
		vleft=d.area.getLeft(); vright=d.area.getRight(); vtop=d.area.getTop(); vbottom=d.area.getBottom();
		memset(&mode_,0,sizeof(mode_));
		memset(&original_mode_,0,sizeof(original_mode_));
		mode_.dmSize       = sizeof(mode_);
		mode_.dmPelsWidth	= d_width;
		mode_.dmPelsHeight	= d_height;
		mode_.dmBitsPerPel	= d_colordepth;
		mode_.dmDisplayFrequency = d_refreshrate;
		mode_.dmFields     = DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT|DM_DISPLAYFREQUENCY;
		generateCanvasInstance(mode_, d);
	}
	void APICanvasProperties::generateCanvasInstance(DEVMODE &d_mode, const Display& d) {
		setWindowClass();

		// Try to set selected mode and get results. NOTE: CDS_FULLSCREEN gets rid of start bar.
		//if (ChangeDisplaySettings(&d_mode,CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) throw Exception(typeid(APICanvasProperties), "API ERROR", "API Error (Failed to switch display mode)");
		if(ChangeDisplaySettingsEx(d.name.c_str(), &d_mode, NULL, CDS_FULLSCREEN, NULL) != DISP_CHANGE_SUCCESSFUL)
			throw Exception(typeid(APICanvasProperties), "API ERROR", "API Error (Failed to switch display mode)");
		width_       = d_mode.dmPelsWidth;
		height_      = d_mode.dmPelsHeight;
		colordepth_  = d_mode.dmBitsPerPel;
		refreshrate_ = d_mode.dmDisplayFrequency;
		ShowCursor(false);

		the_window_ = CreateWindowEx(
			WS_EX_APPWINDOW | WS_EX_TOPMOST,
			APIApplicationProperties::startupinfo.pClassName,
		 	"Psychlops Window",
			WS_POPUP,
			//0, 0, d_width, d_height,
			//vleft, vtop, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN),
			vleft, vtop, width_, height_,
			NULL, NULL, wcx.hInstance, NULL );
		if(the_window_==NULL) throw Exception(typeid(APICanvasProperties), "API ERROR", "Failed to regist main window.");

		// attach OpenGL context for the window
		attachOpenGLContext( &the_window_, &the_display_, &the_GL_context_, mode_.dmBitsPerPel );
		//glAddSwapHintRectWIN(0, 0, width_, height_);

		has_instance_ = true;
		calibration_mode_ = Color::NOTHING;

		MSG msg;
		PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);

//		if(0==AttachThreadInput(GetWindowThreadProcessId(hWnd, NULL), GetWindowThreadProcessId(APIInputProperties::hWnd, NULL), TRUE) )
//			throw Exception(typeid(APICanvasProperties), "API ERROR", "Failed to regist control message listner.");
//		if(0==SetFocus(APIInputProperties::hWnd))
//			throw Exception(typeid(APICanvasProperties), "API ERROR", "Failed to regist control message listner.");
//		SetActiveWindow(APIInputProperties::hWnd);
		ShowWindow(the_window_, APIApplicationProperties::startupinfo.iCmdShow_);
//		BringWindowToTop(hWnd);
	}
	void APICanvasProperties::attachOpenGLContext(HWND *hWnd, HDC * hDC, HGLRC * hRC, int d_colordepth) {
		int format;

		// get the device context (DC)
		*hDC = GetDC( *hWnd );

		// set the pixel format for the DC
		/*PIXELFORMATDESCRIPTOR pfd = {
			sizeof(PIXELFORMATDESCRIPTOR),   // size of this pfd
			1,                     // version number
			PFD_GENERIC_ACCELERATED |
			PFD_SWAP_COPY |
			PFD_DRAW_TO_WINDOW |   // support window
			PFD_DRAW_TO_BITMAP |
			PFD_SUPPORT_OPENGL |   // support OpenGL
			PFD_DOUBLEBUFFER,      // double buffered
			PFD_TYPE_RGBA,         // RGBA type
			d_colordepth,          // color depth
			0, 0, 0, 0, 0, 0,      // color bits ignored
			0,                     // no alpha buffer
			0,                     // shift bit ignored
			0,                     // no accumulation buffer
			0, 0, 0, 0,            // accum bits ignored
			16,                    // 16-bit z-buffer
			1,                     // 1 stencil buffer
			4,                     // 4 auxiliary buffer
			PFD_MAIN_PLANE,        // main layer
			0,                     // reserved
			0, 0, 0                // layer masks ignored
		};*/
		PIXELFORMATDESCRIPTOR pfd;
		ZeroMemory( &pfd, sizeof( pfd ) );
		pfd.nSize = sizeof( pfd );
		pfd.nVersion = 2;
		pfd.dwFlags = PFD_GENERIC_ACCELERATED | PFD_SWAP_EXCHANGE |
		 PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
		pfd.iPixelType = PFD_TYPE_RGBA;
		pfd.cColorBits = d_colordepth;
		pfd.cRedBits = d_colordepth/4;
		pfd.cGreenBits = d_colordepth/4;
		pfd.cBlueBits = d_colordepth/4;
		pfd.cAlphaBits = d_colordepth/4;
		pfd.cDepthBits = 32;
		pfd.iLayerType = PFD_MAIN_PLANE;
		pfd.cStencilBits = 1;
		pfd.cAuxBuffers = 4;
		format = ChoosePixelFormat( *hDC, &pfd );
		if(!(SetPixelFormat( *hDC, format, &pfd ))) throw Exception(typeid(APICanvasProperties), "API ERROR", "API Error (Failed to set pixel format).");

		// create and enable the render context (RC)
		if(!(*hRC = wglCreateContext( *hDC ))) throw Exception(typeid(APICanvasProperties), "API ERROR", "API Error (Failed to set OpenGL context).");
		if(!(wglMakeCurrent( *hDC, *hRC ))) throw Exception(typeid(APICanvasProperties), "API ERROR", "API Error (Failed to set OpenGL context).");
/**/
		//PROC vsync = wglGetProcAddress("wglSwapInterval");
		//if(vsync!=NULL) {
		//	vsync();
		//}
		typedef bool (APIENTRY *PFNWGLSWAPINTERVALFARPROC)(int);
		PFNWGLSWAPINTERVALFARPROC wglSwapIntervalEXT = 0;
		wglSwapIntervalEXT = (PFNWGLSWAPINTERVALFARPROC)wglGetProcAddress("wglSwapIntervalEXT");
		if( wglSwapIntervalEXT ) {
			wglSwapIntervalEXT(1);
			vsync_available_ = true;
		} else {
			//throw Exception("API function[wglSwapIntervalEXT] failed to enforce buffer swap to synchronize to vsync.");
			vsync_available_ = false;
		}
/**/
	}

	void APICanvasProperties::destroyCanvasInstance() {
		if(has_instance_) {
			destroyGammaSettings();
			detachOpenGLContext( the_window_, the_display_, the_GL_context_ );
			DestroyWindow( the_window_ );
			if(window_type == FULLSCREEN) {
				ChangeDisplaySettings(NULL,0);
				ShowCursor(true);
			}
		}
		has_instance_ = false;
	}
	void APICanvasProperties::detachOpenGLContext(HWND hWnd, HDC hDC, HGLRC hRC) {
		wglMakeCurrent( NULL, NULL );
		wglDeleteContext( hRC );
		ReleaseDC( hWnd, hDC );
	}

	double APICanvasProperties::limit(double v) {
		double r = v;
		if(r<0     && !Color::strict_match) throw Exception(typeid(*this), "Gamma Table Error", "Out of Range");
		if(r>65535 && !Color::strict_match) throw Exception(typeid(*this), "Gamma Table Error", "Out of Range");
		r = ( r<0     ? 0 : r );
		r = ( r>65535 ? 0 : r );
		return r;
	}
	void APICanvasProperties::setGammaValue(const double gamma_r, const double gamma_g, const double gamma_b) {
		const WORD MAX = 65535;
		if(has_instance_) {
			GAMMA_RAMP_TABLE table;
			for(int j=0; j<RAMP_STEPS_; j++) {
				table.r[j] = (WORD)limit(MAX*pow(j/255.0, 1.0/gamma_r));
				table.g[j] = (WORD)limit(MAX*pow(j/255.0, 1.0/gamma_g));
				table.b[j] = (WORD)limit(MAX*pow(j/255.0, 1.0/gamma_b));
			}
			LPVOID ptr = &table;
			setGammaTable(ptr);
			calibration_mode_ = Color::GAMMA_VALUE;
		}
	}
	void APICanvasProperties::setGammaTable(const std::vector<double> &table_r, const std::vector<double> &table_g, const std::vector<double> &table_b) {
		const WORD MAX = 65535;
		if(has_instance_) {
			if(table_r.size()!=256 || table_g.size()!=256 || table_b.size()!=256)
				throw Exception(typeid(*this), "Gamma Table Error", "Table size is out of order (not 256).");
			GAMMA_RAMP_TABLE table;
			for(int j=0; j<RAMP_STEPS_; j++) {
				table.r[j] = (WORD)limit(MAX*table_r[j]);
				table.g[j] = (WORD)limit(MAX*table_g[j]);
				table.b[j] = (WORD)limit(MAX*table_b[j]);
			}
			LPVOID ptr = &table;
			setGammaTable(ptr);
			calibration_mode_ = Color::TABLE;
		}
	}
	void APICanvasProperties::setGammaTable(LPVOID table) {
		if(has_instance_) {
			saveGammaValue();
			err = SetDeviceGammaRamp(the_display_, table);
			if(err=FALSE) throw Exception(typeid(*this), "API ERROR", "Failed to set color calibration table for the video renderer.");
		}
	}
	void APICanvasProperties::saveGammaValue() {
		if(has_instance_) {
			if(calibration_mode_==Color::NOTHING) {
				err = GetDeviceGammaRamp(the_display_, savedGammaRamp_ptr_);
				if(err=FALSE) throw Exception(typeid(*this), "API ERROR", "Failed to set color calibration table for the video renderer.");
			}
		}
	}
	void APICanvasProperties::destroyGammaSettings() {
 		if(has_instance_) {
			switch(calibration_mode_) {
				case Color::GAMMA_VALUE:
				case Color::TABLE:
					setGammaTable(savedGammaRamp_ptr_);
					break;
				case Color::NOTHING:
				default:
					break;
			}
			calibration_mode_ = Color::NOTHING;
		}
	}
	int APICanvasProperties::getGammaMode() {
		return calibration_mode_;
	}

	void APICanvasProperties::flip() {
		SwapBuffers(the_display_);
	}


/*
	void APICanvasProperties::uncacheLetters(Letters &letters) {
		if(letters.caches.count(outer)!=0) {
			letters.caches[outer].id->uncache(the_display_);
			letters.caches.erase(outer);
		}
	}
	void APICanvasProperties::cacheLetters(Letters &letters) {
		if(letters.caches.count(outer)!=0) { uncacheLetters(letters); }
		letters.caches.insert(std::pair<DrawableWithCache*, Letters::Cache>(outer, Letters::Cache(new APIFontProperties, false)));
		letters.caches[outer].id->cache(the_display_, letters);
	}
	void APICanvasProperties::drawLetters(
		Letters &letters, const double x, const double y,
		const double r, const double g, const double b, const double a,
		const int horiz_align, const double max_width) {
		if(letters.caches.count(outer)==0) {
			outer->cacheLetters(letters);
		}
		letters.caches[outer].id->draw(the_display_, letters, x,y,r,g,b,a,horiz_align,max_width);
	}
*/

	Point APICanvasProperties::left_top() {
		RECT rect;
		GetWindowRect(the_window_, &rect);
		return Point(rect.left, rect.top);
	}

	void APICanvasProperties::shift(int x, int y) {
	}



	APICanvasPropertiesFullscreen::APICanvasPropertiesFullscreen(const Display &d)
	 : APICanvasProperties() {
	 	getDisplayMertix(d);
		generateCanvasInstance(d);
	}
	APICanvasPropertiesFullscreen::APICanvasPropertiesFullscreen(int d_width, int d_height, int d_colordepth, double d_refreshrate, const Display &d)
	 : APICanvasProperties() {
	 	getDisplayMertix(d);
		generateCanvasInstance(d_width, d_height, d_colordepth, d_refreshrate, d);
	}



	APICanvasPropertiesWindow::APICanvasPropertiesWindow(int d_width, int d_height, const Display& d)
	 : APICanvasProperties() {
	 	getDisplayMertix(d);
		generate(d.area.getLeft()+10, d.area.getTop()+10, d_width, d_height);
	}
	APICanvasPropertiesWindow::APICanvasPropertiesWindow(int d_left, int d_top, int d_width, int d_height)
	 : APICanvasProperties() {
	 	getDisplayMertix(Display::primary);
		generate(d_left, d_top, d_width, d_height);
	}
	APICanvasPropertiesWindow::~APICanvasPropertiesWindow() {
		ShowCursor(true);
	}
	void APICanvasPropertiesWindow::generate(int d_left, int d_top, int d_width, int d_height) {
		setWindowClass();
		width_ = d_width;
		height_ = d_height;

		the_window_ = CreateWindowEx(
			WS_EX_APPWINDOW | WS_EX_ACCEPTFILES,
			APIApplicationProperties::startupinfo.pClassName,
		 	"Psychlops Window",
			WS_POPUP,// | WS_CAPTION,
			d_left, d_top, width_, height_,
			HWND_DESKTOP, NULL, wcx.hInstance, NULL );
		if(the_window_==NULL) throw Exception(typeid(APICanvasProperties), "API ERROR", "Failed to regist main window.");

		// get the device context (DC)
		the_display_ = GetDC( the_window_ );
		// attach OpenGL context for the window
		attachOpenGLContext( &the_window_, &the_display_, &the_GL_context_, GetDeviceCaps(the_display_, BITSPIXEL) );
		//glAddSwapHintRectWIN(0, 0, width_, height_);

		has_instance_ = true;
		calibration_mode_ = Color::NOTHING;
		MSG msg;
		PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
		ShowWindow(the_window_, APIApplicationProperties::startupinfo.iCmdShow_);
		ShowCursor(false);
	}



////////	TextCacheAllocation	////////
	void APICanvasProperties::draw(
		Letters &drawee, const double xx, const double yy,
		const double r, const double g, const double b, const double a,
		const int horiz_align, const int vertical_align, const double max_width)
	//void APICanvasProperties::draw(Letters &drawee)
	{
//MessageBoxW( NULL, drawee.str.c_str(), L"Notification", MB_OK );
		if( !text_cache.isCached(&drawee) ) cache(drawee);
		if( text_cache.isCached(&drawee) ) {
			text_cache.map[&drawee]->draw(drawee, xx, yy, r, g, b, a, horiz_align, vertical_align, max_width, *outer);
		}
		/*
		const int anchor = horiz_align, vertical_anchor = vertical_align;
		double mx = xx, my = yy;
		//double mx = drawee.getDatum().x, my = drawee.getDatum().y;
		if( !text_cache.isCached(&drawee) ) cache(drawee);
		if( text_cache.isCached(&drawee) ) {
			double x, y;
			switch(anchor) {
				case Letters::TEXT_ALIGN_CENTER:
					x = mx - text_cache.map[&drawee]->img.getWidth() / 2;
					break;
				case Letters::TEXT_ALIGN_RIGHT:
					x = mx - text_cache.map[&drawee]->img.getWidth();
					break;
				case Letters::NOT_SPECIFIED:
				case Letters::TEXT_ALIGN_LEFT:
				default:
					x = mx;
					break;
			}
			switch(vertical_anchor) {
				case Letters::TEXT_ALIGN_MIDDLE:
					y = my;
					text_cache.map[&drawee]->img.centering(x, y);
					break;
				case Letters::TEXT_ALIGN_TOP:
					y = my;
					text_cache.map[&drawee]->img.move_to(x, y);
					break;
				default:
					y = my - drawee.font.size;
					text_cache.map[&drawee]->img.move_to(x, y);
					break;
			}
			text_cache.map[&drawee]->img.draw(*outer);
		}
		*/
	}
	void APICanvasProperties::cache(Letters &drawee)
	{
		Utilities::CacheOnImage *alloc;
		if( !text_cache.isCached(&drawee) ) {
			text_cache.map[&drawee] = new Utilities::CacheOnImage();
		}
		cacheText(drawee, *(text_cache.map[&drawee]));
	}
	void APICanvasProperties::cacheText(Letters &drawee, Utilities::CacheOnImage& t)
	{
		int fontsize = (int)(drawee.font.size);
		int ix = 0, iy = 0;
		//std:: str = wtext(drawee.text).dup;

//if(letters.font.weight==0) letters.font = Font::default_font; // patch for VC
//		FontAPI fontprop;
//		fontprop.apply(letters.font);
//		HFONT font = CreateFontIndirectW(fontprop.font_prop);
//		if(fontapi==NULL) {
//			Font tmpfont(letters.font.size, letters.font.weight, letters.font.style, Font::default_font.family[0]);
//			fontprop.apply(tmpfont);
//			fontapi = CreateFontIndirectW(fontprop.font_prop);
//		}
		HFONT font = CreateFontW(
			fontsize, 0, 0, 0,
			drawee.font.weight,
			drawee.font.style==Font::normal_style ? FALSE : TRUE,
			//drawee.font.style==Font.Style.NORMAL ? FALSE : TRUE,
			FALSE, //drawee.font.decoration.underline ? TRUE : FALSE,
			FALSE, //drawee.font.decoration.line_through ? TRUE : FALSE,
			DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, OUT_DEFAULT_PRECIS, PROOF_QUALITY, FF_DONTCARE
			//, Font::default_font.family[0].c_str()
			, drawee.font.family[0].c_str()
		);
		SIZE s;

		HDC hdc_ = the_display_;
		SelectObject(hdc_, font);
		GetTextExtentPoint32W(hdc_, drawee.str.c_str(), drawee.str.length(), &s);

		t.img.release();
		t.img.set(s.cx, s.cy, Image::RGBA);

		BITMAP bitmap_s;
		HBITMAP hBitmap = CreateCompatibleBitmap(hdc_, t.img.getWidth(), t.img.getHeight() );
		HDC hdcBitmap = CreateCompatibleDC(hdc_);

		SelectObject(hdcBitmap, hBitmap);
		SelectObject(hdcBitmap, font);

		RECT area;
		area.left = 0;
		area.right = t.img.getWidth();
		area.top = 0;
		area.bottom = t.img.getHeight();

		Color fg_;
		//if(drawee.fill !is null)// && drawee.fill.paint !is null && drawee.fill.paint.color !is null)
		//	fg_ = drawee.fill;
		//else
			fg_ = Color(1,1,1,1);
		Color tmp = fg_;
		SetTextColor(hdcBitmap, 0xffffff);
		SetBkColor(hdcBitmap, 0);
		int err = DrawTextExW(hdcBitmap, (WCHAR*)drawee.str.c_str(), drawee.str.length(), &area, DT_LEFT | DT_EXPANDTABS, NULL);
		if(err==0) throw Exception("DrawTextExW failed.");
		for(int y=0; y<t.img.getHeight(); y++) {
			for(int x=0; x<t.img.getWidth(); x++) {
				COLORREF c = GetPixel(hdcBitmap, x, y);
//				if(c!=0) { tmp.set( fg_.getR(), fg_.getG(), fg_.getB(),   ((c&0xff0000)>>16)/255.0 ); } else tmp=Color::null_color;
				if(c!=0) { tmp.set( 0,0,0, ((c&0xff0000)>>16)/255.0 ); } else tmp=Color::null_color;
//				t.img.pix_raw(x, t.img.getHeight()-y-1, tmp);
				t.img.pix_raw(x, y, tmp);
			}
		}
		t.img.cache(*outer);
		DeleteObject(hdcBitmap);
		DeleteObject(hBitmap);
		DeleteObject(font);
	}

	void APICanvasProperties::uncache(Letters &drawee)
	{
		if( text_cache.isCached(&drawee) ) {
			text_cache.map[&drawee]->uncache();
		}
	}
	void APICanvasProperties::uncacheAllLetters()
	{
		text_cache.uncacheAll();
	}


////////	APIFontProperties	////////

const std::wstring default_facename(L"MS UI Gothic");
struct FontAPI {
	LOGFONTW storage;
	LOGFONTW* font_prop;
	FontAPI() {
		font_prop = &storage;
		font_prop->lfHeight      = 12;
		font_prop->lfWidth       = 0;
		font_prop->lfEscapement  = 0;
		font_prop->lfOrientation = 0;
		font_prop->lfWeight      = FW_NORMAL;
		font_prop->lfItalic      = FALSE;
		font_prop->lfUnderline   = FALSE;
		font_prop->lfStrikeOut   = FALSE;
		font_prop->lfCharSet     = DEFAULT_CHARSET;
		font_prop->lfOutPrecision   = OUT_DEFAULT_PRECIS;
		font_prop->lfClipPrecision  = CLIP_DEFAULT_PRECIS;
		font_prop->lfQuality        = ANTIALIASED_QUALITY;//CLEARTYPE_COMPAT_QUALITY;
		font_prop->lfPitchAndFamily = FF_DONTCARE|DEFAULT_PITCH;
		applyFacename(default_facename);
	}

	void apply(const Font &font) {
		font_prop->lfHeight      = (LONG)Math::round(font.size);
		font_prop->lfWeight      = (font.weight==0 ? FW_NORMAL : (LONG)Math::round(font.weight));
		font_prop->lfItalic      = font.style;
		applyFacename(font.family.size()>0 ? font.family[0] : default_facename);
	}
	void applyFacename(const std::wstring &facename) {
		for(unsigned int i=0; i<LF_FACESIZE; i++) {
			if(i<facename.length()) font_prop->lfFaceName[i] = facename[i];
			else font_prop->lfFaceName[i] = L'\0';
		}
	}

};

	APIFontProperties::APIFontProperties() : cached(false), cache_id(0), length(0), lists(0) {
	}
	APIFontProperties::~APIFontProperties() {
		uncache();
	}
	void APIFontProperties::uncache() {
		if(cached) {
			glDeleteLists(cache_id, length);
			if(length>100) delete [] lists;
			cache_id = 0;
			length = 0;
			lists = 0;
			cached = false;
		}
	}
	void APIFontProperties::uncache(const HDC &ctx) {
		if(cached) {
			glDeleteLists(cache_id, length);
			if(length>100) delete [] lists;
			cache_id = 0;
			length = 0;
			lists = 0;
			cached = false;
		}
	}
	void APIFontProperties::cache(const HDC &ctx, Letters &letters) {
		if(cached) {
		} else {
if(letters.font.weight==0) letters.font = Font::default_font; // patch for VC
			FontAPI fontprop;
			fontprop.apply(letters.font);
			HFONT fontapi = CreateFontIndirectW(fontprop.font_prop);
			if(fontapi==NULL) {
				Font tmpfont(letters.font.size, letters.font.weight, letters.font.style, Font::default_font.family[0]);
				fontprop.apply(tmpfont);
				fontapi = CreateFontIndirectW(fontprop.font_prop);
			}
			SelectObject(ctx, fontapi);
			const unsigned int result = glGenLists(letters.str.length());
			int error;
			GLYPHMETRICSFLOAT metrix;
			double width=0.0, height=0.0;

			for(int i=0; i<letters.str.length(); i++) {
				// outline
					error = wglUseFontOutlinesW( ctx, letters.str.at(i), 1, result+i, 0.0, 0.0, WGL_FONT_POLYGONS, &metrix);
					width += (metrix.gmfCellIncX!=0) ? metrix.gmfCellIncX : metrix.gmfBlackBoxY;
					height = (height>metrix.gmfBlackBoxY) ? height : metrix.gmfBlackBoxY;
				// bitmap
					//error = wglUseFontBitmapsW( the_display_, letters.str.at(i), 1, result+i );
					//width += (metrix.gmfCellIncX!=0) ? metrix.gmfCellIncX : metrix.gmfBlackBoxY;
					//height = (height>metrix.gmfBlackBoxY) ? height : metrix.gmfBlackBoxY;
				// image
					////HBITMAP CreateDIBitmap(HDC hdc, CONST BITMAPINFOHEADER *lpbmih, DWORD fdwInit, CONST VOID *lpbInit, CONST BITMAPINFO *lpbmi, UINT fuUsage);

					//HDC memDC = CreateCompatibleDC(hdc);
					//HBITMAP memBM = CreateCompatibleBitmap(HDC hdc,int nWidth,int nHeight);


					////hdc = BeginPaint(hwnd , &ps);
					//SelectObject(hBuffer , hBitmap);
					//int DrawText(HDC hDC, LPCTSTR lpString, int nCount, LPRECT lpRect, DT_CALCRECT);

					//COLORREF GetPixel(memDC, x, y);
					//if(DeleteObject(memBM)) ;
					//if(DeleteObject(memDC)) ;

				if(error!=TRUE) {
	//				putErrorMessage;
					throw Exception("Failed to load font.");
				}
			}

			cache_id = result;
			length = letters.str.length();
			base_width = width;
			base_height = height;
			culcVirtualMetrix(letters);

			if(length>100) {
				lists = new GLushort[length];
				for(int i=0; i<length; i++) lists[i] = i;
			} else {
				lists = const_cast<GLushort*>(lists100);
			}

			cached = true;
		}
	}
	void APIFontProperties::culcVirtualMetrix(Letters &letters) {
		letters.width_ = base_width * letters.font.size;
		letters.height_ = base_height * letters.font.size;
	}
	void APIFontProperties::draw(const HDC &ctx,
			const Letters &letters, const double x, const double y,
			const double r, const double g, const double b, const double a,
			const int horiz_align, const double max_width) const {

		if(!cached) { throw Exception("Please ask to developpers to implement drawing uncached Letters."); }
		glPushMatrix();
		glPushAttrib(GL_ALL_ATTRIB_BITS);
		glColor4f(r, g, b, a);

		//	bitmap edition
//		glRasterPos2d(x,y);

		//	outline eddition
/*		glDisable(GL_DEPTH_TEST);
		glDisable(GL_ALPHA_TEST);
		glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);
		glEnable(GL_LINE_SMOOTH);
		glHint(GL_LINE_SMOOTH_HINT,GL_NICEST);
		glEnable(GL_POLYGON_SMOOTH);
		glHint(GL_POLYGON_SMOOTH_HINT,GL_NICEST);
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE);
*/

		glTranslated(x,y,0.0);
		glScaled(letters.font.size, - letters.font.size, 1.0);

		glListBase(cache_id);
		glCallLists(length, GL_UNSIGNED_SHORT, lists);
		glListBase(0);

		glPopAttrib();
		glPopMatrix();
	}

const GLushort APIFontProperties::lists100[100] =
			{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49
			,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99};


////////	APIImageProperties	////////

	APIImageProperties::APIImageProperties() : outer_(NULL) {
//		for(int i=0; i<max_reg; i++) { rights_[i]=-1; bottoms_[i]=0; }
//		rights_[0]=-1; bottoms_[0]=-1;
	}
	APIImageProperties::APIImageProperties(Image *outer) : outer_(outer) {
		attach();
	}
	APIImageProperties::~APIImageProperties() {
		detach();
	}

	void APIImageProperties::attach() {}
	void APIImageProperties::detach() {}


	APIImageCache::APIImageCache() : VRAMoffset(0), tex_width(0), tex_height(0) {}
	unsigned int APIImageCache::getTexIndex() {
		return VRAMoffset;
	}
/*
	bool APIImageProperties::regist(int maxwidth, int maxheight, int width, int height, int &left, int &top) {
		for(int i=1; i<max_reg; i++) {
			if( (rights_[i]+width <= maxwidth && (bottoms_[i-1]+height<=bottoms_[i]) || (bottoms_[i-1]+height <= maxheight && bottoms_[i+1]==0) ) ) {
				left = rights_[i]+1;
				top = bottoms_[i-1]+1;
				rights_[i] = left+width-1;
				bottoms_[i] =  (bottoms_[i-1]+height<bottoms_[i]) ? bottoms_[i] : bottoms_[i-1]+height;
				return true;
			}
		}
		throw Exception(typeid(APIImageProperties), "Memory Error", "Image accelerater failed to allocate VRAM.");
	}
*/



}	/*	<- namespace Psychlops 	*/

