package org.helioviewer.jhv.camera;

import javax.swing.Timer;

import org.helioviewer.jhv.Settings;
import org.helioviewer.jhv.camera.annotate.AnnotateCircle;
import org.helioviewer.jhv.camera.annotate.AnnotateCross;
import org.helioviewer.jhv.camera.annotate.AnnotateFOV;
import org.helioviewer.jhv.camera.annotate.AnnotateRectangle;
import org.helioviewer.jhv.camera.annotate.Annotateable;
import org.helioviewer.jhv.display.Viewport;
import org.helioviewer.jhv.layers.MovieDisplay;
import org.json.JSONObject;

import com.jogamp.newt.event.KeyEvent;
import com.jogamp.newt.event.KeyListener;
import com.jogamp.newt.event.MouseEvent;
import com.jogamp.newt.event.MouseListener;
import com.jogamp.opengl.GL2;

public class Interaction implements MouseListener, KeyListener {

    public enum Mode { PAN, ROTATE, AXIS }

    public enum AnnotationMode {
        Rectangle(AnnotateRectangle.class), Circle(AnnotateCircle.class), Cross(AnnotateCross.class), FOV(AnnotateFOV.class);

        private final Class<? extends Annotateable> clazz;

        AnnotationMode(Class<? extends Annotateable> _clazz) {
            clazz = _clazz;
        }

        Annotateable generate(JSONObject jo) {
            try {
                return clazz.getConstructor(JSONObject.class).newInstance(jo);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return new AnnotateRectangle(jo);
        }
    }

    private static final Timer wheelTimer = new Timer(1000 / 2, e -> MovieDisplay.render(1));
    private final Camera camera;
    private final InteractionAnnotate interactionAnnotate;
    private final InteractionAxis interactionAxis;
    private final InteractionPan interactionPan;
    private final InteractionRotate interactionRotate;

    private Mode mode = Mode.ROTATE;
    private AnnotationMode annotationMode = AnnotationMode.Rectangle;

    public Interaction(Camera _camera) {
        camera = _camera;
        interactionAnnotate = new InteractionAnnotate(camera);
        interactionAxis = new InteractionAxis(camera);
        interactionPan = new InteractionPan(camera);
        interactionRotate = new InteractionRotate(camera);

        wheelTimer.setRepeats(false);
    }

    public void setMode(Mode _mode) {
        mode = _mode;
        Settings.setProperty("display.interaction", mode.toString());
    }

    public Mode getMode() {
        return mode;
    }

    public void setAnnotationMode(AnnotationMode _annotationMode) {
        annotationMode = _annotationMode;
        // Settings.setProperty("display.interaction.annotation", annotationMode.toString());
    }

    public AnnotationMode getAnnotationMode() {
        return annotationMode;
    }

    @Override
    public void mouseWheelMoved(MouseEvent e) {
        float r = e.getRotation()[1];
        if (r != 0) {
            camera.zoom(-Camera.ZOOM_MULTIPLIER_WHEEL * r);
            if (r > 0) {
                MovieDisplay.render(0.5);
                wheelTimer.restart();
            } else
                MovieDisplay.display();
        }
    }

    @Override
    public void mouseDragged(MouseEvent e) {
    }

    @Override
    public void mouseReleased(MouseEvent e) {
    }

    @Override
    public void mouseMoved(MouseEvent e) {
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        if (e.getClickCount() == 2) {
            camera.reset();
        }
    }

    @Override
    public void mouseEntered(MouseEvent e) {
    }

    @Override
    public void mouseExited(MouseEvent e) {
    }

    @Override
    public void mousePressed(MouseEvent e) {
    }

    @Override
    public void keyPressed(KeyEvent e) {
    }

    @Override
    public void keyReleased(KeyEvent e) {
    }

    public void clearAnnotations() {
        interactionAnnotate.clear();
    }

    public void zoomAnnotations() {
        interactionAnnotate.zoom();
    }

    public void initAnnotations(GL2 gl) {
        interactionAnnotate.init(gl);
    }

    public void disposeAnnotations(GL2 gl) {
        interactionAnnotate.dispose(gl);
    }

    public void drawAnnotations(Viewport vp, GL2 gl) {
        interactionAnnotate.draw(vp, gl);
    }

    public JSONObject saveAnnotations() {
        return interactionAnnotate.toJson();
    }

    public void loadAnnotations(JSONObject jo) {
        interactionAnnotate.fromJson(jo);
    }

}

@@ -1879,16 +1879,54 @@
 ctangle;
+%0A    private boolean annotate = false;
 %0A%0A    pu
@@ -2744,32 +2744,328 @@
 ionMode;%0A    %7D%0A%0A
+    private InteractionType getInteractionType() %7B%0A        if (annotate)%0A            return interactionAnnotate;%0A        else if (mode == Mode.PAN)%0A            return interactionPan;%0A        else if (mode == Mode.AXIS)%0A            return interactionAxis;%0A        return interactionRotate;%0A    %7D%0A%0A
     @Override%0A  
@@ -3462,32 +3462,78 @@
 MouseEvent e) %7B%0A
+        getInteractionType().mouseDragged(e);%0A
     %7D%0A%0A    @Over
@@ -3575,32 +3575,105 @@
 MouseEvent e) %7B%0A
+        getInteractionType().mouseReleased(e);%0A        annotate = false;%0A
     %7D%0A%0A    @Over
@@ -4067,65 +4067,295 @@
     
-%7D%0A%0A    @Override%0A    public void keyPressed(KeyEvent e) %7B
+    if (e.isShiftDown()) %7B%0A            annotate = true;%0A        %7D%0A        getInteractionType().mousePressed(e);%0A    %7D%0A%0A    @Override%0A    public void keyPressed(KeyEvent e) %7B%0A        if (e.isShiftDown()) %7B%0A            annotate = true;%0A        %7D%0A        getInteractionType().keyPressed(e);
 %0A   
@@ -4406,32 +4406,58 @@
 d(KeyEvent e) %7B%0A
+        annotate = false;%0A
     %7D%0A%0A    publi

Connect events