/*
 * Copyright 2010-2015 Institut Pasteur.
 * 
 * This file is part of Icy.
 * 
 * Icy is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Icy is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Icy. If not, see <http://www.gnu.org/licenses/>.
 */
package icy.vtk;

import icy.preferences.CanvasPreferences;
import icy.util.EventUtil;

import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.util.Timer;
import java.util.TimerTask;

import vtk.vtkActor;
import vtk.vtkPanel;
import vtk.vtkPropPicker;

/**
 * @author stephane
 */
public class IcyVtkPanelOld extends vtkPanel
{
    /**
     * 
     */
    private static final long serialVersionUID = -8455671369400627703L;

    protected Timer timer;
    final protected vtkPropPicker picker;

    public IcyVtkPanelOld()
    {
        super();

        // used for restore quality rendering after mouse wheel
        timer = new Timer("Timer - vtkPanel");
        // picker
        picker = new vtkPropPicker();
        // set ambient color to white
        lgt.SetAmbientColor(1d, 1d, 1d);
    }

    @Override
    public void Delete()
    {
        super.Delete();

        // important to release timer here
        timer.cancel();
    }

    @Override
    public void removeNotify()
    {
        super.removeNotify();

        // important to release timer here
        timer.cancel();
    }

    @Override
    public void setBounds(int x, int y, int width, int height)
    {
        super.setBounds(x, y, width, height);

        if (windowset == 1)
        {
            Lock();
            rw.SetSize(width, height);
            UnLock();
        }
    }

    @SuppressWarnings("deprecation")
    @Override
    public void setSize(int w, int h)
    {
        // have to use this to by-pass the wrong vtkPanel implementation
        resize(w, h);
    }

    @Override
    public void mouseEntered(MouseEvent e)
    {
        // nothing to do here
    }

    @Override
    public void mouseExited(MouseEvent e)
    {
        // nothing to do here
    }

    @Override
    public void mouseClicked(MouseEvent e)
    {
        if (e.isConsumed())
            return;

        // nothing to do here
    }

    @Override
    public void mouseMoved(MouseEvent e)
    {
        // just save mouse position
        lastX = e.getX();
        lastY = e.getY();
    }

    @Override
    public void mouseDragged(MouseEvent e)
    {
        // camera not yet defined --> exit
        if (cam == null)
            return;

        if (e.isConsumed())
            return;
        if (ren.VisibleActorCount() == 0)
            return;

        // cancel pending task
        timer.cancel();

        // get current mouse position
        final int x = e.getX();
        final int y = e.getY();
        int deltaX = (lastX - x);
        int deltaY = (lastY - y);

        // faster movement with control modifier
        if (EventUtil.isControlDown(e))
        {
            deltaX *= 3;
            deltaY *= 3;
        }

        if (EventUtil.isRightMouseButton(e) || (EventUtil.isLeftMouseButton(e) && EventUtil.isShiftDown(e)))
            // translation mode
            translateView(-deltaX, deltaY);
        else if (EventUtil.isLeftMouseButton(e))
            // rotation mode
            rotateView(deltaX, -deltaY);
        else
            // zoom mode
            zoomView(Math.pow(1.02, -deltaY));

        // save mouse position
        lastX = x;
        lastY = y;
        // request repaint
        repaint();
    }

    @Override
    public void mousePressed(MouseEvent e)
    {
        if (e.isConsumed())
            return;
        if (ren.VisibleActorCount() == 0)
            return;

        // want fast update
        setCoarseRendering(0);
    }

    @Override
    public void mouseReleased(MouseEvent e)
    {
        // restore quality rendering
        setFineRendering(1000);
    }

    @Override
    public void mouseWheelMoved(MouseWheelEvent e)
    {
        // camera not yet defined --> exit
        if (cam == null)
            return;

        // want fast update
        setCoarseRendering(0);

        // get delta
        double delta = e.getWheelRotation() * CanvasPreferences.getMouseWheelSensitivity();
        if (CanvasPreferences.getInvertMouseWheelAxis())
            delta = -delta;

        // faster movement with control modifier
        if (EventUtil.isControlDown(e))
            delta *= 3d;

        zoomView(Math.pow(1.02, delta));

        // request repaint
        repaint();

        // restore quality rendering
        setFineRendering(1000);
    }

    @Override
    public void keyPressed(KeyEvent e)
    {
        if (!e.isConsumed())
            super.keyPressed(e);
    }

    @Override
    public void keyReleased(KeyEvent e)
    {
        if (!e.isConsumed())
            super.keyReleased(e);
    }

    @Override
    public void lock()
    {
        if (windowset == 0)
            return;

        super.lock();
    }

