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

/* Funkctionalita:
     dopředu, dozadu, doleva, doprava, nahoru, dolů,
     otočení o 90 stupňů vlevo či vpravo.

   Klávesy ovlivňují tři věci: kameru směřující kupředu,
   kameru směřující vzad a obrázek uživatele v pohledu
   z ptačí perspektivy.
*/


import java.awt.AWTEvent;
import java.awt.event.*;
import java.util.Enumeration;

import javax.media.j3d.*;
import javax.vecmath.*;
import com.sun.j3d.utils.behaviors.vp.*;


public class KeyBehavior extends ViewPlatformBehavior
{
  // pohybové a rotační konstanty pro komunikaci s objektem typu BridsEye
  private static final int FORWARD = 0;
  private static final int LEFT = 1;
  private static final int BACK = 2;
  private static final int RIGHT = 3;

  private final static double MOVE_AMT = 1.0;
  private final static double ROT_AMT = Math.PI / 2.0;  // 90 stupňů

  /* Pevně nastavené pohybové vektory.
     Tyto pohyby jsou určeny pro výhled, v němž dopředu znamená
     pohyb do scény, takže z-ová hodnota klesá. Ostatní směry
     jsou stanoveny relativně k tomuto směru.
  */
  private static final Vector3d VFWD = new Vector3d(0,0,-MOVE_AMT);
  private static final Vector3d VBACK = new Vector3d(0,0,MOVE_AMT);
  private static final Vector3d VLEFT = new Vector3d(-MOVE_AMT,0,0);
  private static final Vector3d VRIGHT = new Vector3d(MOVE_AMT,0,0);
  private static final Vector3d VDOWN = new Vector3d(0, -MOVE_AMT ,0);
  private static final Vector3d VUP = new Vector3d(0, MOVE_AMT ,0);

  // názvy kláves
  private int forwardKey = KeyEvent.VK_UP;
  private int backKey = KeyEvent.VK_DOWN;
  private int leftKey = KeyEvent.VK_LEFT;
  private int rightKey = KeyEvent.VK_RIGHT;

  private WakeupCondition keyPress;

  private MazeManager mm;    // pro kontrolu pohybu
  private BirdsEye be;       
  private int zOffset;       // pro zamezení pohybu pod podlahu
  private TransformGroup camera2TG;   // pro práci se zpětnou kamerou

  // pro opakované výpočty
  private Transform3D t3d = new Transform3D();
  private Transform3D toMove = new Transform3D();
  private Transform3D toRot = new Transform3D();
  private Vector3d trans = new Vector3d();



  public KeyBehavior(MazeManager mazeMan, BirdsEye bird, TransformGroup c2)
  {
    keyPress = new WakeupOnAWTEvent( KeyEvent.KEY_PRESSED );
    mm = mazeMan;
    be = bird;
    camera2TG = c2;
    zOffset = 0;
  } // konec metody: KeyBehavior()


  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() )   // klávesa + <alt>
      altMove(keyCode);
    else
      standardMove(keyCode);
  } // konec metody: processKeyEvent()


  private void standardMove(int keycode)
  /* Pohyb pro hlavní kameru (např. VFWD pro dopředu) je spojen
     s protichůdným pohybem pro zpětnou kameru (např. VBACK).

     Tento způsob znamená, že relativní orientace těchto dvou kamer
     je v této třídě pevně stanovena.

     Rotace je pro obě kamery stejná. Čili rotace vlevo způsobí rotaci
     ROT_AMT pro obě kamery.
  */
  {
    if(keycode == forwardKey)
      moveBy(VFWD, FORWARD, VBACK);  // argumenty: hlavní kamera, pohled shora, zpětná kamera
    else if(keycode == backKey)
      moveBy(VBACK, BACK, VFWD);
    else if(keycode == leftKey)
      doRotateY(ROT_AMT, LEFT);
    else if(keycode == rightKey)
      doRotateY(-ROT_AMT, RIGHT);
  } // konec metody: standardMove()


  private void altMove(int keycode)
  {
    if(keycode == backKey) {  // pohybuj se dolů
      if (zOffset > 0) {
        doMove(VDOWN);  // test pomocí metody moveBy() neprovádíme
        doMoveC2(VDOWN);
        zOffset--;
      }
    }
    else if(keycode == forwardKey) {  // posuň vzhůru
      doMove(VUP);   // test pomocí metody moveBy() neprovádíme
      doMoveC2(VUP);
      zOffset++;
    }
    else if(keycode == leftKey)
      moveBy(VLEFT, LEFT, VRIGHT);   // doleva pro hlavní kameru, doprava pro zpětnou
    else if(keycode == rightKey)
      moveBy(VRIGHT, RIGHT, VLEFT);
  } // konec metody: altMove()


  
  // ------------------ pohyby -----------------------------

  private void moveBy(Vector3d theMove, int dir, Vector3d theMoveC2)
  /* Vypočítá následující pohyb a otestuje, nachází-li se na požadované pozici překážka.
     Pokud, pak pokračuje normálně, jinak zobrazí varovnou hlášku. */
  {
    Point3d nextLoc = possibleMove(theMove);
    if (mm.canMoveTo(nextLoc.x, nextLoc.z)) {    // žádná překážka?
      targetTG.setTransform(t3d);   
         // efektivní: t3d je globální proměnná nastavená v metodě possibleMove()
      // doMove(theMove, theMoveC2);   // neefektivní přepočítávání
      doMoveC2(theMoveC2);
      be.setMove(dir);
    }
    else  // na požadované pozici se nachází překážka
      be.bangAlert();  // oznam objektu typu BirdsEye, že má zobrazit varovnou hlášku    
  }  // konec metody: moveBy()



  private Point3d possibleMove(Vector3d theMove)
  /* Vypočítá účinek daného posunutí,
     pozici objektu však neaktualizuje, dokud nebude otestována. */
  { 
    targetTG.getTransform(t3d);   // uzel targetTG je transformace pro objekt typu ViewPlatform
    toMove.setTranslation(theMove);
    t3d.mul(toMove);
    t3d.get(trans);
    return new Point3d( trans.x, trans.y, trans.z);
  }  // konec metody: possibleMove()


  private void doMove(Vector3d theMove)
  // pohyb hlavní, vpřed směřující kamery
  {
    targetTG.getTransform(t3d);
    toMove.setTranslation(theMove);
    t3d.mul(toMove);
    targetTG.setTransform(t3d);
  } // konec metody: doMove()


  private void doMoveC2(Vector3d theMoveC2)
  // pohyb zpětné, sekundární kamery
  {
    camera2TG.getTransform(t3d);
    toMove.setTranslation(theMoveC2);
    t3d.mul(toMove);
    camera2TG.setTransform(t3d);
  } // konec metody: doMoveC2()


  // ---------------- rotace -----------------------

  private void doRotateY(double radians, int dir)
  // otočí tři věci
  {
    targetTG.getTransform(t3d);   // otoč hlavní kameru
    toRot.rotY(radians);
    t3d.mul(toRot);
    targetTG.setTransform(t3d);

    camera2TG.getTransform(t3d);  // otoč zpětnou kameru
    t3d.mul(toRot);  // znovupoužij hodnotu toRot
    camera2TG.setTransform(t3d);

    be.setRotation(dir);  // otoč pohled z ptačí perspektivy
  } // konec metody: doRotateY()


}  // konec třídy: KeyBehavior
