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

// External imports
import java.lang.reflect.InvocationTargetException;
import java.security.AccessController;
import java.security.PrivilegedAction;

import javax.media.opengl.GLException;

import org.eclipse.swt.widgets.Display;

import com.sun.opengl.impl.GLWorkerThread;

// Local imports
// None

/**
 * This API provides access to the threading model for the implementation of
 * JOGL on SWT.
 * <p>
 *
 * This class is a re-implementation of the AWT-specific
 * {@link javax.media.opengl.Threading} class that specifically targets the
 * needs of SWT. The core package class uses a lot of AWT-specific code
 * internally, which can cause rendering deadlocks, particularly on Mac OSX.
 * Please use this class instead of the core class if you need this level of
 * functionality. The functionality is the same, so the documentation there is
 * applicable to this class.
 * <p>
 *
 * @author Justin Couch
 * @version $Revision: 1.2 $
 */
public class Threading
{
    /**
     * Message for when calling a method when it is not in single threaded
     * mode. Most of these are pointlesss being called when not single
     * threaded.
     */
    private static final String SINGLE_THREAD_MSG =
        "Should only call this in single-threaded mode";

    /**
     * Message when attempting to invoke something on the opengl thread when
     * we are currently on that thread.
     */
    private static final String ON_OGL_THREAD_ERR =
        "Should only call this from other threads than the OpenGL thread";

    /** The mode is to run on the SWT display thread */
    private static final int SWT = 1;

    /** The mode is to run on the separate worker thread */
    private static final int WORKER = 2;

    /** Is this class operating in single threaded mode */
    private static boolean singleThreaded = true;

    /** The mode that this class is operating in now */
    private static int mode;

    /**
     * Access and set the state
     */
    static
    {
        AccessController.doPrivileged(new PrivilegedAction()
        {
            public Object run()
            {
                String workaround = System.getProperty("opengl.1thread");

                // TODO:
                // The RI code has this for JAWT, but not sure if it still
                // applies for SWT.

                // Default to using the SWT thread on OS X due to apparent
                // instability with using JAWT on non-AWT threads
                boolean isOSX = System.getProperty("os.name").equals("Mac OS X");
                int defaultMode = (isOSX ? SWT : WORKER);
                mode = defaultMode;
                if(workaround != null)
                {
                    workaround = workaround.toLowerCase();
                    if(workaround.equals("true") || workaround.equals("auto"))
                    {
                        // Nothing to do; singleThreaded and mode already set up
                    }
                    else if(workaround.equals("worker"))
                    {
                        singleThreaded = true;
                        mode = WORKER;
                    }
                    else if(workaround.equals("swt"))
                    {
                        singleThreaded = true;
                        mode = SWT;
                    }
                    else
                    {
                        singleThreaded = false;
                    }
                }

                return null;
            }
        });
    }

    /**
     * Private constructor as there is no reason to ever instantiate this class
     */
    private Threading()
    {
    }

    /**
     * If an implementation of the javax.media.opengl APIs offers a
     * multithreading option but the default behavior is single-threading,
     * this API provides a mechanism for end users to disable single-threading
     * in this implementation.
     * <p>
     * Users are strongly discouraged from calling this method unless they are
     * aware of all of the consequences and are prepared to enforce some amount
     * of threading restrictions in their applications.
     * <p>
     *
     * Currently there is no supported way to re-enable it once disabled,
     * partly to discourage careless use of this method. This method should be
     * called as early as possible in an application.
     */
    public static void disableSingleThreading()
    {
        singleThreaded = false;
    }

    /**
     * Indicates whether OpenGL work is being automatically forced to a single
     * thread in this implementation.
     *
     * @return true if we are operating in single threaded mode
     */
    public static boolean isSingleThreaded()
    {
        return singleThreaded;
    }

    /**
     * Indicates whether the current thread is the single thread on which this
     * implementation of the javax.media.opengl APIs performs all of its
     * OpenGL-related work. This method should only be called if the
     * single-thread model is in effect.
     *
     * @return true when We are currently on the OpenGL thread
     */
    public static boolean isOpenGLThread() throws GLException
    {
        if(!isSingleThreaded())
            throw new GLException(SINGLE_THREAD_MSG);

        boolean ret_val = false;

        switch(mode)
        {
            case SWT:
                Thread curr = Thread.currentThread();
                ret_val = (Display.getCurrent().getThread() == curr);
                break;

            case WORKER:
                ret_val = GLWorkerThread.isWorkerThread();
                break;

            default:
                throw new InternalError("Illegal single-threading mode " + mode);
        }

        return ret_val;
    }

    /**
     * Executes the passed Runnable on the single thread used for all OpenGL
     * work in this javax.media.opengl API implementation. It is not specified
     * exactly which thread is used for this purpose. This method should only
     * be called if the single-thread model is in use and if the current thread
     * is not the OpenGL thread (i.e., if {@link #isOpenGLThread()} returns
     * false). It is up to the end user to check to see whether the current
     * thread is the OpenGL thread and either execute the Runnable directly or
     * perform the work inside it.
     */
    public static void invokeOnOpenGLThread(Runnable r) throws GLException
    {
        if(!isSingleThreaded())
            throw new GLException(SINGLE_THREAD_MSG);

        if(isOpenGLThread())
            throw new GLException(ON_OGL_THREAD_ERR);

        switch (mode)
        {
            case SWT:
                Display.getCurrent().asyncExec(r);
                break;

            case WORKER:
                if(!GLWorkerThread.isStarted())
                {
                    synchronized(GLWorkerThread.class)
                    {
                        if(!GLWorkerThread.isStarted())
                            GLWorkerThread.start();
                    }
                }
                try
                {
                    GLWorkerThread.invokeAndWait(r);
                }
                catch(InvocationTargetException e)
                {
                    throw new GLException(e.getTargetException());
                }
                catch(InterruptedException e)
                {
                    throw new GLException(e);
                }

                break;

            default:
                throw new InternalError("Illegal single-threading mode " + mode);
        }
    }
}
