/*
 * Licensed Materials - Property of Rogue Wave Software, Inc. 
 * © Copyright Rogue Wave Software, Inc. 2014, 2015 
 * © 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 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;

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;

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 points = new Vector(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
   */
  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.
   */
  public IlvMapFeature getNextFeature()
    throws IOException
  {
    return readPolyline();
  }

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

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

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

  /**
   * Returns the upper left corner of the feature list.
   */
  public IlvCoordinate getUpperLeftCorner()
  {
    return null;
  }
  
  /**
   * Returns the lower right corner of the feature list.
   */
  public IlvCoordinate getLowerRightCorner()
  {
    return null;
  }  
  
  /**
   * Returns the default renderer for the map features.
   */
  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;
  }
}