import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

class GridPoint {

  public GridPoint(int x, int y, int z) {
    posx = x;
    posy = y;
    posz = z;
  }

  public GridPoint(int x, int y) {
    this(x, y, 0);
  }

  /** return the north neighbor of this point.  returns null if this
      point is on the northern edge of the grid */
  public GridPoint northNeighbor() {
    return myGrid.gridPointAt(posx, posy-1, posz);
  }

  /** return the south neighbor of this point.  returns null if this
      point is on the southern edge of the grid */
  public GridPoint southNeighbor() {
    return myGrid.gridPointAt(posx, posy+1, posz);
  }

  /** return the east neighbor of this point.  returns null if this
      point is on the eastern edge of the grid */
  public GridPoint eastNeighbor() {
    return myGrid.gridPointAt(posx+1, posy, posz);
  }

  /** return the west neighbor of this point.  returns null if this
      point is on the western edge of the grid */
  public GridPoint westNeighbor() {
    return myGrid.gridPointAt(posx-1, posy, posz);
  }

  /** return the neighbor on the next level up.  Returns null if there
      is no layer above current layer */
  public GridPoint upNeighbor() {
    return myGrid.gridPointAt(posx, posy, posz-1);
  }

  /** return the neighbor on the next level down.  Returns null if there
      is no layer below current layer */
  public GridPoint downNeighbor() {
    return myGrid.gridPointAt(posx, posy, posz+1);
  }

  /* expand a routing search */
  public int expand() throws InterruptedException {
    GridPoint xp;
    if ( (xp = westNeighbor()) != null && xp.val == UNROUTED ) {
      xp.val = val+1;
      if (xp.isTarget()) return xp.val;
      else myGrid.enqueueGridPoint(xp);
    }
    if ( (xp = eastNeighbor()) != null && xp.val == UNROUTED ) {
      xp.val = val+1;
      if (xp.isTarget()) return xp.val;
      else myGrid.enqueueGridPoint(xp);
    }
    if ( (xp = southNeighbor()) != null && xp.val == UNROUTED ) {
      xp.val = val+1;
      if (xp.isTarget()) return xp.val;
      else myGrid.enqueueGridPoint(xp);
    }
    if ( (xp = northNeighbor()) != null && xp.val == UNROUTED ) {
      xp.val = val+1;
      if (xp.isTarget()) return xp.val;
      else myGrid.enqueueGridPoint(xp);
    }
    if ( (xp = upNeighbor()) != null && xp.val == UNROUTED ) {
      xp.val = val+1;
      if (xp.isTarget()) return xp.val;
      else myGrid.enqueueGridPoint(xp);
    }
    if ( (xp = downNeighbor()) != null && xp.val == UNROUTED ) {
      xp.val = val+1;
      if (xp.isTarget()) return xp.val;
      else myGrid.enqueueGridPoint(xp);
    }
    return -1;
  }


  public boolean isTarget() {
    return ( this == myGrid.getTarget() );
  }

  public boolean isSource() {
    return ( this == myGrid.getSource() );
  }

  public boolean isEmpty() {
    return val == UNROUTED;
  }

  public void paintGridPoint(Graphics g) {
    g.setColor(Color.black);
    g.drawRect(myGrid.gridPanelX(posx,posy,posz),myGrid.gridPanelY(posx,posy,posz),
        myGrid.GRIDSIZE,myGrid.GRIDSIZE);
    if (isRouted()) {
      g.setColor(routedColor);
      fillGridPoint(g);
    } else if (isObstacle()) {
      g.setColor(Color.red);
      fillGridPoint(g);
    }
    else if (isSource()) {
      if (highlighted) g.setColor(Color.red);
      else g.setColor(Color.yellow);
      fillGridPoint(g);
      g.setColor(Color.black);
      labelGridPoint(g, "S");
    } else if (isTarget()) {
      if (highlighted) {
        g.setColor(Color.red);
        fillGridPoint(g);
        g.setColor(Color.black);
        labelGridPoint(g, Integer.toString(val % 10));
      } else {
        g.setColor(Color.yellow);
        fillGridPoint(g);
        g.setColor(Color.black);
        labelGridPoint(g, "T");
      }
    } else if (val < UNROUTED) {
      if (isEnqueued()) g.setColor(Color.orange);
      else g.setColor(Color.yellow);
      fillGridPoint(g);
      g.setColor(Color.black);
      labelGridPoint(g, Integer.toString(val % 10));
    }
  }

  private void fillGridPoint(Graphics g) {
    g.fillRect(myGrid.gridPanelX(posx,posy,posz)+2,myGrid.gridPanelY(posx,posy,posz)+2,
        myGrid.GRIDSIZE-3,myGrid.GRIDSIZE-3);
  }
  private void labelGridPoint(Graphics g, String s) {
    g.drawString(s, myGrid.gridPanelX(posx,posy,posz)+myGrid.CHARXOFFSET,
                    myGrid.gridPanelY(posx,posy,posz)+myGrid.CHARYOFFSET);
  }

  public String toString() {
    return "GridPoint(" + posx + "," + posy  + "," + posz + ")[" + val + "]";
  }

  public void reset() { val = UNROUTED; }

  public void initExpand() { val = 0; }

  public void setRouted() {
    val = ROUTED;
    routedColor = cseq.current();
  }

  public boolean isRouted() { return (val == ROUTED); }

  public void setObstacle() { val = OBSTACLE; }

  public boolean isObstacle() { return (val == ROUTED || val == OBSTACLE); }

  public boolean lessThan(GridPoint p2) { return val != 0 && val < p2.val; }

  private static final int ROUTED = -2;
  private static final int OBSTACLE = -1;
  private static final int UNROUTED = 9999;

  public int manhattanDistance(GridPoint p2) {
    return ( Math.abs(p2.posx - posx) + Math.abs(p2.posy - posy) +
      Math.abs(p2.posz - posz) );
  }

  private int posx;
  private int posy;
  private int posz;
  private boolean highlighted = false;
  private boolean enqueued = false;
  boolean isEnqueued()  { return enqueued; }
  void setEnqueued(boolean e) { enqueued = e; }

  private int val = UNROUTED;

  private Color routedColor = null;

  public int getVal() { return val; }

  public void highlight(boolean h) { highlighted = h; }

  static Grid myGrid;

  private static ColorSequencer cseq = new ColorSequencer();

  public static void nextRouteColor() { cseq.next(); }
}