    @Override
    public void unlock()
    {
        if (windowset == 0)
            return;

        super.unlock();
    }

    /**
     * return true if currently rendering
     */
    public boolean isRendering()
    {
        return rendering;
    }

    /**
     * Return picker object.
     */
    public vtkPropPicker getPicker()
    {
        return picker;
    }

    /**
     * Pick object at specified position and return it.
     */
    public vtkActor pick(int x, int y)
    {
        Lock();
        picker.PickProp(x, rw.GetSize()[1] - y, ren);
        UnLock();

        return picker.GetActor();
    }

    /**
     * Translate current camera view
     */
    public void translateView(double dx, double dy)
    {
        // translation mode
        double FPoint[];
        double PPoint[];
        double APoint[] = new double[3];
        double RPoint[];
        double focalDepth;

        // get the current focal point and position
        FPoint = cam.GetFocalPoint();
        PPoint = cam.GetPosition();

        // calculate the focal depth since we'll be using it a lot
        ren.SetWorldPoint(FPoint[0], FPoint[1], FPoint[2], 1.0);
        ren.WorldToDisplay();
        focalDepth = ren.GetDisplayPoint()[2];

        APoint[0] = rw.GetSize()[0] / 2.0 + dx;
        APoint[1] = rw.GetSize()[1] / 2.0 + dy;
        APoint[2] = focalDepth;
        ren.SetDisplayPoint(APoint);
        ren.DisplayToWorld();
        RPoint = ren.GetWorldPoint();
        if (RPoint[3] != 0.0)
        {
            RPoint[0] = RPoint[0] / RPoint[3];
            RPoint[1] = RPoint[1] / RPoint[3];
            RPoint[2] = RPoint[2] / RPoint[3];
        }

        /*
         * Compute a translation vector, moving everything 1/2 the distance
         * to the cursor. (Arbitrary scale factor)
         */
        cam.SetFocalPoint((FPoint[0] - RPoint[0]) / 2.0 + FPoint[0], (FPoint[1] - RPoint[1]) / 2.0 + FPoint[1],
                (FPoint[2] - RPoint[2]) / 2.0 + FPoint[2]);
        cam.SetPosition((FPoint[0] - RPoint[0]) / 2.0 + PPoint[0], (FPoint[1] - RPoint[1]) / 2.0 + PPoint[1],
                (FPoint[2] - RPoint[2]) / 2.0 + PPoint[2]);
        resetCameraClippingRange();
    }

    /**
     * Rotate current camera view
     */
    public void rotateView(int dx, int dy)
    {
        // rotation mode
        cam.Azimuth(dx);
        cam.Elevation(dy);
        cam.OrthogonalizeViewUp();
        resetCameraClippingRange();

        if (LightFollowCamera == 1)
        {
            lgt.SetPosition(cam.GetPosition());
            lgt.SetFocalPoint(cam.GetFocalPoint());
        }
    }

    /**
     * Zoom current view by specified factor (negative value means unzoom)
     */
    public void zoomView(double factor)
    {
        if (cam.GetParallelProjection() == 1)
            cam.SetParallelScale(cam.GetParallelScale() / factor);
        else
        {
            cam.Dolly(factor);
            resetCameraClippingRange();
        }
    }

    /**
     * Set coarse and fast rendering mode immediately
     * 
     * @see #setCoarseRendering(long)
     */
    public void setCoarseRendering()
    {
        // set fast rendering
        rw.SetDesiredUpdateRate(10.0);
    }

    /**
     * Set fine (and possibly slow) rendering immediately
     * 
     * @see #setFineRendering(long)
     */
    public void setFineRendering()
    {
        // set quality rendering
        rw.SetDesiredUpdateRate(0.01);
    }

    /**
     * Set coarse and fast rendering for the specified amount of time (in ms, always when set to 0)
     */
    public void setCoarseRendering(long time)
    {
        // cancel pending task
        timer.cancel();
        // want fast update
        rw.SetDesiredUpdateRate(10.0);

        if (time > 0)
            setFineRendering(time);
    }

    /**
     * Set fine (and possibly slow) rendering after specified time delay (in ms)
     */
    public void setFineRendering(long delay)
    {
        // cancel pending task
        timer.cancel();

        if (delay > 0)
        {
            // schedule quality restoration
            timer = new Timer();
            timer.schedule(new TimerTask()
            {
                @Override
                public void run()
                {
                    // no parent --> exit
                    if (getParent() == null)
                        return;

                    // set back quality rendering
                    GetRenderWindow().SetDesiredUpdateRate(0.01);
                    // request repaint
                    repaint();
                }
            }, delay);
        }
        else
            // set back quality rendering
            rw.SetDesiredUpdateRate(0.01);
    }
}