/*
 *  psychlops_g_canvas_API_OSX.cpp
 *  Psychlops Standard Library (MacOSX)
 *
 *  Last Modified 2005/12/24 by Kenchi HOSOKAWA
 *  (C) 2005 Kenchi HOSOKAWA, Kazushi MARUYA, Takao SATO
 */

#include <stdlib.h>
#include <Math.h>
#include <time.h>
#include <wchar.h>
#include <iostream>
#include <map>

#import <Cocoa/Cocoa.h>
#include <ApplicationServices/ApplicationServices.h>
#include <CoreServices/CoreServices.h>
#include <mach/mach.h>
#include <mach/mach_time.h>

#include <AGL/agl.h>
#include <OpenGL/OpenGL.h>
#include <OpenGL/gl.h>

#include "psychlops_g_API_OSX.h"
#include "psychlops_g_API_objc_osx.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 "psychlops_io_display_OSX.h"



namespace Psychlops {
	
	APICanvasProperties::APICanvasProperties() : has_instance_(false), quartz_context_(NULL) {}
	APICanvasProperties::~APICanvasProperties() {}
	
	// Getting Display Settings : requires CGDirectDisplayID(the_display_), CFDictionaryRef(mode_)
	WindowMetrix APICanvasProperties::getDisplayMetrix(CGDirectDisplayID target) {
		WindowMetrix tmp;
		tmp.width       = CGDisplayPixelsWide( target );
		tmp.height      = CGDisplayPixelsHigh( target );
		tmp.colordepth  = CGDisplayBitsPerPixel( target );
		tmp.refreshrate = 0.0;
		
		CFNumberRef ref_refreshrate;
		CFDictionaryRef mode_ = CGDisplayCurrentMode( target );
		ref_refreshrate = (CFNumberRef) CFDictionaryGetValue(mode_, kCGDisplayRefreshRate);
		if(ref_refreshrate) {
			CFNumberGetValue(ref_refreshrate, kCFNumberDoubleType, &(tmp.refreshrate));
			if(tmp.refreshrate == 0.0) tmp.refreshrate = 60.0;  // Assume LCD screen
		}
		return tmp;
	}
	
	// Hardware Gamma Settings : requires CGDirectDisplayID(target_display_)
	void APICanvasProperties::setGammaValue(const double gamma_r, const double gamma_g, const double gamma_b) {
		if(has_instance_) {
			saveGammaValue();
			err = CGSetDisplayTransferByFormula( target_display_,
												0.0, 1.0, (1.0/gamma_r),
												0.0, 1.0, (1.0/gamma_g),
												0.0, 1.0, (1.0/gamma_b));
			//				0.0, 1.0, (1.0/gamma_r)*savedRedGamma_,
			//				0.0, 1.0, (1.0/gamma_g)*savedGreenGamma_,
			//				0.0, 1.0, (1.0/gamma_b)*savedBlueGamma_);
			if(err!=kCGErrorSuccess) throw Exception(typeid(*this), "API ERROR", "Failed to set color calibration table for the video renderer.");
			gamma_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) {
		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).");
		int num_steps = table_r.size();
		if(has_instance_) {
			saveGammaValue();
			CGGammaValue *(table[3]);
			for(int i=0; i<3; i++) table[i] = new CGGammaValue[num_steps];
			for(int j=0; j<num_steps; j++) {
				table[0][j] = (CGGammaValue)table_r[j];
				table[1][j] = (CGGammaValue)table_g[j];
				table[2][j] = (CGGammaValue)table_b[j];
			}
			err = CGSetDisplayTransferByTable(target_display_, num_steps, table[0], table[1], table[2]);
			if(err!=kCGErrorSuccess) throw Exception(typeid(*this), "API ERROR", "Failed to set color calibration table for the video renderer.");
			gamma_mode_ = Color::TABLE;
			for(int i=0; i<3; i++) delete [] table[i];
		}
	}
	void APICanvasProperties::setGammaTable(const CGGammaValue * const table_r, const CGGammaValue * const table_g, const CGGammaValue * const table_b, const int num_steps) {
		if(has_instance_) {
			saveGammaValue();
			err = CGSetDisplayTransferByTable(target_display_, num_steps, table_r, table_g, table_b);
			gamma_mode_ = Color::TABLE;
		}
	}
	void APICanvasProperties::saveGammaValue() {
		if(has_instance_) {
			if(gamma_mode_==Color::NOTHING) {
				err = CGGetDisplayTransferByFormula( target_display_,
													&savedRedMin_, &savedRedMax_, &savedRedGamma_,
													&savedGreenMin_, &savedGreenMax_, &savedGreenGamma_,
													&savedBlueMin_, &savedBlueMax_, &savedBlueGamma_);
			}
		}
	}
	void APICanvasProperties::destroyGammaSettings() {
		if(has_instance_) {
			switch(gamma_mode_) {
				case Color::GAMMA_VALUE:
				case Color::TABLE:
				default:
					err = CGSetDisplayTransferByFormula( target_display_,
														savedRedMin_, savedRedMax_, savedRedGamma_,
														savedGreenMin_, savedGreenMax_, savedGreenGamma_,
														savedBlueMin_, savedBlueMax_, savedBlueGamma_);
					break;
			}
			gamma_mode_ = Color::NOTHING;
		}
	}
	
