package Challenge;

import java.awt.geom.*;
import java.io.*;
import java.util.*;
import java.text.*;
import java.awt.Color;

import GlobalNavigation.*;
import LocalNavigation.*;

/**
 * <p>The known objects in the environment in which the robot will navigate for
 * the construction task.</p>
 *
 * <p>You can either make an instance of this class and use it from within your
 * own program (typical usage), or you can run this code as an indepedent
 * process (see {@link #main}).  The latter mode allows you to display the
 * contents of a map file, but does not give programmatic access to those
 * contents, and thus is useful mostly for displaying/debugging map
 * files.</p>
 *
 * <p>The format of the map file is as follows (all items are signed doubles
 * or strings in ASCII text format, all units are meters, quotes
 * indicate a string literal not including the quotes, braces indicate optional
 * lines, ellipses indicate repetition, and all separators are whitespace):
 * <pre>
 * robotRadius
 * robotStartX robotStartY
 * robotGoalX robotGoalY
 * worldX worldY worldWidth worldHeight
 * ["f" X Y Z Radius H S B]
 * ["c" X Y Height H S B]
 * [...]
 * "obstacles"
 * [obstacle0X0 obstacle0Y0 obstacle0X1 obstacle0Y1 ...]
 * [...]
 * </pre>
 * The first four lines initialize the corresponding instance fields {@link
 * #robotRadius}, {@link #robotStart}, {@link #robotGoal}, and {@link
 * #worldRect}, respectively.  Zero or more lines beginning "f" or "c" follow,
 * identifying fiducials and construction objects, respectively.  Fiducial and
 * construction object lines may be arbitrarily interleaved.  Each fiducial and
 * each construction object is assigned a zero-based integer index according to
 * its position in the file (the fiducial and object indices are incremented
 * independently).  The fiducial/construction object block is terminated by a
 * line consisting of only the string "obstacles", which signals the start of
 * the obstacle list.  There may be zero fiducials and/or zero construction
 * objects, but the "obstacles" line must always be present.  Each obstacle
 * line gives the coordinates of the obstacle's vertices in CCW order.  There
 * may be zero obstacles.</p>
 *
 * <p>Non-rectangular world boundaries are simulated by an enclosing
 * rectangular boundary with polygon obstacles adjacent to the walls.  Note
 * that the obstacle polygons need not be disjoint.</p>
 *
 * @author Marty Vona
 **/
public class ConstructionMap extends PolygonMap {

  /**
   * <p>Name to use when run as an application.</p>
   **/
  public static final String APPNAME = "ConstructionMap";

  /**
   * <p>The fiducials.</p>
   **/
  protected LinkedList<Fiducial> fiducials = new LinkedList<Fiducial>();

  /**
   * <p>The construction objects.</p>
   **/
  protected LinkedList<ConstructionObject> constructionObjects =
    new LinkedList<ConstructionObject>();

  /**
   * <p>Index of next fiducial parsed.</p>
   **/
  protected int nextFiducialIndex = 0;

  /**
   * <p>Index of next construction object parsed.</p>
   **/
  protected int nextConstructionObjectIndex = 0;

  /**
   * <p>Create a new map, parsing <code>mapFile</code>.</p>
   *
   * @param mapFile the map file to parse, or null if none
   **/
  public ConstructionMap(File mapFile) throws IOException, ParseException {
    if (mapFile != null)
      parse(mapFile);
  }

  /**
   * <p>Covers {@link #ConstructionMap(File)}.</p>
   **/
  public ConstructionMap(String mapFile) throws IOException, ParseException {
    this((mapFile != null) ? new File(mapFile) : null);
  }

  /**
   * <p>Create a new un-initialized map.</p>
   *
   * <p>You may populate it using the accessors below.</p>
   **/
  public ConstructionMap() {
  }

