// An interior node for a decision tree for the Agent World.
// This one is used for tests involving OBJECT TYPES 
// along a given sensor direction.

import AgentWorld.*;

class ObjectType_InteriorNode extends InteriorNode
{ DecisionTree objectTypes[];

  ObjectType_InteriorNode(int sensorDirection)
  {
    super(sensorDirection);
    // This type of feature has a child for each possible object
    // that can be sensed.  (Notice that this number can be
    // obtained from the Sensors class.  You may assume that
    // sensors are numbered from 0 to NUMBER_OF_OBJECT_TYPES - 1)
    objectTypes = new DecisionTree[Sensors.NUMBER_OF_OBJECT_TYPES];
  }

  void setChildForObjectType(int objectType, DecisionTree child)
  {
    // The subtree for this outgoing arc has been created.
    // Link it in.
    objectTypes[objectType] = child;
  }

  // Follow the arc out of this interior node that matches
  // the value in these sensor readings.
  boolean makeDecision(Sensors readings)
  { // Look up what is seen in the direction associated with this feature.
    int objectType = readings.getObjectType(sensorDirection);
    
    if (debugging)
    { // If debugging this decision tree, report which arc is being followed.

      System.out.println(" following arc " + toString()
                         + " = " +  getObjectTypeName(objectType));
    }
    // Recursively traverse the tree.
    return objectTypes[objectType].makeDecision(readings);
  }

  void printTree(int currentDepth, int maxDepth)
  { int indent = 2 * currentDepth; // The number of spaces to indent before
                                   // describing this interior node.

    // You may assume that the sensors are numbered starting at 0.
    for(int i = 0; i < Sensors.NUMBER_OF_OBJECT_TYPES; i++)
    {
      // Indent then print the arc's label.
      indentTree(indent);
      System.out.println(toString() + " = " + getObjectTypeName(i));
      
      // Unless at max depth, recursively print the subtree at the
      // end of arc.
      if (currentDepth < maxDepth)
      {
        objectTypes[i].printTree(currentDepth + 1, maxDepth);
      }
      else (objectTypes[i] instanceof LeafNode)
      { // Might as well print leaves rather than "..."
        objectTypes[i].printTree(currentDepth + 1, maxDepth);
      }
      else
      {
        indentTree(indent);
        System.out.println("  ...");
      }
    }
  }

  String getObjectTypeName(int objectType)
  {
    switch(objectType) {
    case Sensors.NOTHING:   return "NOTHING";
    case Sensors.WALL:      return "WALL";
    case Sensors.ANIMAL:    return "ANIMAL";
    case Sensors.MINERAL:   return "MINERAL";
    case Sensors.VEGETABLE: return "VEGETABLE";
    default: return "??? unknown object type: " + objectType;
    }
  }

  int countInteriorNodes()
  { int total = 0;

    for(int i = 0; i < Sensors.NUMBER_OF_OBJECT_TYPES; i++)
    {
      total += objectTypes[i].countInteriorNodes();
    }
    return 1 + total; // Don't forget to count this interior node.
  }

  int countLeaves()
  { int total = 0;

    for(int i = 0; i < Sensors.NUMBER_OF_OBJECT_TYPES; i++)
    {
      total += objectTypes[i].countLeaves();
    }
    return total;
  }

  // Report the direction (in units of degrees) that this feature is "looking."
  public String toString()
  {
    return "objectTypeInDirection("
           + (Sensors.DEGREES_BETWEEN_SENSORS * sensorDirection) + ")";
  }

}
