/* $Id: toglNSOpenGL.c,v 1.7 2009/10/22 00:06:41 gregcouch Exp $ */

/* vi:set sw=4 expandtab: */

/* 
 * Togl - a Tk OpenGL widget
 *
 * Copyright (C) 1996-2002  Brian Paul and Ben Bederson
 * Copyright (C) 2005-2009  Greg Couch
 * See the LICENSE file for copyright details.
 */

static NSOpenGLPixelFormat *
togl_pixelFormat(Togl *togl)
{
    NSOpenGLPixelFormatAttribute   attribs[32];
    int     na = 0;
    NSOpenGLPixelFormat *pix;

#if 0
    if (togl->MultisampleFlag && !hasMultisampling) {
        Tcl_SetResult(togl->Interp,
                TCL_STUPID "multisampling not supported", TCL_STATIC);
        return NULL;
    }
#endif

    if (togl->PbufferFlag && !togl->RgbaFlag) {
        Tcl_SetResult(togl->Interp,
                TCL_STUPID "puffer must be RGB[A]", TCL_STATIC);
        return NULL;
    }

    attribs[na++] = NSOpenGLPFAMinimumPolicy;
    /* ask for hardware-accelerated onscreen */
    attribs[na++] = NSOpenGLPFAAccelerated;
    attribs[na++] = NSOpenGLPFANoRecovery;
    if (togl->RgbaFlag) {
        /* RGB[A] mode */
        attribs[na++] = NSOpenGLPFAColorSize;
	attribs[na++] = togl->RgbaRed + togl->RgbaGreen + togl->RgbaBlue;
	/* NSOpenGL does not take separate red,green,blue sizes. */
        if (togl->AlphaFlag) {
            attribs[na++] = NSOpenGLPFAAlphaSize;
            attribs[na++] = togl->AlphaSize;
        }
    } else {
        /* Color index mode */
        Tcl_SetResult(togl->Interp,
                TCL_STUPID "Color index mode not supported", TCL_STATIC);
        return NULL;
    }
    if (togl->DepthFlag) {
        attribs[na++] = NSOpenGLPFADepthSize;
        attribs[na++] = togl->DepthSize;
    }
    if (togl->DoubleFlag) {
        attribs[na++] = NSOpenGLPFADoubleBuffer;
    }
    if (togl->StencilFlag) {
        attribs[na++] = NSOpenGLPFAStencilSize;
        attribs[na++] = togl->StencilSize;
    }
    if (togl->AccumFlag) {
        attribs[na++] = NSOpenGLPFAAccumSize;
        attribs[na++] = togl->AccumRed + togl->AccumGreen + togl->AccumBlue + (togl->AlphaFlag ? togl->AccumAlpha : 0);
    }
    if (togl->MultisampleFlag) {
        attribs[na++] = NSOpenGLPFAMultisample;
        attribs[na++] = NSOpenGLPFASampleBuffers;
        attribs[na++] = 1;
        attribs[na++] = NSOpenGLPFASamples;
        attribs[na++] = 2;
    }
    if (togl->AuxNumber != 0) {
        attribs[na++] = NSOpenGLPFAAuxBuffers;
        attribs[na++] = togl->AuxNumber;
    }
    if (togl->Stereo == TOGL_STEREO_NATIVE) {
        attribs[na++] = NSOpenGLPFAStereo;
    }
    if (togl->FullscreenFlag) {
        attribs[na++] = NSOpenGLPFAFullScreen;
    }
    attribs[na++] = 0;	/* End of attributes. */

    pix = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs];
    if (pix == nil) {
        Tcl_SetResult(togl->Interp, TCL_STUPID "couldn't choose pixel format",
                TCL_STATIC);
        return NULL;
    }
    return pix;
}

