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

/* Kužel děla a laserový paprsek se otočí k bodu na zvoleném
   místě na šachovnici. Poté se vytvoří vlákno FireBeam, které
   pohybuje paprskem do cílové lokace a zobrazí explozi.
 */

import java.lang.Math;
import javax.vecmath.*;
import javax.media.j3d.*;
import com.sun.j3d.utils.picking.*;
import com.sun.j3d.utils.picking.behaviors.PickMouseBehavior;


public class ShootingBehaviour extends PickMouseBehavior 
{
  // počáteční orientace kuželu: přímo za nosem
  private static final Vector3d UPVEC = new Vector3d(0.0, 1.0, 0.0);

  private Point3d startPt;
  private boolean firstRotation, finishedShot;
  private double shootAngle;

  private ExplosionsClip explsClip;
  private LaserBeam laser;
  private GunTurret gun;

  // pro opakované výpočty
  private AxisAngle4d rotAxisAngle = new AxisAngle4d();
  private Vector3d clickVec = new Vector3d();
  private Vector3d axisVec = new Vector3d();


  public ShootingBehaviour(Canvas3D canvas, BranchGroup root, Bounds bounds,
				Point3d sp, ExplosionsClip ec, LaserBeam lb, GunTurret g) 
  {
    super(canvas, root, bounds);
    setSchedulingBounds(bounds);

    pickCanvas.setMode(PickCanvas.GEOMETRY_INTERSECT_INFO);
       // dovol vracení objektů typu PickIntersection

    startPt = sp;  // lokace kužele děla
    explsClip = ec;
    laser = lb;
    gun = g;

	firstRotation = true;   // pro signalizaci anulování předchozích rotací
    finishedShot = true;    // je-li true, můžeme vystřelit další paprsek
  } // konec metody: ShootingBehaviour()



  public void updateScene(int xpos, int ypos) 
  /* Tato metoda se volá, když dojde k výběru něčeho.
     Vybrat se dá pouze podlaha, takže získáme průsečík, otočíme
     kužel děla a namíříme na něj laserový paprsek, který vzápětí vystřelíme.
  */
  {
    if (finishedShot) {   // předchozí střela již doletěla do cíle
	  pickCanvas.setShapeLocation(xpos, ypos);

	  Point3d eyePos = pickCanvas.getStartPosition();  // lokace pozorovatele

	  PickResult pickResult = null;
	  pickResult = pickCanvas.pickClosest();

	  if (pickResult != null) {
        // pickResultInfo(pickResult);

		PickIntersection pi = 
		    pickResult.getClosestIntersection(startPt);  
   			// get intercesection closest to the gun cone	
		Point3d intercept = pi.getPointCoordinatesVW();

        rotateToPoint(intercept);    // otoč kužel a paprsek
        double turnAngle = calcTurn(eyePos, intercept);

        finishedShot = false;
        new FireBeam(intercept, this, laser, explsClip, turnAngle).start();
					// vystřel paprsek a zobraz explozi
      }
    }
  } // konec metody: updateScene()


  private void pickResultInfo(PickResult pr)
  // pro kontrolu hodnot třídy PickResult
  {
    // získej vybraný uzel (měl by být typu Shape3D)
	Shape3D shape = (Shape3D) pr.getNode(PickResult.SHAPE3D);
    if (shape != null)
      System.out.println("Shape3D: " + shape);
    else
      System.out.println("Shape3D nenalezen");

    SceneGraphPath path = pr.getSceneGraphPath();   // cesta bez lokálních uzlů & terminálů
    if (path != null) {
      int pathLen = path.nodeCount();
      if (pathLen == 0)
        System.out.println("Cesta je prázdná");
      else {
        for (int i=0; i < pathLen; i++) {
          Node node = path.getNode(i); 
          if (node instanceof Shape3D)
            System.out.print(i + ". Shape3D: " + node);
          else {
            System.out.println(i + ". Uzel: " + node);
            String name = (String) node.getUserData();
            if (name != null)
              System.out.println(name);
          }
        }
      }
    }
    else
      System.out.println("Path má hodnotu null");
  }  // konec metody: pickResultInfo()



  private void rotateToPoint(Point3d intercept)
  /* Otočí dělo a válec paprsku k bodu v místě, kam uživatel
     klepnul myší.
  */
  { if (!firstRotation) {   // anuluj předchozí rotaci děla a paprsku
      axisVec.negate();
      rotAxisAngle.set( axisVec, shootAngle );
      gun.makeRotation(rotAxisAngle);
      laser.makeRotation(rotAxisAngle);
    }

	clickVec.set( intercept.x-startPt.x, intercept.y-startPt.y, 
						intercept.z-startPt.z);   
	clickVec.normalize();
	axisVec.cross( UPVEC, clickVec);
    // System.out.println("axisVec: " + axisVec);

    shootAngle = UPVEC.angle(clickVec);
	// shootAngle = Math.acos( UPVEC.dot(clickVec));
    // System.out.println("shootAngle: " + shootAngle);

    rotAxisAngle.set(axisVec, shootAngle);     // build rotation
    // System.out.println("rotAxisAngle: " + rotAxisAngle);

    gun.makeRotation(rotAxisAngle);
    laser.makeRotation(rotAxisAngle);

    firstRotation = false;
  } // konec metody: rotateToPoint()



  private double calcTurn(Point3d eyePos, Point3d intercept)
  /* Vypočítá úhel, o který je nutné otočit kolem osy y obrázek exploze,
     aby směřoval k pozorovateli. 
     Obrázek se neotáčí kolem os x a z.
  */
  {  double zDiff = eyePos.z - intercept.z;
     double xDiff = eyePos.x - intercept.x;
     // System.out.println("zDiff: " + zDiff + "; xDiff: " + xDiff);

     double turnAngle = Math.atan2(xDiff, zDiff);
     // System.out.println("turnAngle: " + turnAngle);
     return turnAngle;
  } // konec metody: calcAngle()


  public void setFinishedShot()
  /* Volá se z vlákna FireBeam, když laservový paprsek dospěl ke svému
     cíli. Paprsek byl resetován a volání této metody umožní další střelbu. */
  { finishedShot = true; }


} // konec třídy: ShootingBehaviour