/*
 * 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.
 */

package demo.triplebuffering;

import ilog.views.*;

import java.awt.*;
import java.awt.image.*;

/**
 * A graphic object showing the track of a plane.
 */
public class Track 
  extends IlvGraphic
  implements ImageObserver
{
  /**
   * Name of the plane.
   */
  private String name;

  /**
   * The logo of the company.
   */
  private Image image;

  /**
   * Speed of the plane.
   */
  private int speed;

  /**
   * Altitude of the plane.
   */
  private int altitude;

  /**
   * Position of the plane in manager's coordinate system.
   */
  private IlvPoint position;

  /**
   * Position of the speed vector of the plane.
   */
  private IlvPoint speedPosition;

  /**
   * Position of the destination of the plane.
   */
  private IlvPoint destination;

  /**
   * Are last positions displayed.
   */
  private boolean showLastPositions = true;

  /**
   * Are last positions displayed using alpha composition.
   */
  public static boolean transparentLastPositions = true;

  /**
   * Are last positions displayed using antialising.
   */
  public static boolean antialiasedLastPositions = true;

  /**
   * Is the speed vector displayed.
   */
  private boolean showSpeed = true;

  /**
   * Are the labels displayed.
   */
  private boolean showLabels = true;

  /**
   * Is the logo displayed.
   */
  private boolean showLogo = true;

  /**
   * Is the route of the plane displayed.
   */
  private boolean showRoute;

  /**
   * The warning distance.
   */
  private double warningDistance;

  /**
   * A cache for the width of the name.
   */
  private int nameWidth;

  /**
   * A cache for the width of the speed label.
   */
  private int speedLabelWidth;

  /**
   * A cache for the width of the altitude label.
   */
  private int altitudeLabelWidth;

  /**
   * Number of positions of the track displayed.
   */
  private static final int TRACKMAXLASTPOSITION = 6;

  /**
   * Last positions of the plane.
   */
  private IlvPoint[] lastPositions = new IlvPoint[TRACKMAXLASTPOSITION];

  /**
   * Font to display labels.
   */
  private Font font;

  /**
   * A cache for the height of the font.
   */
  private float fontHeight;

  /**
   * Colors of the plane.
   */
  private Color color, takeOffColor, landingColor;

  /**
   * The label for altitude.
   */
  private String altitudeStr = "0";

  /**
   * The label for speed.
   */
  private String speedStr = "0";

  /**
   * Highquality rendering for the logo image.
   * Set to false because it seems this is useful mainly if the
   * image gets resized or rotated, which is not the case here.
   */
  private static final boolean HIGHQUALITY_IMAGE = false;

  /**
   * Last positions drawn as rectangles.
   */
  private static final int LAST_POSITION_RECT = 0;

  /**
   * Last positions drawn as ovals.
   */
  private static final int LAST_POSITION_OVAL = 1;

  /**
   * The drawing style for showing the last positions.
   */
  private int _lastPositionDrawingStyle = LAST_POSITION_OVAL;

  /**
   * A factor used when computing alpha transparency.
   */
  private static final float LAST_POS_ALFA_FACTOR = 
    (1.f / (float)TRACKMAXLASTPOSITION) * .8f;

  /**
   * Distance between successive 
  /**
   * Creates an initializes a Track.
   */
  public Track(String name,
               Image image,
               IlvPoint position,
               IlvPoint destination,
               Font font,
               Color color,
               Color takeOffColor,
               Color landingColor) 
  {
    this.name = name;
    this.image = image;
    setFont(font);
    this.color = color;
    this.takeOffColor = takeOffColor;
    this.landingColor = landingColor;    
    init(position, destination);
  }
  
  /**
   * Initializes the positions.
   */
  public void init(IlvPoint position, IlvPoint destination)
  {
    this.position = new IlvPoint(position);
    for (int i = 0; i < TRACKMAXLASTPOSITION; ++i)
      lastPositions[i] = new IlvPoint(position);
    speedPosition = new IlvPoint(position);
    this.destination = new IlvPoint(destination);
  }  

  /**
   * Returns the name of the plane.
   */
  public String getTrackName()
  {
    return name;
  }

  /**
   * Changes the name of the plane.
   */
  public void setTrackName(String s)
  {
    name = s;
    IlvRect r = IlvGraphicUtil.GetStringBounds(name, font, false);
    nameWidth = (int)r.width;
  }

  /**
   * Returns the current position of the plane.
   */
  public IlvPoint getPosition() 
  {
    return new IlvPoint(position); 
  }
  
  /**
   * Changes the position of the plane.
   */
  public void setPosition(float x, float y)
  {
    // Shift last positions
    int i;
    for (i = 0; i < TRACKMAXLASTPOSITION-1; ++i) 
      lastPositions[i].move(lastPositions[i+1].x,
                            lastPositions[i+1].y);    
    speedPosition.move(x, y);

    speedPosition.translate(position.x -
                            lastPositions[TRACKMAXLASTPOSITION-1].x,
                            position.y -
                            lastPositions[TRACKMAXLASTPOSITION-1].y);

    lastPositions[i].move(position.x, position.y);    
    position.move(x, y);
  }

  /**
   * Translates the plane's position.
   */
  public void translatePosition(float dx, float dy)
  {
    setPosition(position.x + dx, position.y + dy);
  }

  /**
   * Returns the destination of the plane.
   */
  public IlvPoint getDestination()  
  {
    return new IlvPoint(destination);   
  }
  
  /**
   * Returns the position of the speed vector of the plane.
   */
  public IlvPoint getSpeedPosition() 
  { 
    return new IlvPoint(speedPosition); 
  }

  
  /**
   * Indicates wheither last positions are displayed.
   */
  public boolean isShowingLastPositions()
  {
    return showLastPositions;
  }

  /**
   * Displays or hides the last positions of the plane.
   */
  public void setShowingLastPositions(boolean value)
  {
    showLastPositions = value;
  }

  /**
   * Indicates wheither the speed is displayed.
   */
  public boolean isShowingSpeed() 
  {
    return showSpeed; 
  }
  
  /**
   * Displays or hides the speed vector of the plane.
   */
  public void setShowingSpeed(boolean value) 
  { 
    showSpeed = value; 
  }

  /**
   * Indicates wheither the labels are displayed.
   */
  public  boolean isShowingLabels() 
  {
    return showLabels; 
  }
  
  /**
   * Displays or hides the labels of the plane.
   */
  public void setShowingLabels(boolean value) 
  {
    showLabels = value; 
  }

  /**
   * Indicates wheither the logo is displayed.
   */
  public  boolean isShowingLogo() 
  {
    return showLogo; 
  }
  
  /**
   * Displays or hides the logo of the plane.
   */
  public void setShowingLogo(boolean value) 
  {
    showLogo = value; 
  }

  /**
   * Indicates wheither the route is displayed.
   */
  public boolean isShowingRoute()  
  {
    return showRoute; 
  }
  
  /**
   * Displays or hides the route of the plane.
   */
  public void setShowingRoute(boolean value) 
  { 
    showRoute = value; 
  }

  /**
   *  Returns the speed of the plane.
   */
  public int getSpeed() 
  {
    return speed;  
  }

  /**
   *  Changes the speed of the plane.
   */
  public void setSpeed(int val)
  {
    speed = val;
    speedStr = String.valueOf(speed);
    IlvRect r = IlvGraphicUtil.GetStringBounds(speedStr, font, false);
    speedLabelWidth = (int)r.width;
  }
  
  /**
   * Returns the altitude of the plane.
   */
  public int getAltitude() 
  {
    return altitude; 
  }

  /**
   * Changes the altitude of the plane.
   */
  public void setAltitude(int val)
  {
    altitude = val;
    altitudeStr = String.valueOf(altitude);
    IlvRect r = IlvGraphicUtil.GetStringBounds(altitudeStr, font, false);
    altitudeLabelWidth = (int)r.width;
  }

  /**
   * Returns the warning distance of the plane.
   */
  public double getWarningDistance()  
  {
    return warningDistance; 
  }
  
  /**
   * Changes the warning distance of the plane.
   */
  public void setWarningDistance(double val)
  { 
    warningDistance = val; 
  }

  /**
   * Returns the font of the labels.
   */
  public Font getFont() 
  {
    return font;
  }

  /**
   * Changes the font of the labels.
   */
  public void setFont(Font font)
  {
    this.font = font;
    IlvRect r = IlvGraphicUtil.GetStringBounds(name, font, false);
    nameWidth = (int)r.width;
    fontHeight = r.height;

    r = IlvGraphicUtil.GetStringBounds(speedStr, font, false);
    speedLabelWidth = (int)r.width;
    r = IlvGraphicUtil.GetStringBounds(altitudeStr, font, false);
    altitudeLabelWidth = (int)r.width;
  }

  /**
   * Returns the color used during take off.
   */
  public Color getTakeOffColor()
  { 
    return takeOffColor; 
  }

  /**
   * Changes the color used during take off.
   */
  public void setTakeOffColor(Color color)
  {
    takeOffColor = color;
  }
  
  /**
   * Returns the color used when landing.
   */
  public Color getLandingColor() 
  {
    return landingColor; 
  }

  /**
   * Changes the color used when landing.
   */
  public void setLandingColor(Color pal)
  {
    landingColor = pal;
  }
  
  /**
   * Returns true if the plane has landed.
   */
  public boolean hasLanded() 
  {
    return position.equals(destination);
  }
  
  /**
   * Sets the position to the destination.
   */
  public void land() 
  {
    setPosition(destination.x, destination.y); 
  }
  
  private static final int  DELTA  = 5;
  private static final int  DELTAX = 10;
  private static final int  DELTAY = 0;

  /**
   * Draws the track.
   */
  public void draw(Graphics g, IlvTransformer t) 
  {
    Graphics2D dst = (Graphics2D)g;

    Color color = this.color;

    if (lastPositions[0].equals(lastPositions[1]))
      color = takeOffColor;
    else if (CloseTo(position, destination, warningDistance)) 
      color = landingColor; 

    IlvPoint p = new IlvPoint();

    IlvPoint point = new IlvPoint(position);

    // Draw position
    if (t != null)
      t.apply(point);
    IlvRect rect = new IlvRect(point.x-DELTA, point.y-DELTA, 2*DELTA, 2*DELTA);

    dst.setColor(color);
    dst.drawOval((int)rect.x, (int)rect.y, (int)rect.width, (int)rect.height);

    // Draw last positions
    if (isShowingLastPositions()) {
      if (antialiasedLastPositions) 
        dst.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                             RenderingHints.VALUE_ANTIALIAS_ON);
      float d = 2;
      for (int i = 0; i < TRACKMAXLASTPOSITION; ++i, ++d) {
        p.move(lastPositions[i].x, lastPositions[i].y);
        if (t != null)
          t.apply(p);

        rect.reshape(p.x - d, p.y - d, 2*d, 2*d);

        if (transparentLastPositions) {
          float alpha = .2f + i * LAST_POS_ALFA_FACTOR;
          dst.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
                                                      alpha));
        }
        if (_lastPositionDrawingStyle == LAST_POSITION_RECT)
          dst.draw(rect);
        else // LAST_POSITION_OVAL (slower, but nicer)
          dst.drawOval(rect.xFloor(),
                       rect.yFloor(),
                       rect.widthFloor(),
                       rect.heightFloor());
      }
      if (transparentLastPositions)
        dst.setComposite(AlphaComposite.SrcOver);
      if (antialiasedLastPositions)
        dst.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                             RenderingHints.VALUE_ANTIALIAS_OFF);
    }

    // Draw Logo
    if (image != null && isShowingLogo()) {
      rect.move(point.x - rect.width/2, point.y - rect.height/2);
      IlvGraphicUtil.DrawImage(dst, rect, image, 
                               null, this, HIGHQUALITY_IMAGE);
    }

    // Draw Speed
    if (isShowingSpeed()) {
      p.move(speedPosition.x, speedPosition.y);
      if (t != null)
        t.apply(p);
      dst.drawLine((int)point.x, (int)point.y, (int)p.x, (int)p.y);
    }

    // Draw Labels
    if (isShowingLabels()) {
      
      float h = (float)fontHeight;
   
      p.move(point.x + DELTAX, point.y);   

      dst.setFont(font);

      dst.drawString(name, (int)p.x, (int)p.y);
      p.y += DELTAY + h;

      dst.drawString(speedStr, (int)p.x, (int)p.y);
      p.y += DELTAY + h;

      dst.drawString(altitudeStr, (int)p.x, (int)p.y);
    }

    // Draw the rest of the route
    if (isShowingRoute() && !hasLanded()) {
      p.move(destination.x, destination.y);
      if (t != null)
        t.apply(p);
      dst.setColor(landingColor);
      dst.drawLine((int)point.x, (int)point.y, (int)p.x, (int)p.y);
    }
  }

  /**
   * Redraws the object when the image is fully loaded. Implementation
   * of the method of ImageObserver.
   */
  public boolean imageUpdate(Image img, int flags,
                             int x, int y, int w, int h)  {
    if (img == image) {
      if ((flags & ERROR) != 0) {
        image = null;
        reDraw();
      }
      if ((flags & ALLBITS) != 0) 
        reDraw();
      if ((flags & FRAMEBITS) != 0) 
        reDraw();

      return (flags & (ALLBITS|ERROR)) == 0;

    } else 
      return false;
  }

  
  /**
   * Returns the bounding box of the object.
   */
  public IlvRect boundingBox(IlvTransformer t)
  {
    IlvRect bbox;
    IlvPoint p = new IlvPoint(position);
    // Add Position
    if (t != null)
      t.apply(p);
    IlvRect rect = new IlvRect(p.x-DELTA, p.y-DELTA, 2*DELTA, 2*DELTA);
    bbox = new IlvRect(rect);
    // Add Last Positions
    int d = 2;
    if (isShowingLastPositions()) {
      for (int i = 0; i < TRACKMAXLASTPOSITION; ++i, ++d) {
        p.move(lastPositions[i].x, lastPositions[i].y);
        if (t != null)
          t.apply(p);
        rect.move(p.x-d, p.y-d);
        rect.resize((2*d), (2*d));
        bbox.add(rect);
      }
    }
    // Add Speed
    if (isShowingSpeed()) {
      p.move(speedPosition.x, speedPosition.y);
      if (t != null)
        t.apply(p);
      bbox.add(p);
    }
    // Add Labels
    if (isShowingLabels()) {
      int h = (int)fontHeight;
      p.move(position.x, position.y);
      if (t != null)
        t.apply(p);
      p.translate(DELTAX,0);
      rect.move(p.x, p.y-h+d);
      rect.resize(nameWidth, h);
      bbox.add(rect);
      p.translate(0, DELTAY+h);
      rect.move(p.x, p.y-h+d);
      rect.resize(speedLabelWidth, h);
      bbox.add(rect);
      p.translate(0, DELTAY+h);
      rect.move(p.x, p.y-h+d);
      rect.resize(altitudeLabelWidth, h);
      bbox.add(rect);
    }
    // Add destination
    if (isShowingRoute()) {
      p.move(destination.x, destination.y);
      if (t != null)
        t.apply(p);
      bbox.add(p);
    }
    return bbox;
  }
  
  /**
   * Returns false, this object is not fully zoomable.
   */
  public boolean zoomable()
  {
    return false;
  }

  /**
   * Applies a transformation to the object.
   */
  public void applyTransform(IlvTransformer t)
  {
    t.apply(position);

    for (int i = 0; i < TRACKMAXLASTPOSITION; ++i) 
      t.apply(lastPositions[i]);

    t.apply(speedPosition);
    t.apply(destination);
  }
    
   
  public IlvGraphic copy()
  {
    return null;
  } 

  /**
   * Returns true if the specified points are close to
   * each other.
   */
  static private boolean CloseTo(IlvPoint pos, IlvPoint dest, double limit)
  {
    double X  = dest.x - pos.x;
    double Y  = dest.y - pos.y;
    return ((X*X + Y*Y) <= (double) (limit*limit));
  }
}