/*****************************************************************************
 *                     Yumetech, Inc Copyright (c) 2005
 *                               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;

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

import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.opengl.GLCanvas;
import org.eclipse.swt.opengl.GLData;

import com.sun.opengl.impl.NativeLibLoader;

// Local imports
import org.j3d.opengl.swt.SWTGLUtils;
import org.j3d.opengl.swt.SWTGraphicsConfiguration;
import org.j3d.opengl.swt.SWTGraphicsDevice;
import org.j3d.opengl.swt.Threading;

/**
 * An implementation of the {@link GLDrawableFactory} that generates
 * SWT-specific capabilities on MS-Windows machines.
 * <p>
 *
 *
 * @author Justin Couch
 * @version $Revision: 1.2 $
 */
public abstract class JOGLDrawableFactory extends GLDrawableFactory
{
    /** Flag indicating if we've tried to work out about Pbuffer support yet */
    protected static boolean pbufferInitialised;

    /**
     * Static constructor to ensure the JOGL core is loaded.
     */
    static
    {
        NativeLibLoader.loadCore();
        pbufferInitialised = false;
    }

    /**
     * Create a new instance of this drawable factory. Note that end users
     * should never call this method directly. It is made public so that the
     * dynamic class loading defined by the specification can load this package
     * from another class in a different package.
     */
    public JOGLDrawableFactory()
    {
    }

    //---------------------------------------------------------------
    // Methods defined by GLDrawableFactory
    //---------------------------------------------------------------

    /**
     * Selects a graphics configuration on the specified graphics
     * device compatible with the supplied GLCapabilities.
     * <p>
     *
     * The concrete data type of the passed graphics device and
     * returned graphics configuration must be specified in the
     * documentation binding this particular API to the underlying
     * window toolkit. The SWT implementation accepts {@link
     * SWTGraphicsDevice} objects and returns {@link
     * SWTGraphicsConfiguration} objects.
     */
    public AbstractGraphicsConfiguration
        chooseGraphicsConfiguration(GLCapabilities capabilities,
                                    GLCapabilitiesChooser chooser,
                                    AbstractGraphicsDevice device)
    {
        SWTGraphicsDevice real_device = (SWTGraphicsDevice)device;

        // Need to do something with the device here.
        GLData data = SWTGLUtils.toGLData(capabilities);

        return new JOGLGraphicsConfiguration(data);
    }

    /**
     * Returns a GLDrawable that wraps a platform-specific window system
     * object, such as an AWT or LCDUI Canvas. On platforms which
     * support it, selects a pixel format compatible with the supplied
     * GLCapabilities, or if the passed GLCapabilities object is null,
     * uses a default set of capabilities. On these platforms, uses
     * either the supplied GLCapabilitiesChooser object, or if the
     * passed GLCapabilitiesChooser object is null, uses a
     * DefaultGLCapabilitiesChooser instance.
     *
     * @throws IllegalArgumentException if the passed target is either
     *   null or its data type is not supported by this GLDrawableFactory.
     * @throws GLException if any window system-specific errors caused
     *     the creation of the GLDrawable to fail.
     */
    public GLDrawable getGLDrawable(Object target,
                                    GLCapabilities capabilities,
                                    GLCapabilitiesChooser chooser)
        throws IllegalArgumentException, GLException
    {
        GLDrawable ret_val = null;
        GLCapabilities real_caps = null;
        GLCapabilitiesChooser real_chooser = null;
        Composite widget = (Composite)target;

        if(target instanceof org.eclipse.swt.opengl.GLCanvas)
        {
            GLData data = ((org.eclipse.swt.opengl.GLCanvas)target).getGLData();

            // Ignore the user-provided capabilities and rebuild based on the
            // ones from the canvas itself
            real_caps = SWTGLUtils.toGLCapabilities(data);
            real_chooser = chooser;
        }
        else
        {
            real_caps = capabilities;
            real_chooser = chooser;
        }

        if(real_caps == null)
            real_caps = new GLCapabilities();

        if(real_chooser == null)
            real_chooser = new DefaultGLCapabilitiesChooser();

        return createOnscreenDrawable(widget, real_caps, real_chooser);
    }


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

    /**
     * Create an instance of an on-screen drawable object.
     */
    protected abstract GLDrawable createOnscreenDrawable(Composite widget,
                                                GLCapabilities capabilities,
                                                GLCapabilitiesChooser chooser);

    /**
     * Local convenience method that allows the derived class to place
     * something onto the same event queue as the windowing system. If the
     * Threading class says that this should be single threaded and we're
     * no on the OpenGL thread, then we'll block until we are and then run the
     * provided action.
     *
     * @param action The action instance that should be run
     */
    protected void maybeDoSingleThreadedWorkaround(Runnable action)
    {
        if(Threading.isSingleThreaded() &&
            !Threading.isOpenGLThread())
        {
            Threading.invokeOnOpenGLThread(action);
        }
        else
        {
            action.run();
        }
    }

}