	// Letters API : requires CGContextRef(quartz_context_)
	//void APICanvasProperties::uncacheLetters(Letters &letters) {
	void APICanvasProperties::uncache(Letters &letters) {
		if(letters.caches.count(outer)!=0) {
			letters.caches[outer].id->uncache(quartz_context_, letters);
			letters.caches.erase(outer);
		}
	}
	//void APICanvasProperties::cacheLetters(Letters &letters) {
	void APICanvasProperties::cache(Letters &letters) {
		if(letters.caches.count(outer)!=0) { uncache(letters); }
		letters.caches.insert(std::pair<DrawableWithCache*, Letters::Cache>(outer, Letters::Cache(new APIFontProperties, false)));
		letters.caches[outer].id->cache(quartz_context_, letters, *outer);
	}
	void APICanvasProperties::drawLetters(Letters &letters, const double x, const double y, const Color &col, const int horiz_align, const int vertical_align, const double max_width) {
		if(letters.caches.count(outer)==0) {
			outer->cacheLetters(letters);
		}
//		letters.caches[outer].id->draw(quartz_context_, letters, x,y,col,horiz_align,vertical_align,max_width, *outer);
		letters.caches[outer].id->draw(quartz_context_, letters, x,y,col,horiz_align,max_width, *outer);
	}
	
	/*
	 void APICanvasProperties::uncacheLetters(Letters &letters) {
	 if(!letters.cache_id.empty()) {
	 delete letters.cache_id.at(0);
	 letters.cache_id.clear();
	 }
	 }
	 void APICanvasProperties::cacheLetters(Letters &letters) {
	 if(letters.cache_id.empty()) {
	 letters.cache_id.push_back(new APIFontProperties);
	 letters.cache_id.at(0)->cache(quartz_context_, letters);
	 }
	 }
	 void APICanvasProperties::drawLetters(const Letters &letters, const double x, const double y, const Color &col, const int horiz_align, const double max_width) {
	 if(!letters.cache_id.empty()) {
	 letters.cache_id.at(0)->draw(quartz_context_, letters, x, y, col, horiz_align, max_width);
	 }
	 }
	 */
	
	
	////////	APICanvasPropertiesFullscreen	////////
	APICanvasPropertiesFullscreen::APICanvasPropertiesFullscreen(const Display &target) {
		generateCanvasInstance(target);
	}
	APICanvasPropertiesFullscreen::APICanvasPropertiesFullscreen(int d_width, int d_height, int d_colordepth, double d_refreshrate, const Display &target) {
		generateCanvasInstance(d_width, d_height, d_colordepth, d_refreshrate, target);
	}
	APICanvasPropertiesFullscreen::~APICanvasPropertiesFullscreen() {
		destroyCanvasInstance();
	}
	
