
// PickDragBehavior.java
// Andrew Davison, April 2005, ad@fivedots.coe.psu.ac.th

/* @(#)PickDragBehavior.java 1.8 01/01/11 07:32:11
 * Copyright (c) 1996-2001 Sun Microsystems, Inc. All Rights Reserved.

   Reaguje na události výběru a tažení myší na 3D-herní desce.

   Pokud byla na desce zvolena pozice, předá ji objektu typu Board pro
   vyhodnocení voláním metody tryPosn().

   Pokud došlo k tažení, otočí deskou.

   Uvelnění tlačítka myši zachytává také, takže tažení bezděčně nepoužije
   pozici myši z předchozí a nyní již ukončeného tažení.

   Změny pro síťovou verzi:
     * při provádění metody selectedPosn()
      volá metodu tryMove() místo metody tryPosn() třídy Board
*/

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.media.j3d.*;
import javax.vecmath.*;



public class PickDragBehavior extends Behavior 
{
   private final static Vector3d IN_VEC = new Vector3d(0.f, 0.f, -1.f);   
		// směr vybírání -- do scény

   private final static double XFACTOR = 0.02;   // pro rotace
   private final static double YFACTOR = 0.02;


   private WakeupCriterion[] mouseEvents;
   private WakeupOr mouseCriterion;

   private int xPrev, yPrev;         // souřadnice používané při tažení
   private boolean isStartDrag;      // pro oznámení zahájení tažení
   private Transform3D modelTrans;   // uchovává celkovou rotaci desky
   private Transform3D transformX, transformY;   // pro opakované výpočty
   private TransformGroup boardTG;   // pro pohyb desky
   private BranchGroup bg;           // pro vybírání

   private OverlayCanvas canvas3D;
   private NetFourByFour fbf;

   private Point3d mousePos;
   private Transform3D imWorldT3d;   // pro transformace obraz-->virtuální svět
   private PickRay pickRay = new PickRay();
   private SceneGraphPath nodePath;


   public PickDragBehavior(OverlayCanvas c3D, NetFourByFour fbf, BranchGroup bg, TransformGroup boardTG) 
   { canvas3D = c3D;
     board = bd;
     this.fbf = fbf; this.boardTG = boardTG;

     modelTrans = new Transform3D();
     transformX = new Transform3D();
     transformY = new Transform3D();

     mousePos = new Point3d();
     imWorldT3d = new Transform3D();

     xPrev = 0; yPrev = 0;
     isStartDrag = true;
   } // konec metody: PickDragBehavior()


   public void initialize() 
   {
     mouseEvents = new WakeupCriterion[3];
     mouseEvents[0] = new WakeupOnAWTEvent(MouseEvent.MOUSE_DRAGGED);
     mouseEvents[1] = new WakeupOnAWTEvent(MouseEvent.MOUSE_PRESSED);
     mouseEvents[2] = new WakeupOnAWTEvent(MouseEvent.MOUSE_RELEASED);
     mouseCriterion = new WakeupOr(mouseEvents);
     wakeupOn (mouseCriterion);
   }  // konec metody: initialize()


   public void processStimulus(Enumeration criteria) 
   {
     WakeupCriterion wakeup;
     AWTEvent[] event;
     int id;
     int xPos, yPos;
     while (criteria.hasMoreElements()) {
         wakeup = (WakeupCriterion) criteria.nextElement();
         if (wakeup instanceof WakeupOnAWTEvent) {
            event = ((WakeupOnAWTEvent)wakeup).getAWTEvent();
            for (int i=0; i<event.length; i++) { 
               xPos = ((MouseEvent)event[i]).getX();
               yPos = ((MouseEvent)event[i]).getY();
               id = event[i].getID();
               if (id == MouseEvent.MOUSE_DRAGGED)
                 processDrag(xPos, yPos);
               else if (id == MouseEvent.MOUSE_PRESSED)
                 processPress(xPos, yPos);
               else if (id == MouseEvent.MOUSE_RELEASED)
                 isStartDrag = true;   // může znamenat konec tažení
             }
         }
      }
      wakeupOn (mouseCriterion);
   } // konec metody: processStimulus()


  private void processDrag(int xPos, int yPos)
  /* Převede taženou vzdálenost  do rotací kolem os x a y.
     Zahrne je do transformace modelu a aplikuje na 
     uzel boardTG. */
  {
     if (isStartDrag)
       isStartDrag = false;
     else {  // nejde o začátek tažení myši, talže můžeme spočítat posun
       int dx = xPos - xPrev;                   // vezmi taženou vzdálenost 
       int dy = yPos - yPrev;
       transformX.rotX( dy * YFACTOR );         // převeď na rotaci
       transformY.rotY( dx * XFACTOR );
       modelTrans.mul(transformX, modelTrans);  // zahrň do existujících rotací
       modelTrans.mul(transformY, modelTrans);
       boardTG.setTransform(modelTrans);   // aplikuj na transformační skupinu
     }

     xPrev = xPos;     // ulož lokaci pro příští tažení
     yPrev = yPos;
  }  // konec metody: processDrag()



  private void processPress(int xPos, int yPos)
  /* Pošle výbirací paprsek do virtuální světa, počínaje pozicí
     kurzoru myši. Zjistí nejbližší protnutý uzel a vezme první
     uzel typu Shape3D jako zvolenou pozici na herní desce.
  */
  { canvas3D.getPixelLocationInImagePlate(xPos, yPos, mousePos);
				// získej pozici myši v rámci herní obrazovky
    canvas3D.getImagePlateToVworld(imWorldT3d);
		        // získej transformaci z herní obrazovky do virtuálního světa
    imWorldT3d.transform(mousePos);   // převeď mousePos na souřadnice virtuálního světa

    pickRay.set(mousePos, IN_VEC);    
                // paprsek začíná od pozice myši a směřuje přímo do scény   

    nodePath = bg.pickClosest(pickRay); // vezmi první uzel podél vybíracího paprsku
    // System.out.println("nodePath: " + nodePath);
    if (nodePath != null)
      selectedmosn(nodePath);
  }  // konec metody: processPress()



  private void selectedmosn(SceneGraphPath np)
  /* Najde zvolenou pozici na desce, pokud existuje, a předá ji
     objektu desky pro zpracování. */
  {
    Node node = np.getObject();       // získej koncový uzel cesty
    if (node instanceof Shape3D) {    // ověř typ Shape3D
      Integer posID = (Integer) node.getUserData();   // získej poziční index
      if (posID != null)
        //board.tryPosn( posID.intValue() );
        fbf.tryMove( posID.intValue() );  // volání na vyšší úroveň
    }
  } // konec metody: selectedmosn()


} // konec třídy: PickDragBehavior