static int
togl_describePixelFormat(Togl *togl)
{
    NSOpenGLPixelFormat *pfmt = togl->PixelFormat;

    /* fill in RgbaFlag, DoubleFlag, and Stereo */
    GLint   has_rgba, has_doublebuf, has_depth, has_accum, has_alpha,
            has_stencil, has_stereo, has_multisample;

    GLint   vscr = 0;
    [pfmt getValues:&has_rgba forAttribute:NSOpenGLPFAColorSize forVirtualScreen:vscr];
    [pfmt getValues:&has_doublebuf forAttribute:NSOpenGLPFADoubleBuffer forVirtualScreen:vscr];
    [pfmt getValues:&has_depth forAttribute:NSOpenGLPFADepthSize forVirtualScreen:vscr];
    [pfmt getValues:&has_accum forAttribute:NSOpenGLPFAAccumSize forVirtualScreen:vscr];
    [pfmt getValues:&has_alpha forAttribute:NSOpenGLPFAAlphaSize forVirtualScreen:vscr];
    [pfmt getValues:&has_stencil forAttribute:NSOpenGLPFAStencilSize forVirtualScreen:vscr];
    [pfmt getValues:&has_stereo forAttribute:NSOpenGLPFAStereo forVirtualScreen:vscr];
    [pfmt getValues:&has_multisample forAttribute:NSOpenGLPFASampleBuffers forVirtualScreen:vscr];

    togl->RgbaFlag = (has_rgba != 0);
    togl->DoubleFlag = (has_doublebuf != 0);
    togl->DepthFlag = (has_depth != 0);
    togl->AccumFlag = (has_accum != 0);
    togl->AlphaFlag = (has_alpha != 0);
    togl->StencilFlag = (has_stencil != 0);
    togl->Stereo = (has_stereo ? TOGL_STEREO_NATIVE : TOGL_STEREO_NONE);
    togl->MultisampleFlag = (has_multisample != 0);
    return True;
}

#define isPow2(x) (((x) & ((x) - 1)) == 0)

static NSOpenGLPixelBuffer *
togl_createPbuffer(Togl *togl)
{
    GLint   min_size[2], max_size[2];
    Bool    hasPbuffer;
    const char *extensions;
    GLint   target;
    GLint   virtualScreen;
    NSOpenGLPixelBuffer *pbuf;

    extensions = (const char *) glGetString(GL_EXTENSIONS);
    hasPbuffer = (strstr(extensions, "GL_APPLE_pixel_buffer") != NULL);
    if (!hasPbuffer) {
        Tcl_SetResult(togl->Interp,
                TCL_STUPID "pbuffers are not supported", TCL_STATIC);
        return NULL;
    }
    glGetIntegerv(GL_MIN_PBUFFER_VIEWPORT_DIMS_APPLE, min_size);
    glGetIntegerv(GL_MAX_VIEWPORT_DIMS, max_size);
    virtualScreen = [togl->Ctx currentVirtualScreen];
    for (;;) {
        /* make sure we don't exceed the maximum size because if we do,
         * NSOpenGLPixelBuffer allocationmay succeed and later uses of
	 * the pbuffer fail
	 */
        if (togl->Width < min_size[0])
            togl->Width = min_size[0];
        else if (togl->Width > max_size[0]) {
            if (togl->LargestPbufferFlag)
                togl->Width = max_size[0];
            else {
                Tcl_SetResult(togl->Interp,
                        TCL_STUPID "pbuffer too large", TCL_STATIC);
                return NULL;
            }
        }
        if (togl->Height < min_size[1])
            togl->Height = min_size[1];
        else if (togl->Height > max_size[1]) {
            if (togl->LargestPbufferFlag)
                togl->Height = max_size[1];
            else {
                Tcl_SetResult(togl->Interp,
                        TCL_STUPID "pbuffer too large", TCL_STATIC);
                return NULL;
            }
        }

        if (isPow2(togl->Width) && isPow2(togl->Height))
            target = GL_TEXTURE_2D;
        else
            target = GL_TEXTURE_RECTANGLE_ARB;

	pbuf = [[NSOpenGLPixelBuffer alloc] initWithTextureTarget:target
		textureInternalFormat:(togl->AlphaFlag ? GL_RGBA : GL_RGB)
		textureMaxMipMapLevel:0
		pixelsWide:togl->Width pixelsHigh:togl->Height];
        if (pbuf != nil) {
            /* setPixelBuffer allocates the framebuffer space */
	  [togl->Ctx setPixelBuffer:pbuf cubeMapFace:0 mipMapLevel:0 
	   currentVirtualScreen:virtualScreen];
	  return pbuf;
	}
        if (!togl->LargestPbufferFlag
                || togl->Width == min_size[0] || togl->Height == min_size[1]) {
            Tcl_SetResult(togl->Interp,
                    TCL_STUPID "unable to create pbuffer", TCL_STATIC);
            return NULL;
        }
        /* largest unavailable, try something smaller */
        togl->Width = togl->Width / 2 + togl->Width % 2;
        togl->Height = togl->Width / 2 + togl->Height % 2;
    }
}

static void
togl_destroyPbuffer(Togl *togl)
{
    [togl->pbuf release];
}