// documentation inherited
  public void paintComponent(Graphics g) {
    super.paintComponent(g);

    Graphics2D g2 = (Graphics2D) g;

    // center the tile display if we are bigger than we need to be
    g.translate(_tx, _ty);

    //         // paint coordinates in the grid that contains our tiles
    //         for (int yy = 0; yy < _height; yy++) {
    //             for (int xx = 0; xx < _width; xx++) {
    //                 int cx = xx*TILE_WIDTH, cy = yy*TILE_HEIGHT;
    //                 g.drawRect(cx, cy, TILE_WIDTH, TILE_HEIGHT);
    //                 String coord = (xx-_origX) + "/" + (yy-_origY);
    //                 g.drawString(coord, cx+TILE_WIDTH/2, cy+TILE_HEIGHT/2);
    //             }
    //         }

    // iterate over our tiles, painting each of them
    for (AtlantiTile tile : _tiles) {
      tile.paint(g2, _origX, _origY);
    }

    // if we have a placing tile, draw that one as well
    if (_placingTile != null && _validPlacement) {
      // if the current position is valid, draw the placing tile
      _placingTile.paint(g2, _origX, _origY);

      // draw a green rectangle around the placing tile
      g.setColor(Color.blue);
      int sx = (_placingTile.x + _origX) * TILE_WIDTH;
      int sy = (_placingTile.y + _origY) * TILE_HEIGHT;
      g.drawRect(sx, sy, TILE_WIDTH - 1, TILE_HEIGHT - 1);
    }

    // if we have a recently placed tile, draw that one as well
    if (_placedTile != null) {
      // draw the tile
      _placedTile.paint(g2, _origX, _origY);

      // draw a white rectangle around the placed tile
      g.setColor(Color.white);
      int sx = (_placedTile.x + _origX) * TILE_WIDTH;
      int sy = (_placedTile.y + _origY) * TILE_HEIGHT;
      g.drawRect(sx, sy, TILE_WIDTH - 1, TILE_HEIGHT - 1);
    }

    // draw a white rectangle around the last placed
    if (_lastPlacedTile != null) {
      g.setColor(Color.white);
      int sx = (_lastPlacedTile.x + _origX) * TILE_WIDTH;
      int sy = (_lastPlacedTile.y + _origY) * TILE_HEIGHT;
      g.drawRect(sx, sy, TILE_WIDTH - 1, TILE_HEIGHT - 1);
    }

    // undo our translations
    g.translate(-_tx, -_ty);
  }
  /** Called when an entry is added to {@link AtlantiObject#TILES}. */
  public void tilesAdded(AtlantiTile tile) {
    Log.info("Adding tile to board " + tile + ".");

    // if we add a tile that is the same as our most recently placed
    // tile, leave the placed tile. otherwise clear it out
    if (!tile.equals(_placedTile)) {
      _placedTile = null;
    }

    // add the tile
    _tiles.add(tile);

    // reference this as our most recently placed tile
    _lastPlacedTile = tile;

    // resort the list
    Collections.sort(_tiles);

    // have the new tile inherit its claim groups
    TileUtil.inheritClaims(_tiles, tile);

    // recompute our desired dimensions and then have our parent
    // adjust to our changed size
    computeDimensions();
  }
  /** Called when an entry is removed from {@link AtlantiObject#PIECENS}. */
  public void piecensRemoved(Object key) {
    // locate the tile associated with this piecen key
    for (AtlantiTile tile : _tiles) {
      if (tile.getKey().equals(key)) {
        // clear the piecen out of the tile
        tile.clearPiecen();
        // and repaint
        repaintTile(tile);
        // and get on out
        return;
      }
    }

    Log.warning(
        "Requested to clear piecen for which we could "
            + "find no associated tile! [key="
            + key
            + "].");
  }
  /**
   * Sets the tile to be placed on the board. The tile will be displayed in the square under the
   * mouse cursor where it can be legally placed and its orientation will be determined based on the
   * pointer's proximity to the edges of the target square. When the user clicks the mouse while the
   * tile is in a placeable position, a {@link AtlantiController#tilePlaced} command will be
   * dispatched. The coordinates and orientation of the tile will be available by fetching the tile
   * back via {@link #getPlacedTile}. The tile provided to this method will not be modified.
   *
   * @param tile the new tile to be placed or null if no tile is to currently be placed.
   */
  public void setTileToBePlaced(AtlantiTile tile) {
    Log.info("Setting tile to be placed [tile=" + tile + "].");

    // make a copy of this tile so that we can play with it
    _placingTile = tile.clone();
    ;

    // update our internal state based on this new placing tile
    if (_placingTile != null) {
      updatePlacingInfo(true);
    }
  }
  /** Called when an entry is added to {@link AtlantiObject#PIECENS}. */
  public void piecensAdded(Piecen piecen) {
    // if we still have a placed tile, we get rid of it
    _placedTile = null;

    Log.info("Placing " + piecen + ".");

    // locate the tile associated with this piecen
    int tidx = _tiles.indexOf(new AtlantiTile(piecen.x, piecen.y));
    if (tidx != -1) {
      AtlantiTile tile = _tiles.get(tidx);
      // set the piecen on the tile (supplying our tile list so that
      // the necessary claim group adjustments can be made)
      tile.setPiecen(piecen, _tiles);
      // and repaint
      repaintTile(tile);

    } else {
      Log.warning(
          "Requested to place piecen for which we could "
              + "find no associated tile! [piecen="
              + piecen
              + "].");
    }
  }
  /** Test code. */
  public static void main(String[] args) {
    JFrame frame = new JFrame("Board test");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    AtlantiBoardView board = new AtlantiBoardView(null);

    TestDSet set = new TestDSet();
    set.addTile(new AtlantiTile(CITY_TWO, true, WEST, 0, 0));
    set.addTile(new AtlantiTile(CITY_TWO, false, WEST, -1, 1));
    set.addTile(new AtlantiTile(CITY_ONE, false, SOUTH, -1, -1));
    AtlantiTile zero = new AtlantiTile(CURVED_ROAD, false, WEST, 0, 2);
    set.addTile(zero);
    AtlantiTile one = new AtlantiTile(TWO_CITY_TWO, false, NORTH, 0, 1);
    set.addTile(one);
    set.addTile(new AtlantiTile(CITY_THREE, false, WEST, 1, 1));
    set.addTile(new AtlantiTile(CITY_THREE_ROAD, false, EAST, 1, 2));
    set.addTile(new AtlantiTile(CITY_THREE, false, NORTH, -1, 0));
    AtlantiTile two = new AtlantiTile(CITY_ONE, false, EAST, -2, 0);
    set.addTile(two);
    board.tilesChanged(set);

    AtlantiTile placing = new AtlantiTile(CITY_TWO, false, NORTH, 0, 0);
    board.setTileToBePlaced(placing);

    // set a feature group to test propagation
    List<AtlantiTile> tiles = new ArrayList<AtlantiTile>();
    CollectionUtil.addAll(tiles, set.iterator());
    Collections.sort(tiles);

    zero.setPiecen(new Piecen(Piecen.GREEN, 0, 0, 2), tiles);
    one.setPiecen(new Piecen(Piecen.BLUE, 0, 0, 0), tiles);
    two.setPiecen(new Piecen(Piecen.RED, 0, 0, 1), tiles);

    Log.info("Incomplete road: " + TileUtil.computeFeatureScore(tiles, zero, 2));

    Log.info("Completed city: " + TileUtil.computeFeatureScore(tiles, two, 1));

    Log.info("Incomplete city: " + TileUtil.computeFeatureScore(tiles, one, 2));

    frame.getContentPane().add(board, BorderLayout.CENTER);
    frame.pack();
    SwingUtil.centerWindow(frame);
    frame.setVisible(true);
  }