#from Carbon.Qd cimport GrafPort, CGrafPtr

cdef extern from "Carbon/Carbon.h":
	struct OpaqueGrafPtr
	ctypedef OpaqueGrafPtr *GrafPtr
	
	struct OpaqueRgnHandle
	ctypedef OpaqueRgnHandle *RgnHandle
	
	ctypedef struct Rect:
		short top, left, bottom, right
	
	void GetPort(GrafPtr *port)
	Rect *GetPortBounds(GrafPtr port, Rect *rect)
	RgnHandle NewRgn()
	void DisposeRgn(RgnHandle rgn)
	void GetPortClipRegion(GrafPtr port, RgnHandle rgn)
	Rect *GetRegionBounds(RgnHandle rgn, Rect *rect)
	
cdef extern from "agl.h":

	enum:
		AGL_RGBA
		AGL_DOUBLEBUFFER
		AGL_ALPHA_SIZE
		AGL_DEPTH_SIZE
		AGL_STENCIL_SIZE
		AGL_ACCUM_RED_SIZE
		AGL_ACCUM_GREEN_SIZE
		AGL_ACCUM_BLUE_SIZE
		AGL_ACCUM_ALPHA_SIZE
		AGL_BUFFER_RECT
		AGL_NONE
		
	ctypedef long GLint
	ctypedef unsigned char GLboolean
	ctypedef unsigned char GLubyte
	ctypedef unsigned long GLenum
	
	struct __AGLPixelFormatRec
	ctypedef __AGLPixelFormatRec *AGLPixelFormat
	
	struct __AGLContextRec 
	ctypedef __AGLContextRec *AGLContext
	
	ctypedef GrafPtr AGLDrawable
	
	AGLPixelFormat aglChoosePixelFormat (
    void *gdevs, #const AGLDevice *gdevs, 
    GLint ndev, 
    GLint *attribs)
    
	AGLContext aglCreateContext (
		AGLPixelFormat pix, 
		AGLContext share)
		
	GLboolean aglSetDrawable (
		AGLContext ctx, 
		AGLDrawable draw)
	
	void aglDestroyPixelFormat (
		AGLPixelFormat pix)
	
	GLboolean aglDestroyContext (
		AGLContext ctx)
		
	AGLContext aglGetCurrentContext ()
	
	GLboolean aglSetCurrentContext (
		AGLContext ctx)
	
	GLboolean aglEnable (
    AGLContext ctx, 
    GLenum pname)
	
	GLboolean aglSetInteger (
		AGLContext ctx, 
    GLenum pname, 
    GLint *params)

	void aglSwapBuffers (
		AGLContext ctx)
	
	GLenum aglGetError ()
	
	GLubyte *aglErrorString (
		GLenum code)


class AGLError(StandardError):

	def __init__(self, funcname):
		message = <char*>aglErrorString(aglGetError())
		Exception.__init__("%s: %s" % (funcname, message))


cdef class Context:

	cdef AGLContext agl_context
	
	def __new__(self, Context share,
			int double_buffer, int alpha_size, int depth_size,
			int stencil_size, int accum_size):
		cdef AGLContext agl_share
		cdef GLint attribs[20]
		cdef int ap
		cdef AGLPixelFormat format
		cdef int result
		if share is not None:
			agl_share = share.agl_context
		else:
			agl_share = NULL
		attribs[0] = AGL_RGBA
		ap = 1
		if alpha_size:
			attribs[ap] = AGL_ALPHA_SIZE
			attribs[ap + 1] = alpha_size
			ap = ap + 2
		if depth_size:
			attribs[ap] = AGL_DEPTH_SIZE
			attribs[ap + 1] = depth_size
			ap = ap + 2
		if stencil_size:
			attribs[ap] = AGL_STENCIL_SIZE
			attribs[ap + 1] = stencil_size
			ap = ap + 2
		if accum_size:
			attribs[ap] = AGL_ACCUM_RED_SIZE
			attribs[ap + 1] = accum_size
			attribs[ap + 2] = AGL_ACCUM_GREEN_SIZE
			attribs[ap + 3] = accum_size
			attribs[ap + 4] = AGL_ACCUM_BLUE_SIZE
			attribs[ap + 5] = accum_size
			ap = ap + 6
			if alpha_size:
				attribs[ap] = AGL_ACCUM_ALPHA_SIZE
				attribs[ap + 1] = alpha_size
				ap = ap + 2
		if double_buffer:
			attribs[ap] = AGL_DOUBLEBUFFER
			ap = ap + 1
		attribs[ap] = AGL_NONE
		format = aglChoosePixelFormat(NULL, 0, attribs)
		if not format and double_buffer:
			attribs[ap - 1] = AGL_NONE
			format = aglChoosePixelFormat(NULL, 0, attribs)
		if not format:
			raise_agl_error("aglChoosePixelFormat")
		try:
			self.agl_context = aglCreateContext(format, agl_share)
			if not self.agl_context:
				raise_agl_error("aglCreateContext")
		finally:
			aglDestroyPixelFormat(format)
	
	def __dealloc__(self):
		if self.agl_context:
			if self.agl_context == aglGetCurrentContext():
				aglSetCurrentContext(NULL)
			aglSetDrawable(self.agl_context, NULL)
			aglDestroyContext(self.agl_context)
	
	def update_port(self):
		"""Attach the context to the current grafport, and set
		the buffer rect from the bounds of the current clip
		region."""
		cdef GLint params[4]
		cdef GrafPtr port
		cdef RgnHandle clip_rgn
		cdef Rect port_rect, buffer_rect
		#---
		GetPort(&port)
		GetPortBounds(port, &port_rect)
		clip_rgn = NewRgn()
		GetPortClipRegion(port, clip_rgn)
		GetRegionBounds(clip_rgn, &buffer_rect)
		DisposeRgn(clip_rgn)
		#  The buffer rect is specified relative to the *bottom
		#  left* corner of the port rect, just to be awkward...
		params[0] = buffer_rect.left - port_rect.left
		params[1] = port_rect.bottom - buffer_rect.bottom
		params[2] = buffer_rect.right - buffer_rect.left
		params[3] = buffer_rect.bottom - buffer_rect.top
		if not aglSetDrawable(self.agl_context, port):
			raise_agl_error("aglSetDrawable")
		if not aglEnable(self.agl_context, AGL_BUFFER_RECT):
			raise_agl_error("aglEnable")
		if not aglSetInteger(self.agl_context, AGL_BUFFER_RECT, params):
			raise_agl_error("aglSetInteger")
		
	def swap_buffers(self):
		aglSwapBuffers(self.agl_context)

	def with_current(self, proc):
		"""Execute the proc with this context as the current context."""
		cdef AGLContext old_agl_context
		old_agl_context = aglGetCurrentContext()
		if not aglSetCurrentContext(self.agl_context):
			raise_agl_error("aglSetCurrentContext")
		try:
			proc()
		finally:
			aglSetCurrentContext(old_agl_context)