	void APICanvasPropertiesFullscreen::generateCanvasInstance(const Display &target) {
		target_display_ = target.api_->did;//CGMainDisplayID();
		CFDictionaryRef mode_ = CGDisplayCurrentMode( target_display_ );
		generateCanvasInstance(mode_, target_display_);
	}
	void APICanvasPropertiesFullscreen::generateCanvasInstance(int d_width, int d_height, int d_colordepth, double d_refreshrate, const Display &target) {
		// note: true refresh rate is not integer
		target_display_ = target.api_->did;//CGMainDisplayID();
		boolean_t exactMatch;
		CFDictionaryRef mode_ = CGDisplayBestModeForParametersAndRefreshRate(target_display_, (size_t)d_colordepth, (size_t)d_width, (size_t)d_height, (CGRefreshRate)d_refreshrate, &exactMatch );
		if(!exactMatch) throw Exception(typeid(*this), "API Error", "Specified display mode was not available.");
		generateCanvasInstance(mode_, target_display_);
	}
	void APICanvasPropertiesFullscreen::generateCanvasInstance(CFDictionaryRef d_mode, CGDirectDisplayID target) {
		original_mode_ = CGDisplayCurrentMode( target );
		
		dispOpenGLDisplayMask = 0;
		CGDisplayCapture( target );
		CGDisplaySwitchToMode( target, d_mode );
		
		CGDisplayHideCursor( target );
		CGOpenGLDisplayMask displayMask = CGDisplayIDToOpenGLDisplayMask( target );
		WindowMetrix metrix = getDisplayMetrix(target);
		width_=metrix.width; height_=metrix.height; colordepth_=metrix.colordepth; refreshrate_=metrix.refreshrate;
		
		CGLPixelFormatObj dispPixFormatObj;
		CGLPixelFormatAttribute dispPixFormatAtt[32] = {
			kCGLPFADoubleBuffer,
			kCGLPFAFullScreen,
			kCGLPFANoRecovery,
			kCGLPFAAuxBuffers,
			(_CGLPixelFormatAttribute)2,
			kCGLPFADisplayMask,
			(_CGLPixelFormatAttribute)displayMask,
			(_CGLPixelFormatAttribute)dispOpenGLDisplayMask,
		};
		GLint syncronizeSwapIntervalWithVerticalRetraceOfTheDisplay = 1;
		GLint num;
		CGLError error;
		
		if( ! CGLChoosePixelFormat(dispPixFormatAtt, &dispPixFormatObj, &num) ) {
			if( ! CGLCreateContext( dispPixFormatObj, NULL, &cgl_context_) ) {
				if( cgl_context_ ) {
					error = CGLSetParameter( cgl_context_, kCGLCPSwapInterval, &syncronizeSwapIntervalWithVerticalRetraceOfTheDisplay );
					vsync_available_ = error ? false : true;
					CGLSetCurrentContext( cgl_context_ );
					CGLSetFullScreen( cgl_context_ );
					has_instance_ = true;
				}
				CGLDestroyPixelFormat( dispPixFormatObj );
			}
		}
		quartz_context_ = CGDisplayGetDrawingContext(target);
		gamma_mode_ = Color::NOTHING;
		CGRect rect = CGDisplayBounds(target);
		lefttop.set(rect.origin.x, rect.origin.y);
	}
	void APICanvasPropertiesFullscreen::destroyCanvasInstance() {
		if(has_instance_) {
			destroyGammaSettings();
			CGDisplayShowCursor( kCGDirectMainDisplay );
			CGLSetCurrentContext( NULL );
			CGLClearDrawable( cgl_context_ );
			CGLDestroyContext( cgl_context_ );	// error has occured
			
			CGDisplaySwitchToMode( kCGDirectMainDisplay, original_mode_ );
			CGDisplayRelease( kCGDirectMainDisplay );
			//CGDisplayErr disperr = 
			CGDisplayRelease( kCGDirectMainDisplay );
			has_instance_ = false;
		}
	}
	void APICanvasPropertiesFullscreen::flip() {
		CGLFlushDrawable( cgl_context_ );
	}
	void APICanvasPropertiesFullscreen::waitRefresh() {
		CGDisplayWaitForBeamPositionOutsideLines(target_display_, 1, height_-1);
		CGDisplayWaitForBeamPositionOutsideLines(target_display_, 0, 1);
	}
	Point APICanvasPropertiesFullscreen::left_top() {
		return lefttop;
	}
	
	
	////////	APICanvasPropertiesWindow	////////
	APICanvasPropertiesWindow::APICanvasPropertiesWindow()
	: api_objc_(0)
	{
	}
	APICanvasPropertiesWindow::APICanvasPropertiesWindow(int d_width, int d_height, const Display& dd)
	: api_objc_(0)
	{
		target_display_ = dd.api_->did;
		generateCanvasInstance(dd.area.getLeft() + 10, dd.area.getTop() + 30, d_width, d_height);
	}
	APICanvasPropertiesWindow::APICanvasPropertiesWindow(int d_left, int d_top, int d_width, int d_height)
	: api_objc_(0)
	{
		target_display_ = CGMainDisplayID();
		generateCanvasInstance(d_left, d_top, d_width, d_height);
	}
	APICanvasPropertiesWindow::~APICanvasPropertiesWindow() {
		destroyCanvasInstance();
		APICanvasPropertiesObjc *api_objc = reinterpret_cast<APICanvasPropertiesObjc*>(api_objc_);
//		[api_objc->pool release];
		delete api_objc;
		api_objc_ = 0;
	}
	void APICanvasPropertiesWindow::generateCanvasInstance(int d_left, int d_top, int d_width, int d_height) {
		//OSStatus err = noErr;
		APICanvasPropertiesObjc *api_objc = new APICanvasPropertiesObjc();
		NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
		//api_objc->pool = [[NSAutoreleasePool alloc] init];
		api_objc->app = [NSApplication sharedApplication];
//		[api_objc->app finishLaunching];
//		[api_objc->app setMainMenu: [[NSMenu alloc] initWithTitle:@"Psychlops"] ];
		api_objc_ = api_objc;

		//NS *window  = [[NSWindow alloc] init];
		
		const GLint swap = 1;
		
		width_ = d_width, height_ =d_height;
		colordepth_ = 32;
		refreshrate_ = 60.0;
		
		Rect rectPort;
		rectPort.left  = d_left;
		rectPort.right = rectPort.left + d_width;
		rectPort.top   = d_top;
		rectPort.bottom= rectPort.top + d_height;
		const Rect* rp = &rectPort;
		CreateNewWindow( /*kOverlayWindowClass*/kDocumentWindowClass , 0, rp, &window_ );
		// replace NSWindow next time
		
		GLint attributes[] =  { AGL_RGBA, AGL_DOUBLEBUFFER, AGL_DEPTH_SIZE, 24, AGL_AUX_BUFFERS, 2, AGL_NONE };
		agl_context_ = NULL;
		
		AGLPixelFormat myAGLPixelFormat;
		myAGLPixelFormat = aglChoosePixelFormat (NULL, 0, attributes); 
		//err = MyAGLReportError();
		if (myAGLPixelFormat) { 
			agl_context_ = aglCreateContext (myAGLPixelFormat, NULL); 
			//err = MyAGLReportError (); 
		}
		if (agl_context_) {
#if DEBUG
			// if we are in debug mode
			aglEnable (agl_context_, AGL_FS_CAPTURE_SINGLE);
#else
			aglDisable (agl_context_, AGL_FS_CAPTURE_SINGLE);
#endif
			if(!aglSetDrawable(agl_context_, GetWindowPort(window_))) throw Exception("AGL: failed to attactch context to the window.");
			//			if(!aglSetWindowRef(agl_context_, window_)) throw Exception("AGL: failed to attactch context to the window.");
			if(!aglSetCurrentContext(agl_context_)) throw Exception("AGL: failed to set current context.");
			if (!aglSetInteger (agl_context_, AGL_SWAP_INTERVAL, &swap)) throw Exception("AGL: failed to set vsync.");
		}
		// add codes to get window quartz context.

		ShowWindow(window_);
		CGDisplayHideCursor( target_display_ );
	}
	void APICanvasPropertiesWindow::destroyCanvasInstance() {
		if(has_instance_) {
			destroyGammaSettings();
			aglSetDrawable (agl_context_, NULL);
			aglSetCurrentContext (NULL);
			aglDestroyContext (agl_context_);		
			agl_context_ = NULL;
			DisposeWindow(window_);
			CGDisplayShowCursor( target_display_ );
			has_instance_ = false;
		}
	}
	void APICanvasPropertiesWindow::flip() {
		aglSwapBuffers( agl_context_ );
	}
	Point APICanvasPropertiesWindow::left_top() {
		Rect rect;
		GetWindowBounds(window_, kWindowContentRgn, &rect);
		return Point(rect.left, rect.top);
	}
	
