/*
!=======================================================================
!
!  PROGRAM  PHASE-Viewer  (PHASE-Viewer 2014.01 ver.3.3.0)
!
!  Created on 2005/10/19, 21:40
!  AUTHOR(S): KOGA, Junichiro
!  File : MyMouseRotate.java
!  
!  Contact address :  Phase System Consortium
!                     E-mail: phase_system@nims.go.jp URL https://azuma.nims.go.jp
!
!
!   Since 2002, this program set had been intensively developed as a part of the following 
!  national projects supported by the Ministry of Education, Culture, Sports, Science and
!  Technology (MEXT) of Japan; "Frontier Simulation Software for Industrial Science
!  (FSIS)" from 2002 to 2005, "Revolutionary Simulation Software (RSS21)" from 2006 to
!  2008. "Research and Development of Innovative Simulation Software (RISS)" from 2008
!  to 2013. These projects is lead by the Center for Research on Innovative Simulation 
!  Software (CISS), the Institute of Industrial Science (IIS), the University of Tokyo.
!   Since 2013, this program set has been further developed centering on PHASE System
!  Consortium. 
!   The activity of development of this program set has been supervised by Takahisa Ohno.
!
!=======================================================================
 */

package ciss.phase_viewer.acviewer.mouselistener;

import java.awt.AWTEvent;
import java.awt.Component;
import java.awt.event.MouseEvent;
import java.util.Enumeration;

import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.WakeupCriterion;
import javax.media.j3d.WakeupOnAWTEvent;
import javax.media.j3d.WakeupOnBehaviorPost;
import javax.vecmath.Matrix4d;
import javax.vecmath.Vector3d;

import org.apache.log4j.Logger;

import com.sun.j3d.utils.behaviors.mouse.MouseBehavior;
import com.sun.j3d.utils.behaviors.mouse.MouseBehaviorCallback;

/**
 * 
 * @author
 */
public class MyMouseRotate extends MouseBehavior {
    private Logger logger = Logger.getLogger(MyMouseRotate.class.getName());

    private MouseBehaviorCallback callback = null;
    double x_angle, y_angle;
    double x_factor = .03;
    double y_factor = .03;

    private boolean firstBoot = true;

    /**
     * Creates a rotate behavior given the transform group.
     * 
     * @param transformGroup
     *            The transformGroup to operate on.
     */
    public MyMouseRotate(TransformGroup transformGroup) {
        super(transformGroup);
    }

    /**
     * Creates a default mouse rotate behavior.
     **/
    public MyMouseRotate() {
        super(0);
    }

    /**
     * Creates a rotate behavior. Note that this behavior still needs a
     * transform group to work on (use setTransformGroup(tg)) and the transform
     * group must add this behavior.
     * 
     * @param flags
     *            interesting flags (wakeup conditions).
     */
    public MyMouseRotate(int flags) {
        super(flags);
    }

    /**
     * Creates a rotate behavior that uses AWT listeners and behavior posts
     * rather than WakeupOnAWTEvent. The behavior is added to the specified
     * Component. A null component can be passed to specify the behavior should
     * use listeners. Components can then be added to the behavior with the
     * addListener(Component c) method.
     * 
     * @param c
     *            The Component to add the MouseListener and MouseMotionListener
     *            to.
     * @since Java 3D 1.2.1
     */
    public MyMouseRotate(Component c) {
        super(c, 0);
    }

    /**
     * Creates a rotate behavior that uses AWT listeners and behavior posts
     * rather than WakeupOnAWTEvent. The behaviors is added to the specified
     * Component and works on the given TransformGroup. A null component can be
     * passed to specify the behavior should use listeners. Components can then
     * be added to the behavior with the addListener(Component c) method.
     * 
     * @param c
     *            The Component to add the MouseListener and MouseMotionListener
     *            to.
     * @param transformGroup
     *            The TransformGroup to operate on.
     * @since Java 3D 1.2.1
     */
    public MyMouseRotate(Component c, TransformGroup transformGroup) {
        super(c, transformGroup);
    }

    /**
     * Creates a rotate behavior that uses AWT listeners and behavior posts
     * rather than WakeupOnAWTEvent. The behavior is added to the specified
     * Component. A null component can be passed to specify the behavior should
     * use listeners. Components can then be added to the behavior with the
     * addListener(Component c) method. Note that this behavior still needs a
     * transform group to work on (use setTransformGroup(tg)) and the transform
     * group must add this behavior.
     * 
     * @param flags
     *            interesting flags (wakeup conditions).
     * @since Java 3D 1.2.1
     */
    public MyMouseRotate(Component c, int flags) {
        super(c, flags);
    }

    public void initialize() {
        super.initialize();
        x_angle = 0;
        y_angle = 0;
        if ((flags & INVERT_INPUT) == INVERT_INPUT) {
            invert = true;
            x_factor *= -1;
            y_factor *= -1;
        }
    }