  /**
   * {@inheritDoc}
   *
   * <p>This impl parses the fiducial/construction objects block.</p>
   **/
  protected boolean parseExtra(BufferedReader br, int lineNumber)
    throws IOException, ParseException, NumberFormatException {

    String line = br.readLine();

    if (line == null)
      return false;

    String[] tok = line.split("\\s+");
   
    boolean bad = false;

    if (tok.length < 1) {
      
      bad = true;
      
    } else if ("f".equals(tok[0])) {
      
      // "f" X Y Z Radius Color
      
      fiducials.add(new Fiducial(nextFiducialIndex++,
                                 Double.parseDouble(tok[1]),
                                 Double.parseDouble(tok[2]),
                                 Double.parseDouble(tok[3]),
                                 Double.parseDouble(tok[4]),
                                 tok[5]));

    } else if ("c".equals(tok[0])) {

      // "c" X Y Height Color

      constructionObjects.
        add(new ConstructionObject(nextConstructionObjectIndex++,
                                   Double.parseDouble(tok[1]),
                                   Double.parseDouble(tok[2]),
                                   Double.parseDouble(tok[3]),
                                   tok[4]));

    } else if ("obstacles".equals(tok[0])) {

      return false;

    } else {

      bad = true;

    }

    if (bad)
      throw new ParseException(
        "unexpected line in fiducial/construction object block (line " +
        lineNumber + ")", lineNumber);

    return true;
  }

  /**
   * <p>Get {@link #fiducials}.</p>
   *
   * @return a reference to <code>fiducials</code> (you may modify it)
   **/
  public List<Fiducial> getFiducials() {
    return fiducials;
  }

  /**
   * <p>Get {@link #constructionObjects}.</p>
   *
   * @return a reference to <code>constructionObjects</code> (you may modify
   * it)
   **/
  public List<ConstructionObject> getConstructionObjects() {
    return constructionObjects;
  }

  /**
   * <p>Return a human-readable string representation of this map.</p>
   *
   * @return a human-readable string representation of this map
   **/
  public String toString() {

    StringBuffer sb = new StringBuffer();

    sb.append(super.toString());

    sb.append("\n" + fiducials.size() + " fiducials:");
    for (Fiducial fiducial : fiducials) {
      sb.append("\n ");
      fiducial.toStringBuffer(sb);
    }

    sb.append("\n" + constructionObjects.size() + " construction objects:");
    for (ConstructionObject constructionObject : constructionObjects) {
      sb.append("\n ");
      constructionObject.toStringBuffer(sb);
    }

    return sb.toString();
  }

  /**
   * <p>Driver program, calls {@link #instanceMain}.</p>
   **/
  public static void main(String[] arg) {
    (new ConstructionMap()).instanceMain(arg);
  }

  /**
   * <p>Get the application name.</p>
   *
   * @return the application name
   **/
  protected String getAppName() {
    return APPNAME;
  }

  /**
   * {@inheritDoc}
   *
   * <p>This impl displays the fiducials and construction objects.</p>
   **/
  protected void driverDisplayHook() {

    for (Fiducial fiducial : getFiducials()) {
        (new GUIEllipseMessage(fiducial.color, fiducial.footprint,
                               true)).publish();

        double angle = Math.random()*Math.PI*2.0;

        double x = fiducial.x + Math.cos(angle)*fiducial.radius;
        double y = fiducial.y + Math.sin(angle)*fiducial.radius;

        (new GUIStringMessage(x, y,
                              Integer.toString(fiducial.index))).publish();
    }
 
    for (ConstructionObject constructionObject : getConstructionObjects()) {
        (new GUIRectMessage(constructionObject.color,
                            constructionObject.footprint,
                            true)).publish();
        (new GUIStringMessage(constructionObject.x, constructionObject.y,
                              Integer.
                              toString(constructionObject.index))).publish();
    }

    (new GUIPointMessage(Color.RED,
                         robotStart.x, robotStart.y,
                         ConstructionGUI.O_POINT)).publish();

    (new GUIPointMessage(Color.RED,
                         robotGoal.x, robotGoal.y,
                         ConstructionGUI.X_POINT)).publish();

//    CSpace cspace = new CSpace(obstacles, robotRadius);
//    for (PolygonObstacle obstacle : cspace.getObstacles())
//      (new GUIPolyMessage(Color.YELLOW, obstacle.getVertices(),
//                          true, false)).publish();
  }
}
