/*******************************************************************************
 * Copyright (c) 2015 VoyagerSearch
 * 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.io;

import java.io.IOException;
import java.io.Writer;
import java.math.RoundingMode;
import java.text.NumberFormat;
import java.util.Iterator;
import org.locationtech.spatial4j.shape.*;
import org.locationtech.spatial4j.shape.impl.BufferedLine;
import org.locationtech.spatial4j.shape.impl.BufferedLineString;

public class WKTWriter implements ShapeWriter {

  @Override
  public String getFormatName() {
    return ShapeIO.WKT;
  }


  protected StringBuilder append(StringBuilder buffer, Point p, NumberFormat nf) {
    return buffer.append(nf.format(p.getX())).append(' ').append( nf.format(p.getY()));
  }

  protected NumberFormat getNumberFormat() {
    return LegacyShapeWriter.makeNumberFormat(6);
  }

  @Override
  public String toString(Shape shape) {
    NumberFormat nf = getNumberFormat();
    if (shape instanceof Point) {
      Point point = (Point)shape;
      if (point.isEmpty()) {
        return "POINT EMPTY";
      }

      StringBuilder buffer = new StringBuilder();
      return append(buffer.append("POINT ("), point, nf).append(")").toString();
    }
    if (shape instanceof Rectangle) {
      NumberFormat nfMIN = nf;
      NumberFormat nfMAX = LegacyShapeWriter.makeNumberFormat(6);

      nfMIN.setRoundingMode( RoundingMode.FLOOR );
      nfMAX.setRoundingMode( RoundingMode.CEILING );
      
      Rectangle rect = (Rectangle)shape;
      return "ENVELOPE (" +
          // '(' x1 ',' x2 ',' y2 ',' y1 ')'
        nfMIN.format(rect.getMinX()) + ", " + nfMAX.format(rect.getMaxX()) + ", "+
        nfMAX.format(rect.getMaxY()) + ", " + nfMIN.format(rect.getMinY()) + ")";
//      
//      return "POLYGON(( "+
//         nf.format(rect.getMinX()) + " " + nf.format(rect.getMinY()) + ", "+
//         nf.format(rect.getMinX()) + " " + nf.format(rect.getMaxY()) + ", "+
//         nf.format(rect.getMaxX()) + " " + nf.format(rect.getMaxY()) + ", "+
//         nf.format(rect.getMaxX()) + " " + nf.format(rect.getMinY()) + ", "+
//         nf.format(rect.getMinX()) + " " + nf.format(rect.getMinY()) + "))";
    }
    if (shape instanceof Circle) {
      Circle c = (Circle) shape;

      StringBuilder str = new StringBuilder();
      str.append("BUFFER (POINT (")
        .append(nf.format(c.getCenter().getX())).append(" ")
        .append(nf.format(c.getCenter().getY()))
        .append("), ")
        .append(nf.format(c.getRadius()))
        .append(")");
      return str.toString();
    }
    if (shape instanceof BufferedLineString) {
      BufferedLineString line = (BufferedLineString) shape;
      StringBuilder str = new StringBuilder();

      double buf = line.getBuf();
      if (buf > 0d) {
        str.append("BUFFER (");
      }

      str.append("LINESTRING (");
      Iterator<BufferedLine> iter = line.getSegments().iterator();
      while(iter.hasNext()) {
        BufferedLine seg = iter.next();
        append(str,seg.getA(),nf).append(", ");
        if(!iter.hasNext()) {
          append(str,seg.getB(),nf);
        }
      }
      str.append(")");

      if (buf > 0d) {
        str.append(", ").append(nf.format(buf)).append(")");
      }
      return str.toString();
    }
    if(shape instanceof ShapeCollection) {
      @SuppressWarnings("unchecked")
      ShapeCollection<? extends Shape> collection = (ShapeCollection<? extends Shape>) shape;

      if (collection.isEmpty()) {
        return "GEOMETRYCOLLECTION EMPTY";
      }

      StringBuilder buffer = new StringBuilder();
      buffer.append("GEOMETRYCOLLECTION (");
      boolean first = true;
      for (Shape sub : collection.getShapes()) {
        if(!first) {
          buffer.append(",");
        }
        buffer.append(toString(sub));
        first = false;
      }
      buffer.append(")");
      return buffer.toString();
    }
    return LegacyShapeWriter.writeShape(shape, nf);
  }
  
  @Override
  public void write(Writer output, Shape shape) throws IOException {
    output.append( toString(shape) );
  }
}