//macscreen.cpp
//mac-specific code to access the screen device
#include <QuickTime/QuickTime.h>
#include <AGL/AGL.h>
#include "screen.h"
#include "config.h"
#include "error.h"
#include "textures.h"

AGLContext gOpenGLContext=NULL;

AGLContext SetupAGL( AGLDrawable window,GLint *attrib,int width, int height)
{
	AGLPixelFormat format;
	AGLContext     context;
	GLboolean      ok;
	GLint attrib2[256];
			
	GDHandle screen;
	if(window)
		screen=GetGWorldDevice((GWorldPtr)window);
	else
		screen=DMGetFirstScreenDevice(true);
	
	if(window==NULL)
	{
		int i=0;
		while(attrib[i]!=AGL_NONE)
			attrib2[i++]=attrib[i];
		attrib2[i++]=AGL_FULLSCREEN;
		//attrib2[i++]=AGL_NO_RECOVERY;
		attrib2[i++]=AGL_NONE;
		attrib=attrib2;
	}
	
	// Choose an rgb pixel format
	format = aglChoosePixelFormat( &screen, 1, attrib );
	if ( format == NULL )
	{
		printf("aglChoosePixelFormat: %s\n",aglErrorString(aglGetError()));
		return NULL;
	}
	// Create an AGL context
	context = aglCreateContext( format, gOpenGLContext );
	if ( context == NULL )
	{
		printf("aglCreateContext: %s\n",aglErrorString(aglGetError()));
		return NULL;
	}

	if(window){
		ok = aglSetDrawable(context,window);
		if ( !ok )
		{
			printf("aglSetDrawable: %s\n",aglErrorString(aglGetError()));
			return NULL;
		}
	}
	else{
		ok = aglSetFullScreen (context,width,height,75,0);
		if ( !ok )
		{
			ok = aglSetFullScreen (context,640,480,75,0);
			if ( !ok )
			{
				printf("aglSetFullScreen: %s\n",aglErrorString(aglGetError()));
				return NULL;
			}
		}
	}
	
	// Make the context the current context
	ok = aglSetCurrentContext( context );
	if ( !ok )
	{
		printf("aglSetCurrentContext: %s\n",aglErrorString(aglGetError()));		
		return NULL;
	}
	// The pixel format is no longer needed so get rid of it
	aglDestroyPixelFormat( format );
	return context;	
}

//Initialize an OpenGL context using AGL
void InitGLContext()
{
	CGrafPtr theScreen=NULL;
	
	//is fullscreen mode enabled?
	if(gConfig->fullscreen)
		theScreen=NULL;
	else
	{
		//get a Window Context
		Rect boundsRect;
		if(gConfig->windowY<40)
			gConfig->windowY=40;
		SetRect(&boundsRect,gConfig->windowX,gConfig->windowY,gConfig->windowX+gConfig->screenXSize,gConfig->windowY+gConfig->screenYSize);
		WindowRef win=NewCWindow(0,&boundsRect,"\pRedline",true,0,(WindowRef)-1L,false,0);
		if(!RectInRgn(&boundsRect,GetGrayRgn()))
			MoveWindow(win,40,40,true);
		theScreen=GetWindowPort(win);
		SetPort(theScreen);
		GetPortBounds(theScreen,&boundsRect);
		Pattern black;
		GetQDGlobalsBlack(&black);
		FillRect(&boundsRect,&black);
		HIWindowFlush(FrontWindow());
	}

	// Setup the OpenGL context
	AGLContext ctx;
	if(gConfig->fsaa)
	{
		GLint attrib[] = { AGL_RGBA, AGL_PIXEL_SIZE, gConfig->color32Bit?32:16,AGL_NO_RECOVERY,AGL_DOUBLEBUFFER, AGL_STENCIL_SIZE, 8,AGL_SAMPLE_BUFFERS_ARB,1,AGL_SAMPLES_ARB,gConfig->fsaa, AGL_NONE};	   
	//	GLint attrib[] = { AGL_RGBA, AGL_PIXEL_SIZE, gConfig->color32Bit?32:16,AGL_DOUBLEBUFFER, AGL_STENCIL_SIZE, 8,AGL_SAMPLE_BUFFERS_ARB,1,AGL_SAMPLES_ARB,gConfig->fsaa, AGL_NONE};	   
		ctx=SetupAGL((AGLDrawable)theScreen,attrib,gConfig->screenXSize,gConfig->screenYSize);
	}
	else
	{
		GLint attrib[] = { AGL_RGBA, AGL_PIXEL_SIZE, gConfig->color32Bit?32:16,AGL_NO_RECOVERY,AGL_DOUBLEBUFFER, AGL_STENCIL_SIZE, 8, AGL_NONE};	   
	//	GLint attrib[] = { AGL_RGBA, AGL_PIXEL_SIZE, gConfig->color32Bit?32:16,AGL_DOUBLEBUFFER, AGL_STENCIL_SIZE, 8, AGL_NONE};	   
		ctx=SetupAGL((AGLDrawable)theScreen,attrib,gConfig->screenXSize,gConfig->screenYSize);
	}
	if(!ctx)
	{
		if(gConfig->fullscreen)
		{
			gConfig->fullscreen=false;
			InitGLContext();
		}
		else
			FailWithErrorString("Couldn't Create Screen");
	}
	else
	{
		if(gOpenGLContext)
			aglDestroyContext(gOpenGLContext);
		gOpenGLContext=ctx;
	}
}

