/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.spatial4j.core.context.jts;

import com.spatial4j.core.context.SpatialContext;
import com.spatial4j.core.distance.DistanceCalculator;
import com.spatial4j.core.exception.InvalidShapeException;
import com.spatial4j.core.io.JtsShapeReadWriter;
import com.spatial4j.core.io.ShapeReadWriter;
import com.spatial4j.core.shape.Circle;
import com.spatial4j.core.shape.Point;
import com.spatial4j.core.shape.Rectangle;
import com.spatial4j.core.shape.Shape;
import com.spatial4j.core.shape.jts.JtsGeometry;
import com.spatial4j.core.shape.jts.JtsPoint;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.util.GeometricShapeFactory;

import java.util.ArrayList;
import java.util.Collection;

/**
 * Enhances the default {@link SpatialContext} with support for Polygons (and
 * other geometry) plus
 * reading <a href="http://en.wikipedia.org/wiki/Well-known_text">WKT</a>. The
 * popular <a href="https://sourceforge.net/projects/jts-topo-suite/">JTS</a>
 * library does the heavy lifting.
 */
public class JtsSpatialContext extends SpatialContext {

  public static final JtsSpatialContext GEO = new JtsSpatialContext(true);

  private final GeometryFactory geometryFactory;

  public JtsSpatialContext( boolean geo ) {
    this(null, geo, null, null);
  }

  /**
   * See {@link SpatialContext#SpatialContext(boolean, com.spatial4j.core.distance.DistanceCalculator, com.spatial4j.core.shape.Rectangle)}.
   *
   * @param geometryFactory optional
   */
  public JtsSpatialContext(GeometryFactory geometryFactory, boolean geo, DistanceCalculator calculator, Rectangle worldBounds) {
    super(geo, calculator, worldBounds);
    this.geometryFactory = geometryFactory == null ? new GeometryFactory() : geometryFactory;
  }

  protected ShapeReadWriter makeShapeReadWriter() {
    return new JtsShapeReadWriter(this);
  }

  /**
   * Gets a JTS {@link Geometry} for the given {@link Shape}. Some shapes hold a
   * JTS geometry whereas new ones must be created for the rest.
   * @param shape Not null
   * @return Not null
   */
  public Geometry getGeometryFrom(Shape shape) {
    if (shape instanceof JtsGeometry) {
      return ((JtsGeometry)shape).getGeom();
    }
    if (shape instanceof JtsPoint) {
      return ((JtsPoint) shape).getGeom();
    }
    if (shape instanceof Point) {
      Point point = (Point) shape;
      return geometryFactory.createPoint(new Coordinate(point.getX(),point.getY()));
    }
    if (shape instanceof Rectangle) {
      Rectangle r = (Rectangle)shape;
      if (r.getCrossesDateLine()) {
        Collection<Geometry> pair = new ArrayList<Geometry>(2);
        pair.add(geometryFactory.toGeometry(new Envelope(
                r.getMinX(), getWorldBounds().getMaxX(), r.getMinY(), r.getMaxY())));
        pair.add(geometryFactory.toGeometry(new Envelope(
                getWorldBounds().getMinX(), r.getMaxX(), r.getMinY(), r.getMaxY())));
        return geometryFactory.buildGeometry(pair);//a MultiPolygon or MultiLineString
      } else {
        return geometryFactory.toGeometry(new Envelope(r.getMinX(), r.getMaxX(), r.getMinY(), r.getMaxY()));
      }
    }
    if (shape instanceof Circle) {
      // TODO, this should maybe pick a bunch of points
      // and make a circle like:
      //  http://docs.codehaus.org/display/GEOTDOC/01+How+to+Create+a+Geometry#01HowtoCreateaGeometry-CreatingaCircle
      // If this crosses the dateline, it could make two parts
      // is there an existing utility that does this?
      Circle circle = (Circle)shape;
      if (circle.getBoundingBox().getCrossesDateLine())
        throw new IllegalArgumentException("Doesn't support dateline cross yet: "+circle);//TODO
      GeometricShapeFactory gsf = new GeometricShapeFactory(geometryFactory);
      gsf.setSize(circle.getBoundingBox().getWidth()/2.0f);
      gsf.setNumPoints(4*25);//multiple of 4 is best
      gsf.setBase(new Coordinate(circle.getCenter().getX(),circle.getCenter().getY()));
      return gsf.createCircle();
    }
    throw new InvalidShapeException("can't make Geometry from: " + shape);
  }
  
  @Override
  public Point makePoint(double x, double y) {
    //A Jts Point is fairly heavyweight!  TODO could/should we optimize this?
    verifyX(x);
    verifyY(y);
    return new JtsPoint(geometryFactory.createPoint(new Coordinate(x, y)), this);
  }

  public GeometryFactory getGeometryFactory() {
    return geometryFactory;
  }

  @Override
  public String toString() {
    if (this.equals(GEO)) {
      return GEO.getClass().getSimpleName()+".GEO";
    } else {
      return super.toString();
    }
  }
}

@@ -4117,85 +4117,49 @@
  // 
-TODO, this should maybe pick a bunch of points%0A      // and make a circle lik
+FYI Some interesting code for this is her
 e:%0A 
@@ -4287,116 +4287,56 @@
   //
- If this crosses the dateline, it could make two parts%0A      // is there an existing utility that does this?
+TODO This should ideally have a geodetic version
 %0A   
@@ -4656,13 +4656,8 @@
 th()
-/2.0f
 );%0A 
@@ -4725,11 +4725,13 @@
 .set
-Bas
+Centr
 e(ne
@@ -4769,16 +4769,17 @@
 .getX(),
+ 
 circle.g
@@ -5431,11 +5431,129 @@
    %7D%0A  %7D
+%0A%0A  public static void main(String%5B%5D args) %7B%0A    System.out.println(GEO.getGeometryFrom(GEO.makeCircle(0, 0, 1)));%0A  %7D
 %0A%7D%0A

Fix #44 where JtsSpatialContext.getGeometryFrom(Circle) is offset wrong and has the wrong size.