/*****************************************************************************
 *                                         Yumetech, Inc Copyright (c) 2006
 *                                                             Java Source
 *
 * This source is licensed under the BSD-style License
 * Please read http://www.opensource.org/licenses/bsd-license.php for more
 * information or docs/BSD.txt in the downloaded code.
 *
 * This software comes with the standard NO WARRANTY disclaimer for any
 * purpose. Use it at your own risk. If there's a problem you get to fix it.
 *
 ****************************************************************************/

package org.j3d.opengl.swt.internal.ri.x11;

// External imports
import javax.media.opengl.*;

import com.sun.opengl.impl.x11.GLX;
import com.sun.opengl.impl.x11.GLXExt;
import com.sun.opengl.impl.x11.GLXFBConfig;

import org.eclipse.swt.internal.Platform;

// Local imports
// None

/**
 *
 *
 * @author Justin Couch
 * @version $Revision: 1.2 $
 */
class X11PbufferGLDrawable extends X11GLDrawable
{
    private static final int MAX_PFORMATS = 256;
    private static final int MAX_ATTRIBS    = 256;

    /** Constructor message for one or other size <= 0. */
    private static final String NEG_SIZE_MSG =
        "Initial width and height of pbuffer must be positive (were (";

    /** createPbuffer message when we fail to create a framebuffer config */
    private static final String FBCONFIG_FAIL_MSG =
        "pbuffer creation error: glXChooseFBConfig() failed";

    /** createPbuffer message when nothing matched. */
    private static final String NO_VALID_CONFIG_MSG =
        "pbuffer creation error: couldn't find a suitable frame " +
        "buffer configuration";

    /** Message when creating the pbuffer failed */
    private static final String PBUFFER_CREATE_ERR =
        "pbuffer creation error: glXCreatePbuffer() failed";

    /** Have we looked to see if multisample is available? */
    private static boolean checkedMultisample;

    /** Yes or no if it is available */
    private static boolean multisampleAvailable;

    /** Width as initially requested by the user */
    private int requestedWidth;

    /** Height as initially requested by the user */
    private int requestedHeight;

    /** The framebuffer config as drawable in superclass is a GLXPbuffer */
    private GLXFBConfig fbConfig;

    /** The real width of the buffer that was created */
    private int width;

    /** The real height of the buffer that was created */
    private int height;

    /**
     * Create a new drawable instance with the given width and height.
     */
    X11PbufferGLDrawable(GLCapabilities capabilities,
                         int initialWidth,
                         int initialHeight)
    {
        super(capabilities, null);

        if(initialWidth <= 0 || initialHeight <= 0)
            throw new GLException(NEG_SIZE_MSG +
                                  initialWidth + ", " +
                                  initialHeight + "))");


        requestedWidth = initialWidth;
        requestedHeight = initialHeight;

        createPbuffer();
    }

    //---------------------------------------------------------------
    // Methods defined by GLDrawable
    //---------------------------------------------------------------

    public GLContext createContext(GLContext shareWith)
    {
        return new X11PbufferGLContext(this, fbConfig, shareWith);
    }

    public void destroy()
    {
        Platform.lock.lock();

        if(drawable != 0)
        {
            GLX.glXDestroyPbuffer(display, drawable);
            drawable = 0;
        }

        Platform.lock.unlock();
        display = 0;
    }

    public void setSize(int width, int height)
    {
        // FIXME
        throw new GLException("Not yet implemented");
    }

    public int getWidth()
    {
        return width;
    }

    public int getHeight()
    {
        return height;
    }

    //---------------------------------------------------------------
    // Local Methods
    //---------------------------------------------------------------

    /**
     * Do the process of creating the pbuffer now.
     */
    private void createPbuffer()
    {
//        Platform.lock.lock();

        try
        {
            long display = GLX.XOpenDisplay(null);

            if(display == 0)
                throw new GLException("Null display");


            if(capabilities.getPbufferRenderToTexture())
                throw new GLException("Render-to-texture pbuffers not supported yet on X11");

            if(capabilities.getPbufferRenderToTextureRectangle())
                throw new GLException("Render-to-texture-rectangle pbuffers not supported yet on X11");


            int screen = 0; // FIXME: provide way to specify this?
            int[] iattributes =
                X11GLXUtils.glCapabilities2AttribList(capabilities,
                                                      isMultisampleAvailable(),
                                                      true,
                                                      display,
                                                      screen);

            int[] num_elements = new int[1];
            GLXFBConfig[] fbConfigs =
                GLX.glXChooseFBConfig(display,
                                      screen,
                                      iattributes,
                                      0,
                                      num_elements,
                                      0);

            if(fbConfigs == null || fbConfigs.length == 0 || fbConfigs[0] == null)
                throw new GLException(FBCONFIG_FAIL_MSG);

            if(num_elements[0] <= 0)
                throw new GLException(NO_VALID_CONFIG_MSG);

            // Note that we currently don't allow selection of anything but
            // the first GLXFBConfig in the returned list
            GLXFBConfig selected_config = fbConfigs[0];

            // Create the p-buffer.
            int niattribs = 0;

            iattributes[niattribs++] = GLXExt.GLX_PBUFFER_WIDTH;
            iattributes[niattribs++] = requestedWidth;
            iattributes[niattribs++] = GLXExt.GLX_PBUFFER_HEIGHT;
            iattributes[niattribs++] = requestedHeight;
            iattributes[niattribs++] = 0;

            long tmpBuffer = GLX.glXCreatePbuffer(display,
                                                  selected_config,
                                                  iattributes,
                                                  0);

            if(tmpBuffer == 0)
                // FIXME: query X error code for detail error message
                throw new GLException(PBUFFER_CREATE_ERR);


            // Set up instance variables
            this.display = display;
            drawable = tmpBuffer;
            this.fbConfig = selected_config;

            // Determine the actual width and height we were able to create.
            int[] tmp = new int[1];
            GLX.glXQueryDrawable(display, drawable, GLXExt.GLX_WIDTH, tmp, 0);
            width = tmp[0];
            GLX.glXQueryDrawable(display, drawable, GLXExt.GLX_HEIGHT, tmp, 0);
            height = tmp[0];
        }
        finally
        {
//            Platform.lock.unlock();
        }
    }

    /**
     * Global check to see if we have multisample available to use for the
     * framebuffer object. This only needs to check once with the system and
     * is assumed to have the same every time thereafter.
     *
     * @return true if the multisample capability is available, false otherwise
     */
    private static boolean isMultisampleAvailable()
    {
        if(!checkedMultisample)
        {
            long display = GLX.XOpenDisplay(null);
            String exts = GLX.glXGetClientString(display, GLX.GLX_EXTENSIONS);

            if(exts != null)
                multisampleAvailable = (exts.indexOf("GLX_ARB_multisample") >= 0);

            checkedMultisample = true;
        }

        return multisampleAvailable;
    }
}
