/*
 *  psychlops_io_display_OSX.h
 *  Psychlops Standard Library (MacOSX)
 *
 *  Last Modified 2009/03/02 by Kenchi HOSOKAWA
 *  (C) 2009 Kenchi HOSOKAWA, Kazushi MARUYA, Takao SATO
 */


#include <string>
#include "../../core/graphic/psychlops_g_canvas.h"
#include "../../core/ApplicationInterfaces/psychlops_code_exception.h"
#include "psychlops_io_display_OSX.h"
#include "../../core/devices/psychlops_io_display.h"


//#define __PSYCHLOPS__OSX_LION


namespace Psychlops {

	APIDisplayProperties::APIDisplayProperties(CGDirectDisplayID ddid) : did(ddid) {}

	Displays APIDisplayProperties::enumDisplays() {
		Displays displays;
		APIDisplayProperties *api;
		CGDisplayCount dspyCnt;
		CGDirectDisplayID activeDspys[32];
		CGDisplayErr err = CGGetActiveDisplayList(32, activeDspys, &dspyCnt);
		if(err!=kCGErrorSuccess) throw Exception("DisplayAPI: display list is not available.");
		for(int i=0; i<dspyCnt; i++) {
			api = new APIDisplayProperties(activeDspys[i]);
			displays.push_back(Display(api));
		}
		return displays;
	}

	void APIDisplayProperties::getInfo(int &width, int &height, int &color_depth, double &refresh_rate, std::string& name, Rectangle& area) {
		CGSize size;
		CGRect bound;
		size = CGDisplayScreenSize(did);
		bound = CGDisplayBounds(did);
		area.set(bound.size.width, bound.size.height).shift(bound.origin.x, bound.origin.y);

		width       = CGDisplayPixelsWide( did );
		height      = CGDisplayPixelsHigh( did );        
#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_6
        CGDisplayModeRef mode = CGDisplayCopyDisplayMode(did);        
        CFStringRef pixEnc = CGDisplayModeCopyPixelEncoding(mode);
        if(CFStringCompare(pixEnc, CFSTR(IO32BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo)
            color_depth = 32;
        else if(CFStringCompare(pixEnc, CFSTR(IO16BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo)
            color_depth = 16;
        else if(CFStringCompare(pixEnc, CFSTR(IO8BitIndexedPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo)
            color_depth = 8;
#else
        color_depth  = CGDisplayBitsPerPixel( did );
#endif
		refresh_rate = 0.0;
		
		CFNumberRef ref_refreshrate;
		CFDictionaryRef mode_ = CGDisplayCurrentMode( did );
		ref_refreshrate = (CFNumberRef) CFDictionaryGetValue(mode_, kCGDisplayRefreshRate);
		if(ref_refreshrate) {
			CFNumberGetValue(ref_refreshrate, kCFNumberDoubleType, &(refresh_rate));
			if(refresh_rate == 0.0) refresh_rate = 60.0;  // Assume LCD screen
		}
	}

	// Hardware Gamma Settings : requires CGDirectDisplayID(target_display_)
	void APIDisplayProperties::setGammaValue(const double gamma_r, const double gamma_g, const double gamma_b) {
		OSErr err;
		saveGammaValue();
		err = CGSetDisplayTransferByFormula( did,
											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 APIDisplayProperties::setGammaTable(const std::vector<double> &table_r, const std::vector<double> &table_g, const std::vector<double> &table_b) {
		OSErr err;
		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();
		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(did, 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 APIDisplayProperties::setGammaTable(const CGGammaValue * const table_r, const CGGammaValue * const table_g, const CGGammaValue * const table_b, const int num_steps) {
		OSErr err;
		saveGammaValue();
		err = CGSetDisplayTransferByTable(did, num_steps, table_r, table_g, table_b);
		gamma_mode_ = Color::TABLE;
	}
	void APIDisplayProperties::setGammaTable() {
		OSErr err;
		saveGammaValue();
		CGByteValue rs[256], gs[256], bs[256];
		for(int i=0; i<256; i++) { rs[i]=i; gs[i]=i; bs[i]=i; }
		err = CGSetDisplayTransferByByteTable(did, 256, rs, gs, bs);
		gamma_mode_ = Color::TABLE;
	}
	void APIDisplayProperties::saveGammaValue() {
		OSErr err;
		if(gamma_mode_==Color::NOTHING) {
			err = CGGetDisplayTransferByFormula( did,
												&savedRedMin_, &savedRedMax_, &savedRedGamma_,
												&savedGreenMin_, &savedGreenMax_, &savedGreenGamma_,
												&savedBlueMin_, &savedBlueMax_, &savedBlueGamma_);
		}
	}
	void APIDisplayProperties::destroyGammaSettings() {
		OSErr err;
		switch(gamma_mode_) {
			case Color::GAMMA_VALUE:
			case Color::TABLE:
			default:
				err = CGSetDisplayTransferByFormula( did,
													savedRedMin_, savedRedMax_, savedRedGamma_,
													savedGreenMin_, savedGreenMax_, savedGreenGamma_,
													savedBlueMin_, savedBlueMax_, savedBlueGamma_);
				break;
		}
		gamma_mode_ = Color::NOTHING;
	}
	

	const std::vector<Display> Display::list() {
		return APIDisplayProperties::enumDisplays();
	}
/*	const Display Display::main() {
		APIDisplayProperties *api;
		api = new APIDisplayProperties( CGMainDisplayID() );
		Display display(api);
		return display;
	}
*/
	

	Display::Display(APIDisplayProperties *apid) : api_(apid) {
		apid->getInfo(width, height, color_depth, refresh_rate, name, area);
	}
/*
	Display::Display(const Display &disp) : api_(disp.api_) {
		width = width;
		height = disp.height;
		color_depth = disp.color_depth;
		refresh_rate = disp.refresh_rate;
		name = disp.name;
		area = disp.area;
	}
	Display::~Display() {
	}
*/
}	/*	<- namespace Psycholops 	*/