static int numberForKey( CFDictionaryRef desc, CFStringRef key )
{
    CFNumberRef value;
    int num = 0;

    if ( (value = (CFNumberRef)CFDictionaryGetValue(desc, key)) == NULL )
        return 0;
    CFNumberGetValue(value, kCFNumberIntType, &num);
    return num;
}

void ScreenGetModes()
{
	CFArrayRef modeList;
    CFIndex i, cnt;
	
	gVideoNumModes=0;
    modeList = CGDisplayAvailableModes(kCGDirectMainDisplay);
    if ( modeList == NULL )
    {
        printf( "Display is invalid\n" );
        exit(1);
    }
    cnt = CFArrayGetCount(modeList);
    for ( i = 0; i < cnt; ++i )
    {
        CFDictionaryRef desc = (CFDictionaryRef)CFArrayGetValueAtIndex(modeList, i);
		int depth=numberForKey(desc, kCGDisplayBitsPerPixel);
		if(depth==32&&gVideoNumModes<kMaxModes)
		{
			int height=numberForKey(desc, kCGDisplayHeight);
			int width=numberForKey(desc, kCGDisplayWidth);
			int freq=numberForKey(desc, kCGDisplayRefreshRate);
			int exists=false;
			for(int j=0;j<gVideoNumModes;j++)
				if(gVideoModes[j].height==height&&gVideoModes[j].width==width)
				{
					if(freq<gVideoModes[j].freq&&freq>=60)
						gVideoModes[j].freq=freq;
					exists=true;
				}
			if(!exists)
			{
				gVideoModes[gVideoNumModes].height=height;
				gVideoModes[gVideoNumModes].width=width;
				gVideoModes[gVideoNumModes].freq=freq;
				gVideoNumModes++;
			}
		}
    }
	
	for(int i=0;i<gVideoNumModes;i++)
		for(int j=0;j<gVideoNumModes-1-i;j++)
			if(gVideoModes[j].width<gVideoModes[j+1].width||(gVideoModes[j].width==gVideoModes[j+1].width&&gVideoModes[j].height<gVideoModes[j+1].height))
			{
				tVideoMode tmp=gVideoModes[j];
				gVideoModes[j]=gVideoModes[j+1];
				gVideoModes[j+1]=tmp;
			}
}

void ScreenExit()
{
#ifndef __TARGET_TOOLAPP
	aglSetDrawable(gOpenGLContext,NULL);
	if(FrontWindow())
		DisposeWindow(FrontWindow());
	InitCursor();
#endif
}

