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

/* Kurzorové klávesy pro pohyb a rotaci 3D-spritu.

   Funkcionalizu spritu jsme omezili na pohyb v rovině XZ a otáčení kolem
   osy y, takže ne všechny pohyby a rotace potřebují vlastní klávesu.

   V této třídě nastavujeme výhled, na základě pozice objektu typu TourSprite.
   Při pohybu spritu se pohybuje také výhled.

   Uživatel může stisknout klávesy 'i' nebo 'o' pro přiblížení či oddálení spritu.
*/

import java.awt.event.*;
import java.awt.AWTEvent;
import java.util.Enumeration;
import javax.media.j3d.*;
import javax.vecmath.*;
import java.text.DecimalFormat;


public class TouristControls extends Behavior
{
  private WakeupCondition keyPress;

  private final static int forwardKey = KeyEvent.VK_DOWN;
  private final static int backKey = KeyEvent.VK_UP;
  private final static int leftKey = KeyEvent.VK_LEFT;
  private final static int rightKey = KeyEvent.VK_RIGHT;

  private final static int inKey = KeyEvent.VK_I;   // pro pohyb pozorovatele
  private final static int outKey = KeyEvent.VK_O;

  private final static double HEIGHT = 2.0;    // výška pozorovatele
  private final static double ZOFFSET = 8.0;   // vzdálenost od spritu
  private final static double ZSTEP = 1.0;   // krok pro zoom pozorovatele

 
  private TourSprite bob;   // 3D-sprite, který odtud ovládáme

  private TransformGroup viewerTG;  // the uzel TG pro výhled
  private Transform3D t3d, toMove;  // pro práci s uzlem viewerTG;
  private Point3d bobPosn;

  private DecimalFormat df;   // pro jednodušší ladící výstup


  public TouristControls(TourSprite b, TransformGroup vTG)
  {
    df = new DecimalFormat("0.###");  // 3 dm
    bob = b;
    viewerTG = vTG;
    t3d = new Transform3D();
    toMove = new Transform3D();
    setViewer();
    keyPress = new WakeupOnAWTEvent( KeyEvent.KEY_PRESSED );
  } // konec metody: TouristControls()


  private void setViewer()
  /* Umístí výhled tak, že je posunut o ZOFFSET od spritu podél osy z 
     a je ve výšece HEIGHT na podlahou.
  */
  {
    bobPosn = bob.getCurrLoc();   // počateční lokace robota
    viewerTG.getTransform( t3d );
    // argumenty jsou: pozice pozorovatele, kam se dívá, směr nahoru
    t3d.lookAt( new Point3d(bobPosn.x, HEIGHT, bobPosn.z + ZOFFSET), 
				new Point3d(bobPosn.x, HEIGHT, bobPosn.z), 
				new Vector3d(0,1,0));
    t3d.invert();
    viewerTG.setTransform(t3d);
  }  // konec metody: setViewer()


  public void initialize( )
  {
    wakeupOn( keyPress );
  }

  public void processStimulus(Enumeration criteria)
  {
    WakeupCriterion wakeup;
    AWTEvent[] event;

    while( criteria.hasMoreElements() ) {
      wakeup = (WakeupCriterion) criteria.nextElement();
      if( wakeup instanceof WakeupOnAWTEvent ) {
        event = ((WakeupOnAWTEvent)wakeup).getAWTEvent();
        for( int i = 0; i < event.length; i++ ) {
          if( event[i].getID() == KeyEvent.KEY_PRESSED )
            processKeyEvent((KeyEvent)event[i]);
        }
      }
    }
    wakeupOn( keyPress );
  } // konec metody: processStimulus()


  private void processKeyEvent(KeyEvent eventKey)
  {
    int keyCode = eventKey.getKeyCode();
    // System.out.println(keyCode);    
 
    if( eventKey.isAltDown() )
      altMove(keyCode);
    else
      standardMove(keyCode);

    viewerMove();
  } // konec metody: processKeyEvent()


  private void standardMove(int keycode)
  // dle stisknuté klávesy keycode přesune sprite:
  // dopředu či dozatu a otočí jej vlevo či vpravo.
  // další klávesy volivňují pozici pozorovatele na ose z
  {
    if(keycode == forwardKey )
      bob.moveForward();
    else if(keycode == backKey)
      bob.moveBackward();
    else if(keycode == leftKey)
      bob.rotClock();
    else if(keycode == rightKey)
      bob.rotCounterClock();
    else if(keycode == inKey)
      shiftViewer(-ZSTEP);    // přesuň pozorovatele podél záporné osy z
    else if(keycode == outKey)
      shiftViewer(ZSTEP);
  } // konec metody: standardMove()


  private void altMove(int keycode)
  // přesune spritu doleva či doprava
  {
    if(keycode == leftKey)
      bob.moveLeft();
    else if(keycode == rightKey)
      bob.moveRight();
  }  // konec metody: altMove()


  private void shiftViewer(double zDist)
  // posune pozorovatele směrem "ven" či "dovnitř"
  {
    Vector3d trans = new Vector3d(0,0,zDist);
    viewerTG.getTransform( t3d );
    toMove.setTranslation(trans);  // přepiš předchozí posun
    t3d.mul(toMove);
    viewerTG.setTransform(t3d);
  }  // konec metody: viewerMove()


  private void viewerMove()
  // Aktualizuje pozici výhledu o změnu při posunu spritu
  {
    Point3d newLoc = bob.getCurrLoc();
    // printTuple(newLoc, "newLoc");
    Vector3d trans = new Vector3d( newLoc.x - bobPosn.x, 0, newLoc.z - bobPosn.z);
    viewerTG.getTransform( t3d );
    toMove.setTranslation(trans);  // přepiš předchozí posun
    t3d.mul(toMove);
    viewerTG.setTransform(t3d);

    bobPosn = newLoc;   // ulož na příště
  }  // konec metody: viewerMove()


  // ----------------- ladění ------------------------------

  private void printTuple(Tuple3d t, String id)
  // pro ladění
  {
    System.out.println(id + " x: " + df.format(t.x) + 
				", " + id + " y: " + df.format(t.y) +
				", " + id + " z: " + df.format(t.z));
  }  // konec metody: printTuple()

} // konec třídy: TouristControls