package app.graph.view3d.behaviors;
import java.util.Enumeration;
import javax.media.j3d.*;
import javax.vecmath.*;
import java.awt.event.*;
import java.awt.*;

public class MouseMoveBehavior extends Behavior
{
    static public int AXIS_X = 1;
    static public int AXIS_Y = 1 << 1;
    static public int AXIS_BOTH = AXIS_X | AXIS_Y;

    private WakeupOr iConditionDragOrRelease;
    private WakeupOnAWTEvent iConditionMousePress;
    private WakeupOnAWTEvent iConditionMouseDrag;
    private WakeupOnAWTEvent iConditionMouseRelease;
    private Point iStartPos, iEndPos;
    private WakeupOnAWTEvent iCriterium;
    private TransformGroup iTransformGroup;
    private Transform3D iTransform3D=new Transform3D();
    private Transform3D iRotate=new Transform3D();
    private Transform3D iT3DTemp = new Transform3D();
    private Vector3d iVectorTemp = new Vector3d();
    private Vector3d iRotateEuler = new Vector3d();
    private Transform3D iTranslate=new Transform3D();
    private double iMoveFactorX=-0.5;
    private double iMoveFactorY=0.7;
    private double iRotateFactorX=-0.01;
    private double iRotateFactorY=-0.01;

    private boolean iDebug=false;

    public MouseMoveBehavior(TransformGroup aTransformGroup)
    {
	iTransformGroup=aTransformGroup;
	iTransformGroup.getTransform(iTransform3D);
	if (iDebug)
	    System.out.println(" axis_both="+AXIS_BOTH+" "+(AXIS_X&AXIS_Y));
    }

    public void initialize()
    {
	iConditionMousePress=new WakeupOnAWTEvent(MouseEvent.MOUSE_PRESSED);
	iConditionMouseDrag=new WakeupOnAWTEvent(MouseEvent.MOUSE_DRAGGED);
	iConditionMouseRelease=new WakeupOnAWTEvent(MouseEvent.MOUSE_RELEASED);
	iConditionDragOrRelease=new WakeupOr(new WakeupCriterion[]{iConditionMouseDrag,iConditionMouseRelease});
	wakeupOn(iConditionMousePress);
    }

    public void processStimulus(Enumeration aCriteria)
    {
	while (aCriteria.hasMoreElements())
	{
	iCriterium = (WakeupOnAWTEvent)aCriteria.nextElement();
//	if (aCriteria.hasMoreElements())
//	    System.out.println(" some criteria not processed!");
	java.awt.AWTEvent[] events = iCriterium.getAWTEvent();
	MouseEvent event=null;
	if (events.length > 0)
	    event = (MouseEvent)events[events.length-1];
//	if (events.length > 1)
//	    System.out.println(" some events not processed!");
	if (event.getID() == MouseEvent.MOUSE_PRESSED)
	{
	    if (iDebug)
	        System.out.println(" press : startcoords= "+event.getX()+","+event.getY());
	    iStartPos = event.getPoint();
	    wakeupOn(iConditionDragOrRelease);
	}
	if (event.getID() == MouseEvent.MOUSE_DRAGGED)
	{
	    iEndPos = event.getPoint();
	    int dx = iEndPos.x - iStartPos.x;
	    int dy = iEndPos.y - iStartPos.y;
	    if (iDebug)
	        System.out.println(" drag : "+dx+","+dy);
	    iStartPos=iEndPos;
	    processDrag(event.getModifiers(),dx,dy);
	    wakeupOn(iConditionDragOrRelease);
	}
	if (event.getID() == MouseEvent.MOUSE_RELEASED)
	{
	    if (iDebug)
	        System.out.println("dragging done : endcoords= "+event.getX()+","+event.getY());
	    wakeupOn(iConditionMousePress);
	}
	}
   }