	////////	APIFontProperties	////////
	
	APIFontProperties::APIFontProperties() : cached(false), layout_defined(false), theUnicodeText(0) {
	}
	APIFontProperties::~APIFontProperties() {
		if(layout_defined) {
			if(theUnicodeText!=0) free(theUnicodeText);
			ATSUDisposeStyle(theStyle);
			ATSUDisposeTextLayout(theLayout);
			layout_defined = false;
		}
		if(cached) {
			cache_img.release();
		}
	}
	void APIFontProperties::cache(const CGContextRef &ctx, Letters &letters, DrawableWithCache &target) {
		cache_on_image(letters, target);
	}
	void APIFontProperties::uncache(const CGContextRef &ctx, Letters &letters) {
		//cache_img.uncache();
		cache_img.release();
		cached = false;
	}
	void APIFontProperties::draw(const CGContextRef &ctx, const Letters &letters, const double x, const double y, const Color &col, const int horiz_align, const double max_width, DrawableWithCache &target) {
		draw_cached_image(x, y, col, target);
	}
	void APIFontProperties::setLayout(Letters &letters) {
		if(!layout_defined) {
#if defined(PANTHER) && ( PANTHER ==1 )
			CFStringRef str = CFStringCreateWithBytes(kCFAllocatorDefault, (const UInt8*)(letters.str.c_str()), letters.str.length()*4, kCFStringEncodingUnicode, false);
			CFStringRef fontfaceName = CFStringCreateWithBytes(kCFAllocatorDefault, (const UInt8*)(letters.font.family.at(0).c_str()), letters.font.family.at(0).length()*4, kCFStringEncodingUnicode, false);
#else
			CFStringRef str = CFStringCreateWithBytes(kCFAllocatorDefault, (const UInt8*)(letters.str.c_str()), letters.str.length()*4, kCFStringEncodingUTF32LE, false);
			CFStringRef fontfaceName = CFStringCreateWithBytes(kCFAllocatorDefault, (const UInt8*)(letters.font.family.at(0).c_str()), letters.font.family.at(0).length()*4, kCFStringEncodingUTF32LE, false);
#endif
			//CFShowStr(str);
			const char * fontName = CFStringGetCStringPtr(fontfaceName, kCFStringEncodingMacRoman);
			int fontNameLength = CFStringGetLength(fontfaceName);
			
			OSStatus err;
			size_t textLength = CFStringGetLength(str);
			if(theUnicodeText!=0) free(theUnicodeText);
			theUnicodeText = (UniChar*)calloc(textLength, sizeof(UniChar));
			CFStringGetCharacters(str, CFRangeMake(0, textLength), theUnicodeText);
			
			ATSUFontID atsuFontID;
			err = ATSUFindFontFromName(fontName, fontNameLength, kFontPostscriptName, kFontNoPlatformCode, kFontNoScriptCode, kFontNoLanguageCode, &atsuFontID);
			
			Fixed atsuSize = FloatToFixed(letters.font.size);
			
			
			ATSUAttributeTag theTags[] = {
				kATSUFontTag, kATSUSizeTag, kATSUVerticalCharacterTag,
				kATSUQDBoldfaceTag, kATSUQDItalicTag, kATSUQDUnderlineTag,
				kATSUQDCondensedTag, kATSUQDExtendedTag
			};
			ByteCount theSizes[] = {
				sizeof(ATSUFontID), sizeof(Fixed), sizeof(UInt16),
				sizeof(Boolean), sizeof(Boolean), sizeof(Boolean),
				sizeof(Boolean), sizeof(Boolean)
			};
			ATSUAttributeValuePtr theValues[] = {
				NULL, NULL, NULL, NULL,
				NULL, NULL, NULL, NULL
			};
			
			Boolean trueV=true, falseV=false;
			short atsuOrientation = kATSUStronglyHorizontal;
			theValues[0] = &atsuFontID;
			theValues[1] = &atsuSize;
			theValues[2] = &atsuOrientation;
			theValues[3] = (letters.font.weight==Font::normal_weight ? &falseV : &trueV);
			theValues[4] = (letters.font.style==Font::normal_style ? &falseV : &trueV);
			theValues[5] = &falseV; //underline
			theValues[6] = &falseV; //condense
			theValues[7] = &falseV; //extend
			
			ATSUCreateStyle(&theStyle);
			ATSUSetAttributes( theStyle, 8, theTags, theSizes, theValues );
			
			ATSUCreateTextLayoutWithTextPtr(theUnicodeText, 0, textLength, textLength, 1, &textLength, &theStyle, &theLayout);
			
			::Rect metrix;
			ATSUMeasureTextImage(theLayout, 0, textLength, 0, 0, &metrix);
			cache_w = letters.width_ = abs(metrix.right-metrix.left);
			cache_h = letters.height_ = abs(metrix.bottom-metrix.top);
			cache_baseX = metrix.left;
			cache_baseY = abs(metrix.top);
			
			layout_defined = true;
		}
	}
	void APIFontProperties::cache_on_context(const CGContextRef &ctx) {
		if(layout_defined) {
			ATSUAttributeTag      theTags  [1];//2
			ByteCount             theSizes [1];//2
			ATSUAttributeValuePtr theValues[1];//2
			
			theTags  [0] = kATSUCGContextTag;
			theSizes [0] = sizeof(CGContextRef);
			theValues[0] = (void *)&ctx;
			ATSUSetLayoutControls(theLayout, 1, theTags, theSizes, theValues);
			
			cached = true;
		}
	}
	void APIFontProperties::draw_on_context(const CGContextRef &ctx, const float posX, const float posY, const double r,  const double g,  const double b, const double a) {
		if(layout_defined) {
			CGContextSetRGBFillColor (ctx, r, g, b, a);
			ATSUDrawText(theLayout, kATSUFromTextBeginning, kATSUToTextEnd, FloatToFixed(posX), FloatToFixed(posY) );
		}
	}
	void APIFontProperties::cache_on_image(Letters &letters, DrawableWithCache &target) {
		if(!cached) {
			setLayout(letters);
			if(cache_w<=0) cache_w=1;
			if(cache_h<=0) cache_h=1;
			cache_img.set(cache_w,cache_h,Image::RGBA);
			cache_img.clear(Color(0.0,0.0,0.0,0.0,false));
			
			if(cache_img.api_->pic_!=0) {
				cache_on_context(cache_img.api_->pic_);
				draw_on_context(cache_img.api_->pic_, -cache_baseX, cache_h-cache_baseY, 0,0,0,1);
			}
			
			const size_t lineb = cache_img.lineBytes_, h = cache_img.height_-1;
			unsigned char *tmp = new unsigned char[lineb];
			unsigned char *bitmapub_ = cache_img.bitmapub_;
			for(size_t i=0; i<(size_t)((h+1)/2); i++) {
				memcpy(tmp, bitmapub_+lineb*i, lineb);
				memcpy(bitmapub_+lineb*i, bitmapub_+lineb*(h-i), lineb);
				memcpy(bitmapub_+lineb*(h-i), tmp, lineb);
			}
			delete [] tmp;
			cache_img.cache(target);
			cached = true;
		}
	}
	void APIFontProperties::draw_cached_image(const double posX, const double posY, const Color &col, DrawableWithCache& target) {
		if(cached) {
			const Color zero_alpha(1,1,1,0,false);
			glPushAttrib(GL_COLOR_BUFFER_BIT);
			cache_img.move_to(posX, posY-cache_baseY);
			
			// clear destination alpha
			glBlendFunc(GL_ZERO, GL_SRC_COLOR);
			cache_img.targetarea_.draw(zero_alpha, target);
			
			// draw font in alpha channel
			glBlendFunc(GL_ONE, GL_ONE);
			cache_img.draw(target);
			
			// apply alpha factor
			if(col.getA()!=1) {
				Color alpha_mask(1,1,1,col.getA(),false);
				glBlendFunc(GL_ZERO, GL_SRC_COLOR);
				cache_img.targetarea_.draw(alpha_mask, target);
			}
			
			// drawing in specified color by destination alpha
			glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA);
			cache_img.targetarea_.draw(col, target);
			
			//		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
			glPopAttrib();
		}
	}
	
	
	
	
	
	////////	APIImageProperties	////////
	
	//	int APIImageProperties::max_reg = 10;
	//	int APIImageProperties::rights_[10+1], APIImageProperties::bottoms_[10+1];
	
	APIImageProperties::APIImageProperties() : pic_(NULL), 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) : pic_(NULL), outer_(outer) {
		attach();
	}
	APIImageProperties::~APIImageProperties() {
		detach();
	}
	void APIImageProperties::attach() {
		colorSpace = CGColorSpaceCreateDeviceRGB();
		if(pic_==NULL && outer_!=NULL && outer_->pixprec_==Image::BYTE && outer_->pixcomp_==Image::RGBA) {
			pic_ = CGBitmapContextCreate(outer_->bitmapub_, outer_->width_, outer_->height_, 8, outer_->lineBytes_, colorSpace, kCGImageAlphaPremultipliedLast);
			//std::cout << pic_ << std::endl;
		}
	}
	void APIImageProperties::detach() {
		if (pic_ != NULL) { CGContextRelease(pic_); pic_=NULL; }
		CGColorSpaceRelease( colorSpace );
	}
	/*	void APIImageProperties::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(pic_!=0) {
	 if(!letters.cache_id.empty()) {
	 letters.cache_id.push_back(new APIFontProperties);
	 CFStringRef cftext = CFStringCreateWithBytes(kCFAllocatorDefault, (const UInt8*)(letters.str.c_str()), letters.str.length()*4, kCFStringEncodingUTF32LE, false);
	 letters.cache_id.at(0)->setLayout(cftext, letters.font);
	 letters.cache_id.at(0)->cache_on_context(pic_);
	 }
	 letters.cache_id.at(0)->draw(pic_, x, y, r, g, b, a);
	 }
	 }
	 */
	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 	*/

