/*****************************************************************************
 *                     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.draw2d;

// External imports
import javax.media.opengl.GL;
import javax.media.opengl.GLContext;
import javax.media.opengl.GLDrawable;
import javax.media.opengl.GLException;

// Local imports
// None

/**
 * Wrapper over the GLContext to hide the fact that under the covers we swap
 * the pbuffer instance, and thus it's GLContext instance every time the window
 * resizes.
 * <p>
 *
 * The contract on GLAutoDrawable states that the GLContext instance from it is
 * required to be constant for the lifetime of the drawable. Since we have a
 * drawable wrapper that remains constant, hiding the changing pbuffer, we need
 * to have exactly the same thing for the context. That's the job of this class
 * - to be the constant public-facing interface, whilst we change the real
 * implementation instance under the covers.
 *
 * @author Justin Couch
 * @version $Revision: 1.3 $
 */
class ContextWrapper extends GLContext
{
    /** The current real context instance */
    private GLContext realContext;

    /** The drawable wrapper that we're also managing */
    private GLDrawable drawableWrapper;

    /** Set when a new context instance has been set */
    private boolean changedContext;

    /**
     * Create an instance of this wrapper that collaborates with the given
     * DrawableWrapper instance.
     *
     * @param wrapper The wrapper instance to share
     */
    ContextWrapper(GLDrawable wrapper)
    {
        drawableWrapper = wrapper;
        changedContext = false;
    }

    //----------------------------------------------------------
    // Methods defined by GLContext
    //----------------------------------------------------------

    /**
     * Returns the GLDrawable to which this context may be used to
     * draw.
     *
     * @param The drawable this context came from
     */
    public GLDrawable getGLDrawable()
    {
        return drawableWrapper;
    }

    /**
     * Makes this GLContext current on the calling thread.
     * <p>
     * There are two return values that indicate success and one that
     * indicates failure. A return value of CONTEXT_CURRENT_NEW
     * indicates that that context has been made current, and that
     * this is the first time this context has been made current, or
     * that the state of the underlying context or drawable may have
     * changed since the last time this context was made current. In
     * this case, the application may wish to initialize the state.  A
     * return value of CONTEXT_CURRENT indicates that the context has
     * been made currrent, with its previous state restored.
     * <p>
     * If the context could not be made current (for example, because
     * the underlying drawable has not ben realized on the display) ,
     * a value of CONTEXT_NOT_CURRENT is returned.
     * <p>
     * If the context is in use by another thread at the time of the
     * call, then if isSynchronized() is true the call will
     * block. If isSynchronized() is false, an exception will be
     * thrown and the context will remain current on the other thread.
     *
     * @return CONTEXT_CURRENT if the context was successfully made current
     * @return CONTEXT_CURRENT_NEW if the context was successfully made
     * current, but need to be initialized.
     *
     * @return CONTEXT_NOT_CURRENT if the context could not be made current.
     *
     * @throws GLException if synchronization is disabled and the
     * context is current on another thread, or because the context
     * could not be created or made current due to non-recoverable,
     * window system-specific errors.
     */
    public int makeCurrent() throws GLException
    {
        int type = 0;

//        if(changedContext)
//            type = (realContext == null) ?
//                   CONTEXT_NOT_CURRENT :
//                   CONTEXT_CURRENT_NEW;
//        else
//            type = (realContext == null) ?
//                   CONTEXT_NOT_CURRENT :
//                   realContext.makeCurrent();
        return (realContext == null) ?
                CONTEXT_NOT_CURRENT :
                realContext.makeCurrent();
    }

    /**
     * Releases control of this GLContext from the current thread.
     *
     * @throws GLException if the context had not previously been made
     * current on the current thread
     */
    public void release() throws GLException
    {
        if(realContext != null)
            realContext.release();
    }

    /**
     * Destroys this OpenGL context and frees its associated
     * resources. The context should have been released before this
     * method is called.
     */
    public void destroy()
    {
        if(realContext != null)
            realContext.destroy();
    }

    /**
     * Returns true if 'makeCurrent' will exhibit synchronized behavior.
     *
     * @return true when exhibiting synchronized behavior.
     */
    public boolean isSynchronized()
    {
        // Always return true if we don't have a real context yet as this is a
        // pbuffer-autodrawable, which requires that it be set to true.
        return realContext != null ? realContext.isSynchronized() : true;
    }

    /**
     * Determines whether 'makeCurrent' will exhibit synchronized behavior.
     *
     * @param isSynchronized true if this should exhibit synchronized behavior
     */
    public void setSynchronized(boolean isSynchronized)
    {
        if(realContext != null)
            realContext.setSynchronized(isSynchronized);
    }

    /**
     * Returns the GL pipeline object for this GLContext.
     */
    public GL getGL()
    {
        return (realContext != null) ? realContext.getGL() : null;
    }

    /**
     * Sets the GL pipeline object for this GLContext.
     */
    public void setGL(GL gl)
    {
        if(realContext != null)
            realContext.setGL(gl);
    }

    //----------------------------------------------------------
    // Local methods
    //----------------------------------------------------------

    /**
     * Change the current context that is being wrapped to the new instance.
     *
     * @param context The real context to manage
     */
    void changeWrappedContext(GLContext context)
    {
        // Fetch the existing values if it is around
        if(realContext != null && context != null)
        {
            context.setGL(realContext.getGL());
        }

        realContext = context;
        changedContext = true;
    }
}