    private void processDrag(int aModifiers,int aDx, int aDy)
    {
	switch(aModifiers)
	{
	    case MouseEvent.BUTTON1_MASK :
	    {
		if (iDebug)
		    System.out.println(" move1 "+MouseEvent.BUTTON1_MASK);
		iRotateEuler.x+=aDy*iRotateFactorX;
		iRotateEuler.y+=aDx*iRotateFactorY;
		iRotate.setEuler(iRotateEuler);
		break;
	    }
	    case 10/*MouseEvent.BUTTON2_MASK */:
	    {
		if (iDebug)
		    System.out.println(" move2 "+MouseEvent.BUTTON2_MASK);
		iT3DTemp.setIdentity();
		iT3DTemp.setTranslation(new Vector3d(0.0,((double)aDy)*iMoveFactorY,0.0));
		iTranslate.mul(iT3DTemp);
		break;
	    }
	    case MouseEvent.BUTTON3_MASK :
	    {
		if (iDebug)
		    System.out.println(" move3 "+MouseEvent.BUTTON3_MASK );
		iVectorTemp.set(0.0,iRotateEuler.y,0.0);
		iT3DTemp.setEuler(iVectorTemp);
		iVectorTemp = new Vector3d(((double)aDx)*iMoveFactorX,0.0,((double)-aDy)*iMoveFactorY);
		iT3DTemp.transform(iVectorTemp);
		iT3DTemp.setIdentity();
		iT3DTemp.setTranslation(iVectorTemp);
		iTranslate.mul(iT3DTemp);
		break;
	    }
	    default:
	    {
		System.out.println(" default-> "+aModifiers );
		break;
	    }
	}
	updateTransform();
    }

    private void updateTransform()
    {
	iTransform3D.set(iTranslate);
	iTransform3D.mul(iRotate);
	iTransformGroup.setTransform(iTransform3D);
    }

    public void invertMove(int aFlags)
    {
	if ((aFlags & AXIS_X)==AXIS_X)
	    iMoveFactorX*=-1.0;
	if ((aFlags & AXIS_Y)==AXIS_Y)
	    iMoveFactorY*=-1.0;
    }

    public void invertRotate(int aFlags)
    {
	if ((aFlags & AXIS_X)==AXIS_X)
	    iRotateFactorX*=-1.0;
	if ((aFlags & AXIS_Y)==AXIS_Y)
	    iRotateFactorY*=-1.0;
    }

    public void setTranslate(Vector3d aTranslation)
    {
	iTranslate.setTranslation(aTranslation);
	updateTransform();
    }

    public Vector3d getTranslate()
    {
	Matrix3d rot = new Matrix3d();
	Vector3d trans = new Vector3d();
	iTranslate.get(rot,trans);
	return trans;
    }

    /*
     * Set rotation
     * args :
     *  aRotation : rotation specified in euler angles (x component for rotation around X-axis, etc.)
     */
    public void setRotate(Vector3d aRotation)
    {
	iRotateEuler.set(aRotation);
	iRotate.setEuler(iRotateEuler);
	updateTransform();
    }

    public Vector3d getRotate()
    {
	return iRotateEuler;
    }

    public double getRotateFactorX()
    {
	return iRotateFactorX;
    }

    public void setRotateFactorX(double aRotateFactorX)
    {
	iRotateFactorX=aRotateFactorX;
    }

    public double getRotateFactorY()
    {
	return iRotateFactorY;
    }

    public void setRotateFactorY(double aRotateFactorY)
    {
	iRotateFactorY=aRotateFactorY;
    }

    public double getMoveFactorX()
    {
	return iMoveFactorX;
    }

    public void setMoveFactorX(double aMoveFactorX)
    {
	iMoveFactorX=aMoveFactorX;
    }

    public double getMoveFactorY()
    {
	return iMoveFactorY;
    }

    public void setMoveFactorY(double aMoveFactorY)
    {
	iMoveFactorY=aMoveFactorY;
    }
}