/*
 * Licensed Materials - Property of Perforce Software, Inc. 
 * © Copyright Perforce Software, Inc. 2014, 2021 
 * © Copyright IBM Corp. 2009, 2014
 * © Copyright ILOG 1996, 2009
 * All Rights Reserved.
 *
 * Note to U.S. Government Users Restricted Rights:
 * The Software and Documentation were developed at private expense and
 * are "Commercial Items" as that term is defined at 48 CFR 2.101,
 * consisting of "Commercial Computer Software" and
 * "Commercial Computer Software Documentation", as such terms are
 * used in 48 CFR 12.212 or 48 CFR 227.7202-1 through 227.7202-4,
 * as applicable.
 */

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.util.StringTokenizer;
import java.util.Vector;

import ilog.views.maps.IlvAttributeInfoProperty;
import ilog.views.maps.IlvCoordinate;
import ilog.views.maps.IlvDefaultFeatureRenderer;
import ilog.views.maps.IlvFeatureAttributeProperty;
import ilog.views.maps.IlvFeatureRenderer;
import ilog.views.maps.IlvMapFeature;
import ilog.views.maps.IlvMapFeatureIterator;
import ilog.views.maps.attribute.IlvStringAttribute;
import ilog.views.maps.format.IlvMapFormatException;
import ilog.views.maps.geometry.IlvMapLineString;
import ilog.views.maps.projection.IlvProjection;
import ilog.views.maps.srs.coordsys.IlvCoordinateSystem;
import ilog.views.maps.srs.coordsys.IlvGeographicCoordinateSystem;

class OptimizedPolylineReader implements IlvMapFeatureIterator {
  /**
   * A decimal parser to parse decimal numbers
   */
  private DecimalFormat decimalParser;

  /**
   * The input stream from which file are read
   */
  private BufferedReader file;

  /**
   * Variables to manage feature
   */
  private IlvMapFeature feature;
  private IlvMapLineString geometry;
  private IlvFeatureAttributeProperty attributeProperty;
  private IlvStringAttribute stringAttribute;

  // The vector to hold all read points
  private Vector<IlvCoordinate> points = new Vector<IlvCoordinate>(0, 0);

  /**
   * Constructs a new PolylineReader to read from file which name is specified.
   * 
   * @param fileName
   *          the name of the file to be read
   */
  public OptimizedPolylineReader(String fileName) throws IOException, IlvMapFormatException {
    // Allocate variables for reading from file
    file = new BufferedReader(new FileReader(fileName));
    // check file format
    String line = file.readLine();
    if ((line == null) || (!line.trim().equals("# ascii polylines"))) {
      file.close();
      throw new IlvMapFormatException("Wrong format");
    }

    // to parse coordinates
    decimalParser = new DecimalFormat();

    // Initialize a feature for processing
    feature = new IlvMapFeature();
    geometry = new IlvMapLineString();
    feature.setGeometry(geometry);

    // Create attribute info
    IlvAttributeInfoProperty attributeInfo;
    String attributeNames[] = { "Comment" };
    Class<?> attributeClasses[] = { IlvStringAttribute.class };
    boolean attributeNullable[] = { true };

    attributeInfo = new IlvAttributeInfoProperty(attributeNames, attributeClasses, attributeNullable);
    // Initialize attribute list of feature
    IlvStringAttribute[] attribute = new IlvStringAttribute[1];
    attribute[0] = null;
    feature.setAttributeInfo(attributeInfo);
    attributeProperty = new IlvFeatureAttributeProperty(attributeInfo, attribute);
    feature.setAttributes(attributeProperty);
    stringAttribute = new IlvStringAttribute();
    attributeProperty.setAttribute(0, stringAttribute);
  }

  /**
   * Release the resources allocated by the feature iterator
   */
  Override
  public void dispose() {
    if (file != null) {
      try {
        file.close();
      } catch (IOException exception) {
        /* DO NOTHING */
      }
    }
  }

  /**
   * Returns next feature from collection, or null if no more feature is
   * available.
   */
  Override
  public IlvMapFeature getNextFeature() throws IOException {
    return readPolyline();
  }

  /**
   * Returns true if the reader can provide the source projection of its map
   * features.
   */
  Override
  public boolean isGeoreferenced() {
    return true;
  }

  /**
   * Returns the projection of the features
   */
  public IlvProjection getProjection() {
    return null; // DEPRECATED
  }

  /**
   * Returns the coordinate system of the features
   */
  Override
  public IlvCoordinateSystem getCoordinateSystem() {
    return IlvGeographicCoordinateSystem.WGS84;
  }

  /**
   * Returns the upper left corner of the feature list.
   */
  Override
  public IlvCoordinate getUpperLeftCorner() {
    return null;
  }

  /**
   * Returns the lower right corner of the feature list.
   */
  Override
  public IlvCoordinate getLowerRightCorner() {
    return null;
  }

  /**
   * Returns the default renderer for the map features.
   */
  Override
  public IlvFeatureRenderer getDefaultFeatureRenderer() {
    return new IlvDefaultFeatureRenderer();
  }

  /**
   * Reads the polyline from file
   */
  private IlvMapFeature readPolyline() throws IOException {
    // a buffer to concatenate comments for polyline
    StringBuffer buffer = new StringBuffer();

    // to store the ascii line that is read
    String line;

    // reset geometry
    geometry.removeAll();

    // Initialize variables to handle read points
    int oldPointNum = points.size();
    int currentPointNum = 0;
    IlvCoordinate c;

    // read a line
    while ((line = file.readLine()) != null) {
      line = line.trim();
      if (line.length() == 0) {
        break;
      }

      // for analyse of the line
      StringTokenizer tokenizer = new StringTokenizer(line, " \t");
      String currentToken;

      // Find out weither to create a new IlvCoodinate or not
      // to avoid temporarly IlvCoordinate allocation
      if (currentPointNum < oldPointNum) {
        // use an IlvCoordinate that was already allocated
        c = (IlvCoordinate) points.elementAt(currentPointNum);
      } else {
        // allocate a new IlvCoordinate
        c = new IlvCoordinate();
        points.addElement(c);
      }

      // process longitude
      if (tokenizer.hasMoreElements() == false)
        throw new IlvMapFormatException("Longitude coordinate expected");
      try {
        currentToken = (String) tokenizer.nextElement();
        c.x = decimalParser.parse(currentToken).doubleValue();
      } catch (ParseException e) {
        throw new IlvMapFormatException("Error while parsing longitude");
      }

      // process latitude
      if (tokenizer.hasMoreElements() == false)
        throw new IlvMapFormatException("Latitude coordinate expected");
      try {
        currentToken = (String) tokenizer.nextElement();
        c.y = decimalParser.parse(currentToken).doubleValue();
      } catch (ParseException e) {
        throw new IlvMapFormatException("Error while parsing latitude");
      }

      // process comment
      while (tokenizer.hasMoreElements()) {
        buffer.append((String) tokenizer.nextElement());
        buffer.append(" ");
      }

      // add this point to geometry
      geometry.addPoint(c);
      currentPointNum++;
    }

    // Case of end of file
    if ((line == null) && (geometry.getPointCount() == 0))
      return null;

    // Set attribute
    if (buffer.length() > 0) {
      stringAttribute.setString(buffer.toString());
      attributeProperty.setAttribute(0, stringAttribute);
    } else {
      attributeProperty.setAttribute(0, null);
    }
    feature.setAttributes(attributeProperty);

    // return read feature
    return feature;
  }
}