/*****************************************************************************
 *                     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;

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

import org.eclipse.swt.widgets.Composite;

// Local imports
// None


/**
 * An implementation of the {@link GLDrawableFactory} that generates
 * SWT-specific capabilities using an extension of the JOGL Reference
 * Implementation codebase.
 * <p>
 *
 * @author Justin Couch
 * @version $Revision: 1.4 $
 */
public class SWTRIDrawableFactory extends GLDrawableFactory
{
    /**
     * Error message when the component passed to getGLDrawable is not a SWT
     * component (perhaps it was AWT?).
     */
    private static final String NON_SWT_COMPONENT_MSG =
        "The target object for this call is not from the SWT toolkit.";

    private static final String NON_SWT_DEVICE_MSG =
        "The AbstractGraphicsDevice instance provided is not an instance of " +
        "SWTGraphicsDevice.";

    /** Name of the Windows drawable factory class */
    private static final String WIN_FACTORY_CLASS =
        "org.j3d.opengl.swt.internal.ri.windows.WindowsDrawableFactory";

    /** Name of the OSX drawable factory class */
    private static final String MAC_FACTORY_CLASS =
        "org.j3d.opengl.swt.internal.ri.osx.MacDrawableFactory";

    /** Name of the X11 32-bit drawable factory class */
    private static final String X11_FACTORY_CLASS =
        "org.j3d.opengl.swt.internal.ri.x11.X11DrawableFactory";

    /** Name of the X11 64-bit drawable factory class */
    private static final String X1164_FACTORY_CLASS =
        "org.j3d.opengl.swt.internal.ri.x1164.X11DrawableFactory";

    /** The real factory instance we are delegating to */
    private GLDrawableFactory realFactory;

    /**
     * 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 SWTRIDrawableFactory()
    {
        // Load the OS-specific factory.
        String os_name = System.getProperty("os.name").toLowerCase();
        String class_name = null;

        if(os_name.startsWith("wind"))
            class_name = WIN_FACTORY_CLASS;
        else if(os_name.startsWith("mac os x"))
            class_name = MAC_FACTORY_CLASS;
        else
        {
            String arch = System.getProperty("os.arch").toLowerCase();
            if(arch.equals("x86_64") || arch.equals("amd64"))
            {
                class_name = X1164_FACTORY_CLASS;
            }
            else
            {
                class_name = X11_FACTORY_CLASS;
            }
        }

        if(class_name == null)
            throw new GLException("OS " + os_name + " not yet supported");

        try
        {
            Class cls = Class.forName(class_name);
            realFactory = (GLDrawableFactory)cls.newInstance();
        }
        catch(Exception e)
        {
            throw new GLException("Couldn't create the appropriate factory " +
                                  class_name);
        }
    }

    //---------------------------------------------------------------
    // 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)
    {
        if((device != null) && !(device instanceof SWTGraphicsDevice))
            throw new IllegalArgumentException(NON_SWT_DEVICE_MSG);

        return realFactory.chooseGraphicsConfiguration(capabilities,
                                                       chooser,
                                                       device);
    }

    /**
     * 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
    {
        if(!(target instanceof Composite))
            throw new IllegalArgumentException(NON_SWT_COMPONENT_MSG);

        return realFactory.getGLDrawable(target, capabilities, chooser);
    }


    /**
     * Check to see if this system is capable of creating a Pbuffer. Some
     * older graphics cards are not capable of this.
     *
     * @return true if it is possible to create a GLPbuffer.
     */
    public boolean canCreateGLPbuffer()
    {
        return realFactory.canCreateGLPbuffer();
    }

    /**
     * Creates a GLPbuffer with the given capabilites and dimensions.
     */
    public GLPbuffer createGLPbuffer(GLCapabilities capabilities,
                                     GLCapabilitiesChooser chooser,
                                     int initialWidth,
                                     int initialHeight,
                                     GLContext shareWith)
    {
        return realFactory.createGLPbuffer(capabilities,
                                           chooser,
                                           initialWidth,
                                           initialHeight,
                                           shareWith);
    }

    /**
     * <P> Creates a GLContext object representing an existing OpenGL
     * context in an external (third-party) OpenGL-based library. This
     * GLContext object may be used to draw into this preexisting
     * context using its {@link GL} and {@link
     * javax.media.opengl.glu.GLU} objects. New contexts created through
     * {@link GLDrawable}s may share textures and display lists with
     * this external context. </P>
     *
     * <P> The underlying OpenGL context must be current on the current
     * thread at the time this method is called. The user is responsible
     * for the maintenance of the underlying OpenGL context; calls to
     * <code>makeCurrent</code> and <code>release</code> on the returned
     * GLContext object have no effect. If the underlying OpenGL context
     * is destroyed, the <code>destroy</code> method should be called on
     * the <code>GLContext</code>. A new <code>GLContext</code> object
     * should be created for each newly-created underlying OpenGL
     * context.
     */
    public GLContext createExternalGLContext()
    {
        return realFactory.createExternalGLContext();
    }

    /**
     * Check to see if this card can create a drawable instance using
     * an external GL context.
     *
     * @return true if it is possible to create an external GLDrawable
     *   object via {@link #createExternalGLDrawable}.
     */
    public boolean canCreateExternalGLDrawable()
    {
        return realFactory.canCreateExternalGLDrawable();
    }

    /**
     * Create a {@link GLDrawable} object representing an existing
     * OpenGL drawable in an external (third-party) OpenGL-based
     * library. This GLDrawable object may be used to create new,
     * fully-functional {@link GLContext}s on the OpenGL drawable. This
     * is useful when interoperating with a third-party OpenGL-based
     * library and it is essential to not perturb the state of the
     * library's existing context, even to the point of not sharing
     * textures or display lists with that context. </P>
     *
     */
    public GLDrawable createExternalGLDrawable()
    {
        return realFactory.createExternalGLDrawable();
    }
}
