/*******************************************************************************
 * Copyright (c) 2015 Voyager Search and MITRE
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Apache License, Version 2.0 which
 * accompanies this distribution and is available at
 *    http://www.apache.org/licenses/LICENSE-2.0.txt
 ******************************************************************************/

package org.locationtech.spatial4j.shape;

import org.locationtech.spatial4j.context.SpatialContext;

/**
 * The base interface defining a geometric shape. Shape instances should be
 * instantiated via one of the create* methods on a {@link SpatialContext} or
 * by reading WKT which calls those methods; they should <em>not</em> be
 * created directly.
 * <p>
 * Shapes are generally immutable and thread-safe. If a particular shape has a
 * <code>reset(...)</code> method then its use means the shape is actually
 * mutable. Mutating shape state is considered expert and should be done with care.
 */
public interface Shape {

  /**
   * Describe the relationship between the two objects.  For example
   * <ul>
   *   <li>this is WITHIN other</li>
   *   <li>this CONTAINS other</li>
   *   <li>this is DISJOINT other</li>
   *   <li>this INTERSECTS other</li>
   * </ul>
   * Note that a Shape implementation may choose to return INTERSECTS when the
   * true answer is WITHIN or CONTAINS for performance reasons. If a shape does
   * this then it <i>must</i> document when it does.  Ideally the shape will not
   * do this approximation in all circumstances, just sometimes.
   * <p>
   * If the shapes are equal then the result is CONTAINS (preferred) or WITHIN.
   */
  SpatialRelation relate(Shape other);

  /**
   * Get the bounding box for this Shape. This means the shape is within the
   * bounding box and that it touches each side of the rectangle.
   * <p>
   * Postcondition: <code>this.getBoundingBox().relate(this) == CONTAINS</code>
   */
  Rectangle getBoundingBox();

  /**
   * Does the shape have area?  This will be false for points and lines. It will
   * also be false for shapes that normally have area but are constructed in a
   * degenerate case as to not have area (e.g. a circle with 0 radius or
   * rectangle with no height or no width).
   */
  boolean hasArea();

  /**
   * Calculates the area of the shape, in square-degrees. If ctx is null then
   * simple Euclidean calculations will be used.  This figure can be an
   * estimate.
   */
  double getArea(SpatialContext ctx);

  /**
   * Returns the center point of this shape. This is usually the same as
   * <code>getBoundingBox().getCenter()</code> but it doesn't have to be.
   * <p>
   * Postcondition: <code>this.relate(this.getCenter()) == CONTAINS</code>
   */
  Point getCenter();

  /**
   * Returns a buffered version of this shape.  The buffer is usually a
   * rounded-corner buffer, although some shapes might buffer differently. This
   * is an optional operation.
   *
   *
   * @param distance
   * @return Not null, and the returned shape should contain the current shape.
   */
  Shape getBuffered(double distance, SpatialContext ctx);

  /**
   * Shapes can be "empty", which is to say it exists nowhere. The underlying coordinates are
   * typically NaN.
   */
  boolean isEmpty();

  /** The sub-classes of Shape generally implement the
   * same contract for {@link Object#equals(Object)} and {@link Object#hashCode()}
   * amongst the same sub-interface type.  This means, for example, that multiple
   * Point implementations of different classes are equal if they share the same x
   * &amp; y. */
  @Override
  public boolean equals(Object other);
  
  
  /**
   * Get the SpatialContext that created the Shape
   */
  public SpatialContext getContext();
}

