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

/**
 * @author Tom Gaskins
 * @version $Id: BasicSceneController.java 1792 2007-05-08 21:28:37Z tgaskins $
 */
public class BasicSceneController extends WWObjectImpl implements SceneController
{
    private Model model;
    private View view;
    private FrameController frameController;
    private double verticalExaggeration = 1d;
    private DrawContext dc = new DrawContextImpl();
    private gov.nasa.worldwind.PickedObjectList lastPickedObjects;

    // These are for tracking performance
    private long frame = 0;
    private long timebase = System.currentTimeMillis();
    private double framesPerSecond;
    private double frameTime;


    public BasicSceneController()
    {
        // Establish a default frame controller and view.
        this.setFrameController((FrameController) WorldWind.createConfigurationComponent(
            AVKey.FRAME_CONTROLLER_CLASS_NAME));

        this.setVerticalExaggeration(Configuration.getDoubleValue(AVKey.VERTICAL_EXAGGERATION, 1d));
    }

    public FrameController getFrameController()
    {
        return this.frameController;
    }

    public Model getModel()
    {
        return this.model;
    }

    public View getView()
    {
        return this.view;
    }

    /**
     * @param frameController the frame controller
     * @throws IllegalArgumentException if <code>frameController</code> is null
     */
    public void setFrameController(FrameController frameController)
    {
        if (frameController == null)
        {
            String msg = WorldWind.retrieveErrMsg("nullValue.FrameControllerIsNull");
            WorldWind.logger().log(java.util.logging.Level.FINE, msg);
            throw new IllegalArgumentException(msg);
        }
        FrameController oldFrameControler = this.frameController;
        this.frameController = frameController;
        this.firePropertyChange(AVKey.FRAME_CONTROLLER, oldFrameControler, frameController);
    }

    public void setModel(Model model)
    {
        if (this.model != null)
            this.model.removePropertyChangeListener(this);
        if (model != null)
            model.addPropertyChangeListener(this);

        Model oldModel = this.model;
        this.model = model;
        this.firePropertyChange(AVKey.MODEL, oldModel, model);
    }

    public void setView(View view)
    {
        if (this.view != null)
            this.view.removePropertyChangeListener(this);
        if (view != null)
            view.addPropertyChangeListener(this);

        View oldView = this.view;
        this.view = view;

        this.firePropertyChange(AVKey.VIEW, oldView, view);
    }

    public void setVerticalExaggeration(double verticalExaggeration)
    {
        Double oldVE = this.verticalExaggeration;
        this.verticalExaggeration = verticalExaggeration;
        this.firePropertyChange(AVKey.VERTICAL_EXAGGERATION, oldVE, verticalExaggeration);
    }

    public double getVerticalExaggeration()
    {
        return this.verticalExaggeration;
    }

    public void repaint()
    {
        this.frameTime = System.currentTimeMillis();

        this.dc.initialize(javax.media.opengl.GLContext.getCurrent());
        this.dc.setModel(this.model);
        this.dc.setView(this.view);
        this.dc.setVerticalExaggeration(this.verticalExaggeration);

        if (this.dc.getGLContext() == null)
        {
            String message = WorldWind.retrieveErrMsg("BasicSceneController.GLContextNullStartRedisplay");
            WorldWind.logger().log(java.util.logging.Level.FINE, message);
            throw new IllegalStateException(message);
        }

        FrameController fc = this.getFrameController();
        if (fc == null)
        {
            String message = WorldWind.retrieveErrMsg("BasicSceneController.NoFrameControllerStartRepaint");
            WorldWind.logger().log(java.util.logging.Level.FINE, message);
            throw new IllegalStateException(message);
        }

        try
        {
            fc.initializeFrame(this.dc);
            fc.drawFrame(this.dc);
        }
        catch (Throwable e)
        {
            String message = WorldWind.retrieveErrMsg("BasicSceneController.ExceptionDuringRendering");
            WorldWind.logger().log(java.util.logging.Level.FINE, message, e);
        }
        finally
        {
            fc.finalizeFrame(this.dc);

            ++this.frame;
            long time = System.currentTimeMillis();
            this.frameTime = System.currentTimeMillis() - this.frameTime;
            if (time - this.timebase > 5000)
            {
                this.framesPerSecond = frame * 1000d / (time - timebase);
                this.timebase = time;
                this.frame = 0;
//                System.out.printf("average frame rate is %f\n", this.framesPerSecond);
            }
        }
    }

    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);
        }

        if (this.dc.getSurfaceGeometry() == null || this.dc.getVisibleSector() == null)
        {
            this.lastPickedObjects = null;
            return null;
        }

        // PickingMode should be enabled before the initialize() method
        this.dc.enablePickingMode();
        this.dc.initialize(javax.media.opengl.GLContext.getCurrent());
        this.dc.setModel(this.model);
        this.dc.setView(view);
        this.dc.setVerticalExaggeration(this.verticalExaggeration);

        if (this.dc.getGLContext() == null)
        {
            String message = WorldWind.retrieveErrMsg("BasicSceneController.GLContextNullStartPick");
            WorldWind.logger().log(java.util.logging.Level.FINE, message);
            throw new IllegalStateException(message);
        }

        FrameController fc = this.getFrameController();
        if (fc == null)
        {
            String message = WorldWind.retrieveErrMsg("BasicSceneController.NoFrameControllerStartPick");
            WorldWind.logger().log(java.util.logging.Level.FINE, message);
            throw new IllegalStateException(message);
        }

        try
        {
            fc.initializePicking(this.dc);
            fc.pick(this.dc, pickPoint);
        }
        catch (Throwable e)
        {
            String message = WorldWind.retrieveErrMsg("BasicSceneController.ExceptionDuringPick");
            WorldWind.logger().log(java.util.logging.Level.FINE, message, e);
        }
        finally
        {
            fc.finalizePicking(this.dc);
            this.dc.disablePickingMode();
        }

        return this.lastPickedObjects = new PickedObjectList(this.dc.getPickedObjects());
    }

    public PickedObjectList getPickedObjectList()
    {
        return this.lastPickedObjects;
    }

    public double getFramesPerSecond()
    {
        return framesPerSecond;
    }

    public double getFrameTime()
    {
        return frameTime;
    }
}