    /**
     * Return the x-axis movement multipler.
     **/
    public double getXFactor() {
        return x_factor;
    }

    /**
     * Return the y-axis movement multipler.
     **/
    public double getYFactor() {
        return y_factor;
    }

    /**
     * Set the x-axis amd y-axis movement multipler with factor.
     **/
    public void setFactor(double factor) {
        x_factor = y_factor = factor;
    }

    /**
     * Set the x-axis amd y-axis movement multipler with xFactor and yFactor
     * respectively.
     **/
    public void setFactor(double xFactor, double yFactor) {
        x_factor = xFactor;
        y_factor = yFactor;
    }

    public void processStimulus(Enumeration criteria) {

        WakeupCriterion wakeup;
        AWTEvent[] events;
        MouseEvent evt;
        // int id;
        // int dx, dy;

        while (criteria.hasMoreElements()) {
            wakeup = (WakeupCriterion) criteria.nextElement();
            if (wakeup instanceof WakeupOnAWTEvent) {
                events = ((WakeupOnAWTEvent) wakeup).getAWTEvent();
                if (events.length > 0) {
                    evt = (MouseEvent) events[events.length - 1];
                    doProcess(evt);
                }
            }

            else if (wakeup instanceof WakeupOnBehaviorPost) {
                while (true) {
                    // access to the queue must be synchronized
                    synchronized (mouseq) {
                        if (mouseq.isEmpty())
                            break;
                        evt = (MouseEvent) mouseq.remove(0);
                        // consolidate MOUSE_DRAG events
                        while ((evt.getID() == MouseEvent.MOUSE_DRAGGED)
                                && !mouseq.isEmpty()
                                && (((MouseEvent) mouseq.get(0)).getID() == MouseEvent.MOUSE_DRAGGED)) {
                            evt = (MouseEvent) mouseq.remove(0);
                        }
                    }
                    doProcess(evt);
                }
            }

        }
        wakeupOn(mouseCriterion);
    }

    private Vector3d trans = new Vector3d(0, 0, 0);

    public void setTranslation(Vector3d trans) {
        this.trans = trans;
    }

    void doProcess(MouseEvent evt) {
        int id;
        int dx, dy;

        if (x_last == 0 && y_last == 0) {
            x_last = evt.getX();
            y_last = evt.getY();
            if (firstBoot) {
                firstBoot = false;
            } else {
                return;
            }
        }
        processMouseEvent(evt);
        if (((buttonPress) && ((flags & MANUAL_WAKEUP) == 0))
                || ((wakeUp) && ((flags & MANUAL_WAKEUP) != 0))) {
            id = evt.getID();
            if ((id == MouseEvent.MOUSE_DRAGGED) && !evt.isMetaDown()
                    && !evt.isAltDown()) {
                x = evt.getX();
                y = evt.getY();
                dx = x - x_last;
                dy = y - y_last;

                if (!reset) {
                    x_angle = dy * y_factor;
                    y_angle = dx * x_factor;

                    Transform3D t3d = new Transform3D();
                    t3d.setTranslation(trans);
                    transformX.rotX(x_angle);
                    transformY.rotY(y_angle);

                    transformGroup.getTransform(currXform);

                    Matrix4d mat = new Matrix4d();
                    // Remember old matrix
                    currXform.get(mat);
                    transformGroup.setTransform(t3d);

                    // Translate to origin
                    currXform.setTranslation(trans);
                    if (invert) {
                        currXform.mul(currXform, transformX);
                        currXform.mul(currXform, transformY);
                    } else {
                        currXform.mul(transformX, currXform);
                        currXform.mul(transformY, currXform);
                    }

                    // Set old translation back
                    Vector3d translation = new Vector3d(mat.m03, mat.m13,
                            mat.m23);

                    logger.debug("translation: " + trans + "," + translation);
                    currXform.setTranslation(translation);
                    // Update xform
                    transformGroup.setTransform(currXform);

                    transformChanged(currXform);

                    if (callback != null)
                        callback.transformChanged(MouseBehaviorCallback.ROTATE,
                                currXform);
                } else {
                    reset = false;
                }

                x_last = x;
                y_last = y;
            } else if (id == MouseEvent.MOUSE_PRESSED) {
                x_last = evt.getX();
                y_last = evt.getY();
            }
        }
    }

    /**
     * Users can overload this method which is called every time the Behavior
     * updates the transform
     * 
     * Default implementation does nothing
     */
    public void transformChanged(Transform3D transform) {
    }

    /**
     * The transformChanged method in the callback class will be called every
     * time the transform is updated
     */
    public void setupCallback(MouseBehaviorCallback callback) {
        this.callback = callback;
    }

    public void init() {
        x_last = 0;
        y_last = 0;
    }

}
