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

/* Třída GrowthBehavior realizuje chování řízené časem, které
   se spouští každých TIME_DELAY milisekund.

   Volá metodu applyRulesToLimbs(), která prochází skrz pole
   pole objektů typu TreeLimb, a aplikuje na ně řadu "pravidel".

   Tato pravidla mají v metodě applyRules() tvar if-testů. Každý
   if-test říká, jak se větev stromu změní, pokud splňuje jeho podmínky.
*/

import java.util.Enumeration;
import javax.media.j3d.*;
import javax.vecmath.*;
import java.util.*;


public class GrowthBehavior extends Behavior
{
  private final static int TIME_DELAY = 100;  //ms

  // konstanty pro jednotlivé osy
  private final static int X_AXIS = 0;
  private final static int Y_AXIS = 1;
  private final static int Z_AXIS = 2;


  private WakeupCondition timeOut;
  private ArrayList treeLimbs;           // pole objektů typu TreeLimb
  private ImageComponent2D[] leafIms;    // sekvence obrázků s listy



  public GrowthBehavior(ImageComponent2D[] lfIms)
  { 
    timeOut = new WakeupOnElapsedTime(TIME_DELAY);
    treeLimbs = new ArrayList();
    leafIms = lfIms;
  }


  public void addLimb(TreeLimb limb)
  {  treeLimbs.add(limb);  }


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

  public void processStimulus( Enumeration criteria )
  { // ignoruj kritéria
    applyRulesToLimbs();
    wakeupOn( timeOut );
  } // konec metody: processStimulus()


  private void applyRulesToLimbs()
  /* Aplikuje pravidla na každou větev stromu.
     Seznam se může zvětšit během cyklu for, protože
     pravidlo v metodě applyRules() může spustit vytvoření
     nové větve, která taktéž musí být uložena v seznamu.
  */
  {
    TreeLimb limb;
    for(int i=0; i < treeLimbs.size(); i++) {
      limb = (TreeLimb) treeLimbs.get(i);
      applyRules(limb);
      limb.incrAge();   // po každé iteraci je kočetina o něco starší
    }
  }  // konec metody: applyRulesToLimbs()


  private void applyRules(TreeLimb limb)
  // Aplikuj pravidla na končetinu stromu limb
  {
    // zvětši svou délku
    if ((limb.getLength() < 1.0f) && !limb.hasLeaves())
      limb.scaleLength(1.1f);

    // zesil svou tloušťku
    if ((limb.getRadius() <= (-0.05f*limb.getLevel()+0.25f))&& !limb.hasLeaves())
      limb.scaleRadius(1.05f);

    // obarvi se více do hněda
    limb.stepToBrown();

    // zploď nějaké končetiny jako své potomky
    int axis;
    if ((limb.getAge() == 5) && (treeLimbs.size() <= 256) && !limb.hasLeaves() &&   
        (limb.getLevel() < 10)) {
      axis = (Math.random() < 0.5) ? Z_AXIS : X_AXIS;
      if (Math.random() < 0.85)
        makeChild(axis, randomRange(10,30), 0.05f, 0.5f, limb);

      axis = (Math.random() < 0.5) ? Z_AXIS : X_AXIS;
      if (Math.random() < 0.85)
        makeChild(axis, randomRange(-30,-10), 0.05f, 0.5f, limb);
    }

    // nech vyrašit listoví
    if ( (limb.getLevel() > 3) && (Math.random() < 0.08) && 
         (limb.getNumChildren() == 0) && !limb.hasLeaves() )
      makeLeaves(limb);

    // nech listoví povyrůst
    if (limb.getAge()%10 == 0)
      limb.showNextLeaf();

    // proměň dolní část na "modrý květináč"
    if ((limb.getAge() == 100) && (limb.getLevel() == 1)) {
      limb.setRadius( 2.0f*limb.getRadius());
      // limb.setLength( 2.0f*limb.getLength());
      limb.setCurrColour( new Color3f(0.0f, 0.0f, 1.0f));
    }

  }  // konec metody: applyRules()



  private void makeChild(int axis, double angle, float rad, float len, TreeLimb par)
  { 
    TransformGroup startLimbTG = par.getEndLimbTG();
    TreeLimb child = new TreeLimb(axis, angle, rad, len, startLimbTG, par);
    treeLimbs.add(child);   // extend ArrayList
  } // konec metody: makeChild()



  private void makeLeaves(TreeLimb limb)
  /* Listy jsou reprezentovány dvěma obrazovkami typu ImageCsSeries. Jedna bude
     rotovat kolem bodu před listy a druhá kolem bodu za listy, což vytvoří
     uspokojivou "hustotu" listoví.
  {
    ImageCsSeries frontLeafShape = new ImageCsSeries(0.5f, 2.0f, leafIms);
    ImageCsSeries backLeafShape = new ImageCsSeries(-0.5f, 2.0f, leafIms);

    limb.addLeaves(frontLeafShape, backLeafShape);
  }  // konec metody: makeLeaves()


  private double randomRange(double min, double max)
  // vrátí náhodné číslo mezi min a max
  {  return (Math.random()*(max - min)) + min;  }

}  // konec třídy: GrowthBehavior