//Swap front- and backbuffer
void ScreenBlit()
{
	aglSwapBuffers(gOpenGLContext);
}

int GetVRAMSize()
{
	char str[256];

	AGLRendererInfo info, head_info;
	GLint inum;
	GLint dAccel = 0;
	GLint dVRAM = 0, dMaxVRAM = 0;
	AGLDevice gd=GetMainDevice();
	head_info = aglQueryRendererInfo(&gd, 1);
	if(!head_info)
		return 0;
	info = head_info;
	inum = 0;
	// see if we have an accelerated renderer, if so ignore non-accelerated ones
	// this prevents returning info on software renderer when actually we'll get the hardware one

	while (info)
	{
		aglDescribeRenderer (info, AGL_ACCELERATED, &dAccel);
		// if we can accel then we will choose the accelerated renderer
		// how about compliant renderers???
		if (dAccel)
		{
			aglDescribeRenderer (info, AGL_VIDEO_MEMORY, &dVRAM); // we assume that VRAM returned is total thus add texture and VRAM required
			if (dVRAM >= dMaxVRAM) // find card with max VRAM
				dMaxVRAM = dVRAM; // store max
		}
		info = aglNextRendererInfo(info);
		inum++;
	}
//	aglDestroyRendererInfo(head_info);

	return dVRAM;
}

int ScreenSupportsTextureCompression()
{
	static int result=-1;
	if(result==-1)
	{
		char* extensions=(char*)glGetString(GL_EXTENSIONS);
		result=strstr(extensions,"GL_EXT_texture_compression_s3tc")?true:false;

		long resp;
		Gestalt(gestaltSystemVersion,&resp);
		if(resp<0x00001030)
		{
			//GeForce Texture compression seems buggy in 10.2.8
			char* renderer=(char*)glGetString(GL_RENDERER);
			if (strstr(renderer,"GeForce")) 
				result=false;
		}
	}
	return result;
}

int ScreenSupportsAnisotropicFiltering()
{
	static int result=-1;
	if(result==-1)
	{
		char* extensions=(char*)glGetString(GL_EXTENSIONS);
		result=strstr(extensions,"GL_EXT_texture_filter_anisotropic")?true:false;
				
		char* renderer=(char*)glGetString(GL_RENDERER);
		//GeForce 5200 bug
		if (strstr (renderer, "NV34MAP") || (strstr (renderer , "GeForce") && strstr (renderer, "5200"))) 
			result=false;
	}
	return result;
}

int ScreenSupports3DTextures()
{
	static int result=-1;
	if(result==-1)
	{
		/*
		char* extensions=(char*)glGetString(GL_EXTENSIONS);
		result=strstr(extensions,"GL_EXT_texture3D")?true:false;
		*/
		
		result=true; //built-in in GL 1.2 or higher.
		
		char* renderer=(char*)glGetString(GL_RENDERER);
		printf("Renderer: %s\n",renderer);
		//GeForce 2 only has software 3d textures (? - according to unity)
		if (strstr (renderer, "GeForce") && !(strstr (renderer , "TI") || strstr (renderer, "FX"))) 
			result=false;
		//same with Rage128
		if (strstr (renderer, "Rage") && strstr (renderer, "128")) 
			result=false;
	}
	return result;
}

int ScreenSupportsBlendColor()
{
	static int result=-1;
	if(result==-1)
	{
		char* extensions=(char*)glGetString(GL_EXTENSIONS);
		result=strstr(extensions,"GL_ARB_imaging")?true:false;
	}
	return result;
}

int ScreenNoBigTextures()
{
	static int result=-1;
	if(result==-1)
		result=GetVRAMSize()<=1024*1024*8;
	return result;
}

int ScreenNoWindow()
{
	static int result=-1;
	if(result==-1)
	{
		long resp;
		Gestalt(gestaltSystemVersion,&resp);
		result=(resp<0x00001030);
	}
	return result;
}