// ============================================================================
// File:               $File$
//
// Project:            
//
// Purpose:            
//
// Author:             Rammi
//
// Copyright Notice:   (c) 2008  Rammi (rammi@caff.de)
//                     This code is in the public domain.
//                     Use at own risk.
//                     No guarantees given.
//
// Latest change:      $Date$
//
// History:	       $Log$
//=============================================================================
package de.caff.asteroid;

import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.util.HashMap;
import java.util.Map;

/**
 *  Exploding ship in the game.
 *
 *  Please note that this currently only exists to display something.
 *
 *  This class is part of a solution for a
 *  <a href="http://www.heise.de/ct/creativ/08/02/details/">competition by the German computer magazine c't</a>
 */
public class ShipExplosion
        extends GameObject
{
  /** Data of shape line. */
  private static class LineData
  {
    private final int p1x;
    private final int p1y;
    private final int p2x;
    private final int p2y;

    LineData(int p1x, int p1y, int p2x, int p2y)
    {
      this.p1x = p1x;
      this.p1y = p1y;
      this.p2x = p2x;
      this.p2y = p2y;
    }

    void addTo(GeneralPath path)
    {
      path.moveTo(p1x, p1y);
      path.lineTo(p2x, p2y);
    }

    public boolean equals(Object o)
    {
      if (this == o) {
        return true;
      }
      if (o == null || getClass() != o.getClass()) {
        return false;
      }

      LineData lineData = (LineData)o;

      if (p1x != lineData.p1x) {
        return false;
      }
      if (p1y != lineData.p1y) {
        return false;
      }
      if (p2x != lineData.p2x) {
        return false;
      }
      if (p2y != lineData.p2y) {
        return false;
      }

      return true;
    }

    public int hashCode()
    {
      int result;
      result = p1x;
      result = 31 * result + p1y;
      result = 31 * result + p2x;
      result = 31 * result + p2y;
      return result;
    }
  }
  /** Shape node used to identify a explosion shape via its lines. */
  private static class ShapeNode
  {
    private final LineData line;
    private ShapeNode parent;
    private Shape shape;
    private Map<LineData, ShapeNode> subNodes;

    ShapeNode(LineData ld)
    {
      line = ld;
    }

    ShapeNode getShapeNode(LineData ld)
    {
      if (subNodes == null) {
        subNodes = new HashMap<LineData, ShapeNode>();
      }
      ShapeNode sub = subNodes.get(ld);
      if (sub == null) {
        sub = new ShapeNode(ld);
        sub.parent = this;
        subNodes.put(ld, sub);
      }
      return sub;
    }

    private static GeneralPath createShape(ShapeNode node)
    {
      if (node == null) {
        return new GeneralPath();
      }
      else  {
        GeneralPath path = createShape(node.parent);
        node.line.addTo(path);
        return path;
      }
    }

    Shape getShape()
    {
      if (shape == null) {
        shape = createShape(this);
      }
      return shape;
    }
  }
  /** Internal buffer for explosion shapes to avoid using too much memory for redundant shapes. */
  private static final Map<LineData, ShapeNode> SHAPE_NODES = new HashMap<LineData, ShapeNode>();

  /** The shape of this explosion. */
  private ShapeNode shapeNode;

  /**
   *  Constructor.
   *  @param x x position of explosion
   *  @param y y position of explosion
   */
  public ShipExplosion(int x, int y)
  {
    super(x, y);
  }

  /**
   *  Add a line to the shape.
   *  @param px start of line, x coordinate
   *  @param py start of line, y coordinate
   *  @param dx delta of line, x coordinate
   *  @param dy delta of line, y coordinate
   */
  public void addLine(int px, int py, int dx, int dy)
  {
    LineData ld = new LineData(px, py, px+dx, py+dy);
    if (shapeNode == null) {
      shapeNode = SHAPE_NODES.get(ld);
      if (shapeNode == null) {
        shapeNode = new ShapeNode(ld);
        SHAPE_NODES.put(ld, shapeNode);
      }
    }
    else {
      shapeNode = shapeNode.getShapeNode(ld);
    }
  }

  /**
   * Get the bounding box of this rectangle.
   *
   * @return the bounding box
   */
  public Rectangle getBounds()
  {
    return shapeNode.getShape().getBounds();
  }

  /**
   * Get the type of game object.
   *
   * @return game object type
   */
  public String getObjectType()
  {
    return "ShipExplosion";
  }

  /**
   * Draw the object.
   *
   * @param g graphics context
   */
  public void draw(Graphics2D g)
  {
    g.setColor(Color.gray);
    g.draw(AffineTransform.getTranslateInstance(x, y).createTransformedShape(shapeNode.getShape()));
  }

  /**
   * Access method for visitor pattern.
   *
   * @param visitor visitor
   */
  public void visitedBy(GameObjectVisitor visitor)
  {
    visitor.handle(this);
  }
}
