/*
Copyright (C) 2001, 2006 United States Government
as represented by the Administrator of the
National Aeronautics and Space Administration.
All Rights Reserved.
*/
package gov.nasa.worldwind;

import javax.media.opengl.*;
import javax.swing.event.*;
import java.beans.*;

/**
 * A non-platform specific {@link WorldWindow} class. This class can be aggregated into platform-specific classes to
 * provide the core functionality of World Wind.
 *
 * @author Tom Gaskins
 * @version $Id: WorldWindowGLAutoDrawable.java 1757 2007-05-07 09:17:09Z tgaskins $
 */
public class WorldWindowGLAutoDrawable extends WorldWindowImpl implements GLEventListener
{
    private final GLAutoDrawable drawable;
    private final EventListenerList eventListeners = new EventListenerList();

    private java.awt.Point pickPoint; // used to pass pick point to display method

    /**
     * Construct a new <code>WorldWindowGLCanvase</code> for a specified {@link GLDrawable}.
     *
     * @param drawable the drawable associated with the window.
     * @throws IllegalArgumentException if <code>drawable</code> is <code>null</code>.
     */
    public WorldWindowGLAutoDrawable(GLAutoDrawable drawable)
    {
        if (drawable == null)
        {
            String message = WorldWind.retrieveErrMsg("WorldWindowGLCanvas.GLAutoDrawableNullToConstructor");
            WorldWind.logger().log(java.util.logging.Level.FINE, message);
            throw new IllegalArgumentException(message);
        }

        this.drawable = drawable;
        this.drawable.setAutoSwapBufferMode(false); // to prevent buffer swapping after a pick traversal
        drawable.addGLEventListener(this);

        SceneController sc = this.getSceneController();
        if (sc != null)
        {
            sc.addPropertyChangeListener(this);
        }
    }

    @Override
    public void propertyChange(PropertyChangeEvent propertyChangeEvent)
    {
        if (propertyChangeEvent == null)
        {
            String msg = WorldWind.retrieveErrMsg("nullValue.PropertyChangeEventIsNull");
            WorldWind.logger().log(java.util.logging.Level.FINE, msg);
            throw new IllegalArgumentException(msg);
        }
        this.drawable.repaint(); // Queue a JOGL repaint request.
    }

    /**
     * See {@link GLEventListener#init(GLAutoDrawable)}.
     *
     * @param glAutoDrawable the drawable
     */
    public void init(GLAutoDrawable glAutoDrawable)
    {
        // This GLEventListener callback method is not used.
//        this.drawable.setGL(new DebugGL(this.drawable.getGL()));
    }

    /**
     * See {@link GLEventListener#display(GLAutoDrawable)}.
     *
     * @param glAutoDrawable the drawable
     * @throws IllegalStateException if no {@link SceneController} exists for this canvas
     */
    public void display(GLAutoDrawable glAutoDrawable)
    {
        try
        {
            SceneController sc = this.getSceneController();
            if (sc == null)
            {
                String message = WorldWind.retrieveErrMsg("WorldWindowGLCanvas.ScnCntrllerNullOnRepaint");
                WorldWind.logger().log(java.util.logging.Level.FINE, message);
                throw new IllegalStateException(message);
            }

            if (this.pickPoint != null)
            {
                sc.pick(this.pickPoint);
                this.pickPoint = null;
                // Keep going here in order to repair the back buffer and affect any highlighting the app may have made.
            }

            try
            {
                this.callRenderingListeners(new RenderingEvent(this.drawable, RenderingEvent.BEGIN));
            }
            catch (Exception e)
            {
                WorldWind.logger().log(java.util.logging.Level.FINE, WorldWind.retrieveErrMsg(
                    "WorldWindowGLAutoDrawable.ExceptionDuringGLEventListenerDisplay"), e);
            }

            sc.repaint();
            try
            {
                this.callRenderingListeners(new RenderingEvent(this.drawable, RenderingEvent.END));
            }
            catch (Exception e)
            {
                WorldWind.logger().log(java.util.logging.Level.FINE, WorldWind.retrieveErrMsg(
                    "WorldWindowGLAutoDrawable.ExceptionDuringGLEventListenerDisplay"), e);
            }

            this.drawable.swapBuffers();
        }
        catch (Exception e)
        {
            WorldWind.logger().log(java.util.logging.Level.FINE, WorldWind.retrieveErrMsg(
                "WorldWindowGLCanvas.ExceptionAttemptingRepaintWorldWindow"), e);
        }
        finally
        {
            this.pickPoint = null;
        }
    }

    /**
     * See {@link GLEventListener#reshape(GLAutoDrawable,int,int,int,int)}.
     *
     * @param glAutoDrawable the drawable
     */
    public void reshape(GLAutoDrawable glAutoDrawable, int x, int y, int w, int h)
    {
    }

    /**
     * See {@link GLEventListener#displayChanged(GLAutoDrawable,boolean,boolean)}.
     *
     * @param glAutoDrawable the drawable
     */
    public void displayChanged(GLAutoDrawable glAutoDrawable, boolean b, boolean b1)
    {
        WorldWind.logger().log(java.util.logging.Level.FINEST, WorldWind.retrieveErrMsg(
            "WorldWindowGLCanvas.DisplayEventListenersDisplayChangedMethodCalled"));
    }

    public PickedObjectList pick(java.awt.Point pickPoint)
    {
        if (pickPoint == null)
        {
            String msg = WorldWind.retrieveErrMsg("nullValue.PickPoint");
            WorldWind.logger().log(java.util.logging.Level.FINE, msg);
            throw new IllegalArgumentException(msg);
        }

        this.pickPoint = pickPoint;
        this.drawable.display();

        return this.getSceneController().getPickedObjectList();
    }

    public void addRenderingListener(RenderingListener listener)
    {
        this.eventListeners.add(RenderingListener.class, listener);
    }

    public void removeRenderingListener(RenderingListener listener)
    {
        this.eventListeners.remove(RenderingListener.class, listener);
    }

    private void callRenderingListeners(RenderingEvent event)
    {
        for (RenderingListener listener : this.eventListeners.getListeners(RenderingListener.class))
        {
            listener.stageChanged(event);
        }
    }
}
